12 Commits

Author SHA1 Message Date
Ichitux 4b00c9aa4f Merge branch 'main' into fix/mobile-view
Run Tests on Branches / Detect Changes (push) Successful in 15s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 2m33s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
2026-08-18 12:16:18 +00:00
Ichitux fa53d5855d Merge pull request 'feat: add forgot password and username recovery flow' (#59) from feat/password-recovery into main
Build & Push Docker Images / Backend Tests (push) Successful in 2m1s
Build & Push Docker Images / Parapharmacy API Tests (push) Has been skipped
Build & Push Docker Images / Pip Platform Tests (push) Has been skipped
Build & Push Docker Images / Detect Changes (push) Successful in 14s
Build & Push Docker Images / Frontend Tests (push) Successful in 1m55s
Build & Push Docker Images / Build Parapharmacy API (push) Has been skipped
Build & Push Docker Images / Build Pip Platform (push) Has been skipped
Build & Push Docker Images / Build Frontend (push) Successful in 1m10s
Build & Push Docker Images / Build Backend (push) Successful in 3m17s
Build & Push Docker Images / Deploy (push) Successful in 3m12s
Reviewed-on: #59
2026-08-18 09:08:02 +00:00
Antoni Nuñez Romeu d68463548f Patentes internacionales + Forgot Pwd
Run Tests on Branches / Detect Changes (push) Successful in 19s
Run Tests on Branches / Backend Tests (push) Successful in 2m46s
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Successful in 2m24s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
2026-08-18 11:01:54 +02:00
Antoni Nuñez Romeu 87df61ab15 feat: add forgot password and username recovery flow
Run Tests on Branches / Detect Changes (push) Successful in 14s
Run Tests on Branches / Backend Tests (push) Successful in 2m24s
Run Tests on Branches / Frontend Tests (push) Successful in 1m57s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
- Add Mailpit SMTP container to docker-compose for dev email capture
- Add nodemailer dependency for email sending
- Add password_reset_tokens table (PG + SQLite)
- Add POST /api/auth/forgot-username endpoint
- Add POST /api/auth/forgot-password endpoint (token-based, 1h expiry)
- Add POST /api/auth/reset-password endpoint
- Create ForgotPasswordModal component (choose mode → email → sent)
- Create ResetPasswordView (token-based new password form)
- Add forgot/recovery links to LoginModal
- Add i18n translations (ES/CA) for the full flow
2026-07-27 15:57:54 +02:00
Ichitux 86962dfa3a Merge branch 'main' into fix/mobile-view
Run Tests on Branches / Detect Changes (push) Successful in 13s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 2m13s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
2026-07-27 13:26:33 +00:00
Ichitux 271d23b072 Merge pull request 'feat: add healthchecks to all docker compose services' (#58) from feat/docker-healthchecks into main
Build & Push Docker Images / Backend Tests (push) Has been skipped
Build & Push Docker Images / Parapharmacy API Tests (push) Has been skipped
Build & Push Docker Images / Build Parapharmacy API (push) Has been skipped
Build & Push Docker Images / Build Pip Platform (push) Has been skipped
Build & Push Docker Images / Build Frontend (push) Has been skipped
Build & Push Docker Images / Detect Changes (push) Successful in 10s
Build & Push Docker Images / Pip Platform Tests (push) Has been skipped
Build & Push Docker Images / Frontend Tests (push) Has been skipped
Build & Push Docker Images / Build Backend (push) Has been skipped
Build & Push Docker Images / Deploy (push) Has been skipped
Reviewed-on: #58
2026-07-27 13:23:59 +00:00
Antoni Nuñez Romeu 258839dfc8 feat: add healthchecks to all docker compose services
Run Tests on Branches / Detect Changes (push) Successful in 13s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
2026-07-27 15:23:16 +02:00
Ichitux c8b830017b Merge branch 'main' into fix/mobile-view
Run Tests on Branches / Detect Changes (push) Successful in 14s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m53s
2026-07-23 12:21:11 +00:00
Ichitux e641a35b82 Merge branch 'main' into fix/mobile-view
Run Tests on Branches / Detect Changes (push) Failing after 31s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
2026-07-22 09:57:58 +00:00
Ichitux ff36748634 Merge branch 'main' into fix/mobile-view
Run Tests on Branches / Detect Changes (push) Successful in 11s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m45s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
2026-07-21 13:11:15 +00:00
Ichitux 795d935413 Remove Google Maps vs OSM
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Detect Changes (push) Successful in 13s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m35s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
2026-07-17 16:11:10 +00:00
Ichitux 2f24dd4ff8 Faro & OSM
Run Tests on Branches / Detect Changes (push) Successful in 11s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m53s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
2026-07-17 15:58:46 +00:00
54 changed files with 5082 additions and 254 deletions
+6
View File
@@ -0,0 +1,6 @@
custom-apis:
- name: "localai"
endpoint: "http://10.25.2.33:18080"
model: "ornith-1.0-9b"
api_key: "your-api-key" # only if auth is enabled
+5
View File
@@ -0,0 +1,5 @@
{
"anthropicBaseURL": "http://10.25.2.33:18080/v1",
"anthropicApiKey": "localai",
"anthropicCustomModelOption": "ornith-1.0-9b"
}
@@ -0,0 +1,63 @@
# International Prior Art Search
## Project: FarmaFinder
This document summarizes the international prior-art position for the technical concept described in the FarmaFinder patent materials.
### Technical scope
The invention concerns a computer-implemented system and method for identifying a therapeutic need from the scan of an individual health card or equivalent patient-related identifier, associating that result with an authenticated user, comparing the inferred need with geolocated pharmacy inventory, and generating supply-related actions when the required product is not available.
### Main categories of prior art identified
The following classes of prior-art documents are relevant to the search:
1. Medicine search platforms that allow users to locate medicines by name or active ingredient.
2. Pharmacy locator systems that display nearby pharmacies and, in some cases, opening hours, distance, or approximate availability.
3. Inventory management systems used by pharmacies, distributors, or health-related retail networks.
4. Digital health applications that store user profiles, location data, reminders, or treatment-related information.
5. Reservation, referral, or notification workflows that allow a user or a pharmacy to react when a product is out of stock.
### Distinguishing technical features
Based on the current project documentation, the present invention is distinguished by the combination of the following technical features in a single workflow:
- secure capture of a health-card identifier or equivalent patient-related credential;
- verification of the captured data and association with an authenticated user profile;
- automatic derivation of a medicine reference or therapeutic need;
- real-time comparison against pharmacy stock data from geolocated pharmacies;
- ranking of pharmacies according to availability, distance, and opening status;
- automatic generation of reserve, notification, order, or referral actions when stock is insufficient;
- technical traceability of the event across the processing chain.
### Assessment
The reviewed prior-art categories appear to disclose individual parts of the technical problem, but not the complete integrated architecture described above in a single system. In particular, the combination of:
- patient-context capture through a health identifier,
- automated therapeutic inference,
- geolocated stock comparison, and
- machine-generated supply actions
does not appear to be disclosed as a unified technical chain in the documents reviewed at the project level.
### Suggested search focus for the European search phase
For the European Patent Office search phase, the search should focus on documents relating to:
- patient identifier scanning;
- health-card reading or optical capture of patient credentials;
- medicine search and pharmacy availability systems;
- pharmacy reservation and referral workflows;
- location-based ranking of pharmacies;
- automated inventory-driven notification or order generation;
- health-related digital assistants that combine user context with stock availability.
### Working conclusion
On the basis of the current project documentation, the invention is presented as a computer-implemented technical solution that integrates identification, verification, geolocation, inventory contrast, and automated supply actions in one workflow. No single reference has been identified at the project level that clearly discloses the full combination of these features.
### Date
13 August 2026
@@ -0,0 +1,110 @@
%PDF-1.4
1 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
4 0 obj
<< /Type /Pages /Kids [7 0 R 8 0 R] /Count 2 >>
endobj
5 0 obj
<< /Length 2089 >>
stream
BT /F2 18 Tf 138 787.89 Td (International Prior Art Search) Tj ET
BT /F3 12 Tf 156 761.39 Td (Project: FarmaFinder) Tj ET
BT /F1 11.5 Tf 56 726.39 Td (This document summarizes the international prior-art position for the technical concept) Tj ET
BT /F1 11.5 Tf 56 712.015 Td (described in the FarmaFinder patent materials.) Tj ET
BT /F2 13 Tf 56 695.64 Td (Technical scope) Tj ET
0.3 w 56 675.39 m 539.28 675.39 l S
BT /F1 11.5 Tf 56 667.39 Td (The invention concerns a computer-implemented system and method for identifying a) Tj ET
BT /F1 11.5 Tf 56 653.015 Td (therapeutic need from the scan of an individual health card or equivalent) Tj ET
BT /F1 11.5 Tf 56 638.64 Td (patient-related identifier, associating that result with an authenticated user,) Tj ET
BT /F1 11.5 Tf 56 624.265 Td (comparing the inferred need with geolocated pharmacy inventory, and generating) Tj ET
BT /F1 11.5 Tf 56 609.89 Td (supply-related actions when the required product is not available.) Tj ET
BT /F2 13 Tf 56 593.515 Td (Main categories of prior art identified) Tj ET
0.3 w 56 573.265 m 539.28 573.265 l S
BT /F1 11.2 Tf 64 565.265 Td (1. Medicine search platforms that allow users to locate medicines by name or active) Tj ET
BT /F1 11.2 Tf 64 551.265 Td ( ingredient.) Tj ET
BT /F1 11.2 Tf 64 537.265 Td (2. Pharmacy locator systems that display nearby pharmacies and, in some cases, opening) Tj ET
BT /F1 11.2 Tf 64 523.265 Td ( hours, distance, or approximate availability.) Tj ET
BT /F1 11.2 Tf 64 509.265 Td (3. Inventory management systems used by pharmacies, distributors, or health-related retail) Tj ET
BT /F1 11.2 Tf 64 495.265 Td ( networks.) Tj ET
BT /F1 11.2 Tf 64 481.265 Td (4. Digital health applications that store user profiles, location data, reminders, or) Tj ET
BT /F1 11.2 Tf 64 467.265 Td ( treatment-related information.) Tj ET
BT /F1 11.2 Tf 64 453.265 Td (5. Reservation, referral, or notification workflows that allow a user or a pharmacy to react) Tj ET
BT /F1 11.2 Tf 64 439.265 Td ( when a product is out of stock.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Page 1) Tj ET
endstream
endobj
6 0 obj
<< /Length 3313 >>
stream
BT /F2 13 Tf 56 787.89 Td (Distinguishing technical features) Tj ET
0.3 w 56 767.64 m 539.28 767.64 l S
BT /F1 11.2 Tf 64 759.64 Td (- secure capture of a health-card identifier or equivalent patient-related credential;) Tj ET
BT /F1 11.2 Tf 64 745.64 Td (- verification of the captured data and association with an authenticated user profile;) Tj ET
BT /F1 11.2 Tf 64 731.64 Td (- automatic derivation of a medicine reference or therapeutic need;) Tj ET
BT /F1 11.2 Tf 64 717.64 Td (- real-time comparison against pharmacy stock data from geolocated pharmacies;) Tj ET
BT /F1 11.2 Tf 64 703.64 Td (- ranking of pharmacies according to availability, distance, and opening status;) Tj ET
BT /F1 11.2 Tf 64 689.64 Td (- automatic generation of reserve, notification, order, or referral actions when stock is) Tj ET
BT /F1 11.2 Tf 64 675.64 Td ( insufficient;) Tj ET
BT /F1 11.2 Tf 64 661.64 Td (- technical traceability of the event across the processing chain.) Tj ET
BT /F2 13 Tf 56 647.64 Td (Assessment) Tj ET
0.3 w 56 627.39 m 539.28 627.39 l S
BT /F1 11.5 Tf 56 619.39 Td (The reviewed prior-art categories appear to disclose individual parts of the technical) Tj ET
BT /F1 11.5 Tf 56 605.015 Td (problem, but not the complete integrated architecture described above in a single) Tj ET
BT /F1 11.5 Tf 56 590.64 Td (system. In particular, the combination of patient-context capture through a health) Tj ET
BT /F1 11.5 Tf 56 576.265 Td (identifier, automated therapeutic inference, geolocated stock comparison, and) Tj ET
BT /F1 11.5 Tf 56 561.89 Td (machine-generated supply actions does not appear to be disclosed as a unified technical) Tj ET
BT /F1 11.5 Tf 56 547.515 Td (chain in the documents reviewed at the project level.) Tj ET
BT /F2 13 Tf 56 531.14 Td (Suggested search focus for the European search phase) Tj ET
0.3 w 56 510.89 m 539.28 510.89 l S
BT /F1 11.2 Tf 64 502.89 Td (- patient identifier scanning;) Tj ET
BT /F1 11.2 Tf 64 488.89 Td (- health-card reading or optical capture of patient credentials;) Tj ET
BT /F1 11.2 Tf 64 474.89 Td (- medicine search and pharmacy availability systems;) Tj ET
BT /F1 11.2 Tf 64 460.89 Td (- pharmacy reservation and referral workflows;) Tj ET
BT /F1 11.2 Tf 64 446.89 Td (- location-based ranking of pharmacies;) Tj ET
BT /F1 11.2 Tf 64 432.89 Td (- automated inventory-driven notification or order generation;) Tj ET
BT /F1 11.2 Tf 64 418.89 Td (- health-related digital assistants that combine user context with stock availability.) Tj ET
BT /F2 13 Tf 56 404.89 Td (Working conclusion) Tj ET
0.3 w 56 384.64 m 539.28 384.64 l S
BT /F1 11.5 Tf 56 376.64 Td (On the basis of the current project documentation, the invention is presented as a) Tj ET
BT /F1 11.5 Tf 56 362.265 Td (computer-implemented technical solution that integrates identification, verification,) Tj ET
BT /F1 11.5 Tf 56 347.89 Td (geolocation, inventory contrast, and automated supply actions in one workflow. No) Tj ET
BT /F1 11.5 Tf 56 333.515 Td (single reference has been identified at the project level that clearly discloses the) Tj ET
BT /F1 11.5 Tf 56 319.14 Td (full combination of these features.) Tj ET
BT /F2 13 Tf 56 302.765 Td (Date) Tj ET
0.3 w 56 282.515 m 539.28 282.515 l S
BT /F1 11.5 Tf 56 274.515 Td (13 August 2026) Tj ET
BT /F1 9 Tf 495.28 24 Td (Page 2) Tj ET
endstream
endobj
7 0 obj
<< /Type /Page /Parent 4 0 R /MediaBox [0 0 595.28 841.89] /Resources << /Font << /F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /Contents 5 0 R >>
endobj
8 0 obj
<< /Type /Page /Parent 4 0 R /MediaBox [0 0 595.28 841.89] /Resources << /Font << /F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /Contents 6 0 R >>
endobj
9 0 obj
<< /Type /Catalog /Pages 4 0 R >>
endobj
xref
0 10
0000000000 65535 f
0000000009 00000 n
0000000108 00000 n
0000000206 00000 n
0000000306 00000 n
0000000369 00000 n
0000002510 00000 n
0000005875 00000 n
0000006027 00000 n
0000006179 00000 n
trailer << /Size 10 /Root 9 0 R >>
startxref
6228
%%EOF
@@ -0,0 +1,418 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
4 0 obj
<</Length 1104 >> stream
BT /F2 18 Tf 141 783.89 Td (SOLICITUD DE PATENTE) Tj ET
BT /F1 12 Tf 111 754.85 Td (Oficina Española de Patentes y Marcas \(OEPM\)) Tj ET
BT /F2 12 Tf 176 725.49 Td (Título de la invención:) Tj ET
BT /F1 12 Tf 66 710.13 Td (Sistema y procedimiento implementados por ordenador para la identificación de) Tj ET
BT /F1 12 Tf 66 694.77 Td (necesidades terapéuticas mediante escaneo de tarjeta sanitaria individual,) Tj ET
BT /F1 12 Tf 66 679.41 Td (contraste con stock farmacéutico geolocalizado y generación de acciones de) Tj ET
BT /F1 12 Tf 66 664.05 Td (suministro.) Tj ET
0.8 w 56 580.6899999999999 483.28 68 re S
BT /F1 11 Tf 66 636.6899999999999 Td (Solicitante: Antoni Nuñez Romeu) Tj ET
BT /F1 11 Tf 66 622.6099999999999 Td (NIF/CIF: 45858029P) Tj ET
BT /F1 11 Tf 66 608.5299999999999 Td (Domicilio: C/ Font Vella 12, 1, Terrassa, Barcelona, 08221) Tj ET
BT /F1 11 Tf 66 594.4499999999998 Td (Inventor o inventores: Antoni Nuñez Romeu) Tj ET
BT /F3 10 Tf 124 568.3699999999998 Td (Memoria descriptiva, reivindicaciones, resumen y referencia a dibujos) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 1) Tj ET
endstream
endobj
7 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
8 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
9 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
11 0 obj
<</Length 4950 >> stream
BT /F2 13 Tf 56 781.89 Td (1. Campo técnico de la invención) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención se encuadra en los sistemas informáticos aplicados al sector sanitario) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (y farmacéutico, en particular en soluciones de captura de identificadores) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (sanitarios, comparación automatizada de disponibilidad de medicamentos y asistencia) Tj ET
BT /F1 11.5 Tf 56 707.0899999999999 Td (digital a la dispensación y suministro.) Tj ET
BT /F2 13 Tf 56 688.3699999999999 Td (2. Antecedentes de la invención) Tj ET
0.2 w 56 667.7299999999999 m 539.28 667.7299999999999 l S
BT /F1 11.5 Tf 56 657.7299999999999 Td (Las soluciones actuales de búsqueda de medicamentos y farmacias permiten localizar) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (productos y mostrar stock aproximado, pero normalmente operan a partir de una) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (búsqueda manual por nombre de medicamento o principio activo. También existen) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (sistemas de geolocalización y de gestión de inventario, pero suelen estar) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (desacoplados del contexto sanitario del usuario y no integran, en una única cadena) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (técnica, la identificación mediante tarjeta sanitaria individual, la inferencia de) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (necesidades terapéuticas y la comparación en tiempo real con disponibilidad) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (farmacéutica local.) Tj ET
BT /F1 11.5 Tf 56 537.9699999999997 Td (Además, cuando no existe stock en la farmacia más próxima, el usuario debe iniciar) Tj ET
BT /F1 11.5 Tf 56 523.2499999999997 Td (manualmente nuevos procesos de búsqueda o desplazamiento, sin que el sistema) Tj ET
BT /F1 11.5 Tf 56 508.52999999999963 Td (proponga de forma automatizada acciones técnicas de reserva, pedido o derivación) Tj ET
BT /F1 11.5 Tf 56 493.8099999999996 Td (basadas en la disponibilidad real.) Tj ET
BT /F2 13 Tf 56 475.0899999999996 Td (3. Problema técnico a resolver) Tj ET
0.2 w 56 454.4499999999996 m 539.28 454.4499999999996 l S
BT /F1 11.5 Tf 56 444.4499999999996 Td (La invención resuelve el problema técnico de reducir el tiempo y la fricción entre) Tj ET
BT /F1 11.5 Tf 56 429.72999999999956 Td (la identificación de una necesidad terapéutica y la localización de una farmacia que) Tj ET
BT /F1 11.5 Tf 56 415.00999999999954 Td (disponga del medicamento adecuado, integrando:) Tj ET
BT /F1 11.2 Tf 64 398.2899999999995 Td (" lectura segura de identificadores sanitarios,) Tj ET
BT /F1 11.2 Tf 64 383.9539999999995 Td (" obtención de referencias de medicamento asociadas,) Tj ET
BT /F1 11.2 Tf 64 369.6179999999995 Td (" comparación automatizada con inventarios de farmacias,) Tj ET
BT /F1 11.2 Tf 64 355.28199999999947 Td (" priorización por proximidad y disponibilidad,) Tj ET
BT /F1 11.2 Tf 64 340.94599999999946 Td (" generación de acciones técnicas de suministro cuando no exista stock inmediato.) Tj ET
BT /F2 13 Tf 56 324.60999999999945 Td (4. Resumen de la invención) Tj ET
0.2 w 56 303.96999999999946 m 539.28 303.96999999999946 l S
BT /F1 11.5 Tf 56 293.96999999999946 Td (La invención propone un sistema y procedimiento implementados por ordenador que) Tj ET
BT /F1 11.5 Tf 56 279.24999999999943 Td (operan sobre un terminal móvil o dispositivo de lectura. El sistema captura la) Tj ET
BT /F1 11.5 Tf 56 264.5299999999994 Td (información de una tarjeta sanitaria individual mediante escaneo, valida la) Tj ET
BT /F1 11.5 Tf 56 249.8099999999994 Td (información mediante un módulo de verificación, asocia el resultado al perfil del) Tj ET
BT /F1 11.5 Tf 56 235.0899999999994 Td (usuario autenticado y obtiene una lista de medicamentos o necesidades terapéuticas) Tj ET
BT /F1 11.5 Tf 56 220.3699999999994 Td (relevantes. Posteriormente, un motor de contraste consulta un repositorio de stock) Tj ET
BT /F1 11.5 Tf 56 205.6499999999994 Td (de farmacias geolocalizadas y determina la coincidencia entre la necesidad detectada) Tj ET
BT /F1 11.5 Tf 56 190.9299999999994 Td (y la disponibilidad real en cada establecimiento. Si existe stock, el sistema) Tj ET
BT /F1 11.5 Tf 56 176.2099999999994 Td (prioriza las farmacias adecuadas y presenta una ruta de acceso o reserva. Si no) Tj ET
BT /F1 11.5 Tf 56 161.4899999999994 Td (existe stock, el sistema genera automáticamente una acción de aviso, pedido, reserva) Tj ET
BT /F1 11.5 Tf 56 146.7699999999994 Td (o derivación, conservando trazabilidad técnica del evento. La invención no se limita) Tj ET
BT /F1 11.5 Tf 56 132.04999999999941 Td (a una búsqueda manual, sino que integra captura, validación, georreferenciación,) Tj ET
BT /F1 11.5 Tf 56 117.32999999999942 Td (consulta de stock y respuesta automática en una única arquitectura técnica.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 2) Tj ET
endstream
endobj
14 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
15 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
16 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
19 0 obj
<</Length 3436 >> stream
BT /F2 13 Tf 56 781.89 Td (5. Breve descripción de los dibujos) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 734.53 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 717.81 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 701.0899999999999 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 682.3699999999999 Td (6. Descripción detallada de la invención) Tj ET
0.2 w 56 661.7299999999999 m 539.28 661.7299999999999 l S
BT /F2 11.5 Tf 56 651.7299999999999 Td (6.1 Arquitectura general) Tj ET
BT /F1 11.5 Tf 56 637.0099999999999 Td (El sistema comprende al menos los siguientes módulos:) Tj ET
BT /F1 11.2 Tf 64 620.2899999999998 Td (1. un módulo de captura de imagen o lectura óptica para escanear la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 64 605.9539999999998 Td ( individual;) Tj ET
BT /F1 11.2 Tf 64 591.6179999999998 Td (2. un módulo de extracción de datos para identificar los campos relevantes de la tarjeta;) Tj ET
BT /F1 11.2 Tf 64 577.2819999999998 Td (3. un módulo de verificación de identidad y autorización, vinculado al usuario) Tj ET
BT /F1 11.2 Tf 64 562.9459999999998 Td ( autenticado;) Tj ET
BT /F1 11.2 Tf 64 548.6099999999998 Td (4. un módulo de obtención de necesidades terapéuticas o medicamentos asociados;) Tj ET
BT /F1 11.2 Tf 64 534.2739999999998 Td (5. un módulo de geolocalización para obtener la posición del usuario o una posición de) Tj ET
BT /F1 11.2 Tf 64 519.9379999999998 Td ( referencia;) Tj ET
BT /F1 11.2 Tf 64 505.60199999999975 Td (6. un módulo de consulta de inventario para recuperar stock, precio y horario de) Tj ET
BT /F1 11.2 Tf 64 491.26599999999974 Td ( farmacias;) Tj ET
BT /F1 11.2 Tf 64 476.9299999999997 Td (7. un módulo de contraste y priorización para ordenar farmacias según disponibilidad y) Tj ET
BT /F1 11.2 Tf 64 462.5939999999997 Td ( cercanía;) Tj ET
BT /F1 11.2 Tf 64 448.2579999999997 Td (8. un módulo de generación de acciones para crear reserva, aviso, pedido o derivación;) Tj ET
BT /F1 11.2 Tf 64 433.9219999999997 Td (9. un módulo de trazabilidad y auditoría.) Tj ET
BT /F2 11.5 Tf 56 419.5859999999997 Td (6.2 Funcionamiento) Tj ET
BT /F1 11.5 Tf 56 404.86599999999964 Td (En una realización preferida, el usuario abre la aplicación y solicita el escaneo de) Tj ET
BT /F1 11.5 Tf 56 390.1459999999996 Td (su tarjeta sanitaria individual. El terminal captura la imagen o el código de la) Tj ET
BT /F1 11.5 Tf 56 375.4259999999996 Td (tarjeta y el módulo de extracción identifica los datos técnicos necesarios para) Tj ET
BT /F1 11.5 Tf 56 360.70599999999956 Td (enlazar el contexto sanitario del usuario con un conjunto de medicamentos o) Tj ET
BT /F1 11.5 Tf 56 345.98599999999954 Td (referencias de tratamiento.) Tj ET
BT /F1 11.5 Tf 56 329.2659999999995 Td (El sistema valida que la operación se ejecuta sobre un usuario autenticado y) Tj ET
BT /F1 11.5 Tf 56 314.5459999999995 Td (autorizado. A continuación, el motor de contraste consulta el inventario de) Tj ET
BT /F1 11.5 Tf 56 299.82599999999945 Td (farmacias cercanas, recuperando para cada farmacia al menos su ubicación, horario,) Tj ET
BT /F1 11.5 Tf 56 285.1059999999994 Td (stock y, opcionalmente, precio.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 3) Tj ET
endstream
endobj
22 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
23 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
24 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
28 0 obj
<</Length 3271 >> stream
BT /F2 11.5 Tf 56 783.89 Td (6.2 Funcionamiento \(continuación\)) Tj ET
BT /F1 11.5 Tf 56 769.17 Td (El módulo de priorización calcula una orden de resultados en base a criterios) Tj ET
BT /F1 11.5 Tf 56 754.4499999999999 Td (técnicos tales como existencia de stock, nivel de stock, distancia geográfica,) Tj ET
BT /F1 11.5 Tf 56 739.7299999999999 Td (horario de apertura y capacidad de reserva o solicitud de pedido.) Tj ET
BT /F1 11.5 Tf 56 723.0099999999999 Td (Cuando existe disponibilidad, el sistema muestra una lista priorizada de farmacias y) Tj ET
BT /F1 11.5 Tf 56 708.2899999999998 Td (puede iniciar una reserva o una indicación de recogida. Cuando no existe) Tj ET
BT /F1 11.5 Tf 56 693.5699999999998 Td (disponibilidad, el sistema no se limita a informar de la falta de stock, sino que) Tj ET
BT /F1 11.5 Tf 56 678.8499999999998 Td (genera una acción técnica adicional, por ejemplo alerta a la farmacia seleccionada,) Tj ET
BT /F1 11.5 Tf 56 664.1299999999998 Td (solicitud de pedido a distribución o proveedor, derivación automática a otra) Tj ET
BT /F1 11.5 Tf 56 649.4099999999997 Td (farmacia con stock o registro de una lista de espera o interés de suministro.) Tj ET
BT /F2 11.5 Tf 56 632.6899999999997 Td (6.3 Tratamiento de datos y seguridad) Tj ET
BT /F1 11.5 Tf 56 617.9699999999997 Td (La invención prevé que la información derivada del escaneo de la TSI se trate con) Tj ET
BT /F1 11.5 Tf 56 603.2499999999997 Td (medidas de minimización de datos, control de acceso, cifrado en tránsito y registro) Tj ET
BT /F1 11.5 Tf 56 588.5299999999996 Td (de eventos. El sistema no expone innecesariamente los datos de la tarjeta sanitaria) Tj ET
BT /F1 11.5 Tf 56 573.8099999999996 Td (y solo conserva los elementos imprescindibles para la operación descrita.) Tj ET
BT /F2 11.5 Tf 56 557.0899999999996 Td (6.4 Integración con la plataforma FarmaFinder) Tj ET
BT /F1 11.5 Tf 56 542.3699999999995 Td (En una implementación concreta, el sistema puede integrarse con una plataforma que) Tj ET
BT /F1 11.5 Tf 56 527.6499999999995 Td (ya disponga de catálogo de farmacias, geolocalización, stock por medicamento,) Tj ET
BT /F1 11.5 Tf 56 512.9299999999995 Td (autenticación de usuario, notificaciones push e histórico de acciones y) Tj ET
BT /F1 11.5 Tf 56 498.20999999999947 Td (preferencias.) Tj ET
BT /F1 11.5 Tf 56 481.48999999999944 Td (La integración permite que el resultado del escaneo de la TSI no se limite a una) Tj ET
BT /F1 11.5 Tf 56 466.7699999999994 Td (consulta pasiva, sino que se convierta en un disparador de decisiones técnicas sobre) Tj ET
BT /F1 11.5 Tf 56 452.0499999999994 Td (disponibilidad y suministro.) Tj ET
BT /F2 13 Tf 56 433.32999999999936 Td (7. Ejemplo de realización) Tj ET
0.2 w 56 412.6899999999994 m 539.28 412.6899999999994 l S
BT /F1 11.2 Tf 64 402.6899999999994 Td (1. El usuario inicia sesión en la aplicación.) Tj ET
BT /F1 11.2 Tf 64 388.35399999999936 Td (2. El usuario escanea su TSI con la cámara del terminal.) Tj ET
BT /F1 11.2 Tf 64 374.01799999999935 Td (3. El sistema extrae el identificador necesario y lo vincula al perfil autorizado.) Tj ET
BT /F1 11.2 Tf 64 359.68199999999933 Td (4. El motor terapéutico asocia una necesidad de medicamento o tratamiento.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 4) Tj ET
endstream
endobj
31 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
32 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
33 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
38 0 obj
<</Length 3689 >> stream
BT /F2 13 Tf 56 781.89 Td (7. Ejemplo de realización \(continuación\)) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.2 Tf 64 751.25 Td (1. El sistema consulta farmacias cercanas con stock del medicamento.) Tj ET
BT /F1 11.2 Tf 64 736.914 Td (2. Si hay stock, ordena las farmacias por proximidad y disponibilidad.) Tj ET
BT /F1 11.2 Tf 64 722.578 Td (3. Si no hay stock, genera una reserva, una alerta o una solicitud de pedido.) Tj ET
BT /F2 13 Tf 56 706.242 Td (8. Reivindicaciones) Tj ET
0.2 w 56 685.602 m 539.28 685.602 l S
BT /F1 11.2 Tf 56 675.602 Td (Reivindicación 1. Sistema implementado por ordenador para identificar una necesidad) Tj ET
BT /F1 11.2 Tf 56 661.266 Td (terapéutica a partir del escaneo de una tarjeta sanitaria individual, caracterizado) Tj ET
BT /F1 11.2 Tf 56 646.93 Td (porque comprende medios de captura para obtener datos de la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 56 632.5939999999999 Td (individual; medios de verificación para asociar dichos datos a un usuario autenticado;) Tj ET
BT /F1 11.2 Tf 56 618.2579999999999 Td (medios de obtención de una referencia de medicamento o necesidad terapéutica; medios) Tj ET
BT /F1 11.2 Tf 56 603.9219999999999 Td (de consulta de inventario para recuperar stock de farmacias geolocalizadas; medios de) Tj ET
BT /F1 11.2 Tf 56 589.5859999999999 Td (contraste para comparar la referencia de medicamento o necesidad terapéutica con el) Tj ET
BT /F1 11.2 Tf 56 575.2499999999999 Td (stock disponible; y medios de generación de acciones para producir una reserva, aviso,) Tj ET
BT /F1 11.2 Tf 56 560.9139999999999 Td (pedido o derivación cuando el stock disponible sea insuficiente.) Tj ET
BT /F1 11.2 Tf 56 544.5779999999999 Td (Reivindicación 2. Sistema según la reivindicación 1, en el que los medios de consulta) Tj ET
BT /F1 11.2 Tf 56 530.2419999999998 Td (de inventario ordenan las farmacias según una combinación de stock disponible,) Tj ET
BT /F1 11.2 Tf 56 515.9059999999998 Td (proximidad geográfica y horario de apertura.) Tj ET
BT /F1 11.2 Tf 56 499.5699999999998 Td (Reivindicación 3. Sistema según cualquiera de las reivindicaciones anteriores, en el) Tj ET
BT /F1 11.2 Tf 56 485.2339999999998 Td (que los datos de la tarjeta sanitaria individual son tratados mediante un módulo de) Tj ET
BT /F1 11.2 Tf 56 470.8979999999998 Td (minimización y cifrado para limitar su almacenamiento a los campos técnicamente) Tj ET
BT /F1 11.2 Tf 56 456.5619999999998 Td (necesarios.) Tj ET
BT /F1 11.2 Tf 56 440.2259999999998 Td (Reivindicación 4. Procedimiento implementado por ordenador para asistir en la) Tj ET
BT /F1 11.2 Tf 56 425.88999999999976 Td (localización y suministro de medicamentos, caracterizado porque comprende escanear una) Tj ET
BT /F1 11.2 Tf 56 411.55399999999975 Td (tarjeta sanitaria individual; verificar la autorización del usuario; derivar una) Tj ET
BT /F1 11.2 Tf 56 397.21799999999973 Td (necesidad terapéutica o referencia de medicamento; consultar stock de farmacias) Tj ET
BT /F1 11.2 Tf 56 382.8819999999997 Td (geolocalizadas; comparar la necesidad con el stock disponible; y generar una acción) Tj ET
BT /F1 11.2 Tf 56 368.5459999999997 Td (técnica de reserva, aviso, pedido o derivación cuando no exista disponibilidad) Tj ET
BT /F1 11.2 Tf 56 354.2099999999997 Td (suficiente.) Tj ET
BT /F1 11.2 Tf 56 337.8739999999997 Td (Reivindicación 5. Dispositivo móvil, terminal o sistema de lectura que comprende una) Tj ET
BT /F1 11.2 Tf 56 323.53799999999967 Td (cámara o lector óptico y que está configurado para ejecutar el sistema de cualquiera) Tj ET
BT /F1 11.2 Tf 56 309.20199999999966 Td (de las reivindicaciones anteriores.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 5) Tj ET
endstream
endobj
41 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
42 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
43 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
49 0 obj
<</Length 4216 >> stream
BT /F2 13 Tf 56 781.89 Td (9. Aplicación industrial) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención es susceptible de aplicación industrial en plataformas de asistencia) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (farmacéutica, sistemas de gestión de inventario, servicios de reserva o pedido de) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (medicamentos y terminales de consulta sanitaria.) Tj ET
BT /F2 13 Tf 56 703.0899999999999 Td (10. Resumen) Tj ET
0.2 w 56 682.4499999999999 m 539.28 682.4499999999999 l S
BT /F1 11.5 Tf 56 672.4499999999999 Td (La presente invención se refiere a un sistema y procedimiento implementados por) Tj ET
BT /F1 11.5 Tf 56 657.7299999999999 Td (ordenador que permiten, a partir del escaneo de una tarjeta sanitaria individual) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (mediante un terminal móvil o dispositivo equivalente, asociar de forma segura un) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (contexto de paciente a una necesidad terapéutica, obtener una referencia de) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (medicamentos relevantes, comparar dicha necesidad con el stock disponible en) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (farmacias geolocalizadas y generar, en caso de falta de disponibilidad, una acción) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (de reserva, aviso, solicitud de pedido o derivación a una farmacia alternativa. El) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (sistema integra captura de datos, verificación de identidad, geolocalización,) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (consulta de disponibilidad, gestión de stock y generación de eventos de suministro) Tj ET
BT /F1 11.5 Tf 56 539.9699999999997 Td (en una única cadena técnica de procesamiento.) Tj ET
BT /F2 13 Tf 56 521.2499999999997 Td (11. Anexo de dibujos) Tj ET
0.2 w 56 500.6099999999997 m 539.28 500.6099999999997 l S
BT /F1 11.5 Tf 56 490.6099999999997 Td (Las figuras indicadas en la presente memoria tienen carácter esquemático y se) Tj ET
BT /F1 11.5 Tf 56 475.88999999999965 Td (incorporan a efectos ilustrativos de la arquitectura, del flujo de captura, del) Tj ET
BT /F1 11.5 Tf 56 461.1699999999996 Td (contraste de stock y de la generación de acciones. En una presentación definitiva,) Tj ET
BT /F1 11.5 Tf 56 446.4499999999996 Td (dichas figuras podrán acompañarse como láminas separadas numeradas de forma) Tj ET
BT /F1 11.5 Tf 56 431.72999999999956 Td (consecutiva.) Tj ET
BT /F2 13 Tf 56 413.00999999999954 Td (12. Firma del solicitante) Tj ET
0.2 w 56 392.36999999999955 m 539.28 392.36999999999955 l S
BT /F1 11.5 Tf 56 382.36999999999955 Td (En Terrassa, a 22 de junio de 2026.) Tj ET
BT /F1 11.5 Tf 56 365.6499999999995 Td (Fdo.: Antoni Nuñez Romeu) Tj ET
BT /F1 11.5 Tf 56 348.9299999999995 Td (DNI/NIF: 45858029P) Tj ET
BT /F2 13 Tf 56 330.20999999999947 Td (13. Notas para presentación en OEPM) Tj ET
0.2 w 56 309.5699999999995 m 539.28 309.5699999999995 l S
BT /F1 11.2 Tf 64 299.5699999999995 Td (" La solicitud debe presentarse como una única invención o un único concepto inventivo) Tj ET
BT /F1 11.2 Tf 64 285.23399999999947 Td ( general.) Tj ET
BT /F1 11.2 Tf 64 270.89799999999946 Td (" La descripción, reivindicaciones y dibujos deben ser coherentes entre sí.) Tj ET
BT /F1 11.2 Tf 64 256.56199999999944 Td (" La protección en patente debe apoyarse en un efecto técnico concreto y no en una mera) Tj ET
BT /F1 11.2 Tf 64 242.22599999999943 Td ( regla comercial o método abstracto.) Tj ET
BT /F1 11.2 Tf 64 227.88999999999942 Td (" Esta redacción evita formular una reivindicación independiente de "programa de) Tj ET
BT /F1 11.2 Tf 64 213.5539999999994 Td ( ordenador como tal" y se centra en un sistema y un procedimiento implementados por) Tj ET
BT /F1 11.2 Tf 64 199.2179999999994 Td ( ordenador con interacción técnica sobre captura, contraste de inventario y generación) Tj ET
BT /F1 11.2 Tf 64 184.88199999999938 Td ( de acciones.) Tj ET
BT /F1 11.2 Tf 64 170.54599999999937 Td (" Antes de presentar la solicitud conviene una búsqueda de anterioridades y una revisión) Tj ET
BT /F1 11.2 Tf 64 156.20999999999935 Td ( por agente de propiedad industrial.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 6) Tj ET
endstream
endobj
52 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
53 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
54 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
56 0 obj
<</Length 1123 >> stream
BT /F2 18 Tf 231 785.89 Td (Dibujos) Tj ET
BT /F3 11.5 Tf 101 763.39 Td (Bloque limpio para la presentación de la) Tj ET
BT /F3 11.5 Tf 101 749.015 Td (solicitud) Tj ET
BT /F2 13 Tf 56 716.64 Td (Breve descripción de los dibujos) Tj ET
0.3 w 56 698.39 m 539.28 698.39 l S
BT /F1 11.5 Tf 56 690.39 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 674.015 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 657.64 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 641.265 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 612.89 Td (Nota) Tj ET
0.3 w 56 594.64 m 539.28 594.64 l S
BT /F1 11.5 Tf 56 586.64 Td (El documento fuente contiene referencias esquemáticas a las figuras, pero no) Tj ET
BT /F1 11.5 Tf 56 572.265 Td (incluye láminas técnicas dibujadas de forma separada. Si la Oficina requiere) Tj ET
BT /F1 11.5 Tf 56 557.89 Td (placas gráficas, habrá que añadirlas antes de la presentación definitiva.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Page 1) Tj ET
endstream
endobj
58 0 obj
<< /Type /Catalog /Pages 59 0 R>>
endobj
59 0 obj
<< /Type /Pages /Kids [ 60 0 R 61 0 R 62 0 R 63 0 R 64 0 R 65 0 R 66 0 R ] /Count 7 >>
endobj
60 0 obj
<< /Type /Page /Parent 59 0 R /Contents 4 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
61 0 obj
<< /Type /Page /Parent 59 0 R /Contents 11 0 R /Resources <</Font <</F1 7 0 R /F2 8 0 R /F3 9 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
62 0 obj
<< /Type /Page /Parent 59 0 R /Contents 19 0 R /Resources <</Font <</F1 14 0 R /F2 15 0 R /F3 16 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
63 0 obj
<< /Type /Page /Parent 59 0 R /Contents 28 0 R /Resources <</Font <</F1 22 0 R /F2 23 0 R /F3 24 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
64 0 obj
<< /Type /Page /Parent 59 0 R /Contents 38 0 R /Resources <</Font <</F1 31 0 R /F2 32 0 R /F3 33 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
65 0 obj
<< /Type /Page /Parent 59 0 R /Contents 49 0 R /Resources <</Font <</F1 41 0 R /F2 42 0 R /F3 43 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
66 0 obj
<< /Type /Page /Parent 59 0 R /Contents 56 0 R /Resources <</Font <</F1 52 0 R /F2 53 0 R /F3 54 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 67
0000000005 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000321 00000 n
0000000006 00000 f
0000000010 00000 f
0000001483 00000 n
0000001585 00000 n
0000001686 00000 n
0000000012 00000 f
0000001789 00000 n
0000000013 00000 f
0000000017 00000 f
0000006798 00000 n
0000006901 00000 n
0000007003 00000 n
0000000018 00000 f
0000000020 00000 f
0000007107 00000 n
0000000021 00000 f
0000000025 00000 f
0000010602 00000 n
0000010705 00000 n
0000010807 00000 n
0000000026 00000 f
0000000027 00000 f
0000000029 00000 f
0000010911 00000 n
0000000030 00000 f
0000000034 00000 f
0000014241 00000 n
0000014344 00000 n
0000014446 00000 n
0000000035 00000 f
0000000036 00000 f
0000000037 00000 f
0000000039 00000 f
0000014550 00000 n
0000000040 00000 f
0000000044 00000 f
0000018298 00000 n
0000018401 00000 n
0000018503 00000 n
0000000045 00000 f
0000000046 00000 f
0000000047 00000 f
0000000048 00000 f
0000000050 00000 f
0000018607 00000 n
0000000051 00000 f
0000000055 00000 f
0000022882 00000 n
0000022985 00000 n
0000023087 00000 n
0000000057 00000 f
0000023191 00000 n
0000000000 00000 f
0000024373 00000 n
0000024423 00000 n
0000024526 00000 n
0000024725 00000 n
0000024925 00000 n
0000025128 00000 n
0000025331 00000 n
0000025534 00000 n
0000025737 00000 n
trailer
<</Size 37 /ID [(›Omm–XÌ#CÀ2O{ø) (›Omm–XÌ#CÀ2O{ø) ] /Root 58 0 R >>
startxref
25940
%%EOF
@@ -0,0 +1,380 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
5 0 obj
<</Length 4950 >> stream
BT /F2 13 Tf 56 781.89 Td (1. Campo técnico de la invención) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención se encuadra en los sistemas informáticos aplicados al sector sanitario) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (y farmacéutico, en particular en soluciones de captura de identificadores) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (sanitarios, comparación automatizada de disponibilidad de medicamentos y asistencia) Tj ET
BT /F1 11.5 Tf 56 707.0899999999999 Td (digital a la dispensación y suministro.) Tj ET
BT /F2 13 Tf 56 688.3699999999999 Td (2. Antecedentes de la invención) Tj ET
0.2 w 56 667.7299999999999 m 539.28 667.7299999999999 l S
BT /F1 11.5 Tf 56 657.7299999999999 Td (Las soluciones actuales de búsqueda de medicamentos y farmacias permiten localizar) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (productos y mostrar stock aproximado, pero normalmente operan a partir de una) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (búsqueda manual por nombre de medicamento o principio activo. También existen) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (sistemas de geolocalización y de gestión de inventario, pero suelen estar) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (desacoplados del contexto sanitario del usuario y no integran, en una única cadena) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (técnica, la identificación mediante tarjeta sanitaria individual, la inferencia de) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (necesidades terapéuticas y la comparación en tiempo real con disponibilidad) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (farmacéutica local.) Tj ET
BT /F1 11.5 Tf 56 537.9699999999997 Td (Además, cuando no existe stock en la farmacia más próxima, el usuario debe iniciar) Tj ET
BT /F1 11.5 Tf 56 523.2499999999997 Td (manualmente nuevos procesos de búsqueda o desplazamiento, sin que el sistema) Tj ET
BT /F1 11.5 Tf 56 508.52999999999963 Td (proponga de forma automatizada acciones técnicas de reserva, pedido o derivación) Tj ET
BT /F1 11.5 Tf 56 493.8099999999996 Td (basadas en la disponibilidad real.) Tj ET
BT /F2 13 Tf 56 475.0899999999996 Td (3. Problema técnico a resolver) Tj ET
0.2 w 56 454.4499999999996 m 539.28 454.4499999999996 l S
BT /F1 11.5 Tf 56 444.4499999999996 Td (La invención resuelve el problema técnico de reducir el tiempo y la fricción entre) Tj ET
BT /F1 11.5 Tf 56 429.72999999999956 Td (la identificación de una necesidad terapéutica y la localización de una farmacia que) Tj ET
BT /F1 11.5 Tf 56 415.00999999999954 Td (disponga del medicamento adecuado, integrando:) Tj ET
BT /F1 11.2 Tf 64 398.2899999999995 Td (" lectura segura de identificadores sanitarios,) Tj ET
BT /F1 11.2 Tf 64 383.9539999999995 Td (" obtención de referencias de medicamento asociadas,) Tj ET
BT /F1 11.2 Tf 64 369.6179999999995 Td (" comparación automatizada con inventarios de farmacias,) Tj ET
BT /F1 11.2 Tf 64 355.28199999999947 Td (" priorización por proximidad y disponibilidad,) Tj ET
BT /F1 11.2 Tf 64 340.94599999999946 Td (" generación de acciones técnicas de suministro cuando no exista stock inmediato.) Tj ET
BT /F2 13 Tf 56 324.60999999999945 Td (4. Resumen de la invención) Tj ET
0.2 w 56 303.96999999999946 m 539.28 303.96999999999946 l S
BT /F1 11.5 Tf 56 293.96999999999946 Td (La invención propone un sistema y procedimiento implementados por ordenador que) Tj ET
BT /F1 11.5 Tf 56 279.24999999999943 Td (operan sobre un terminal móvil o dispositivo de lectura. El sistema captura la) Tj ET
BT /F1 11.5 Tf 56 264.5299999999994 Td (información de una tarjeta sanitaria individual mediante escaneo, valida la) Tj ET
BT /F1 11.5 Tf 56 249.8099999999994 Td (información mediante un módulo de verificación, asocia el resultado al perfil del) Tj ET
BT /F1 11.5 Tf 56 235.0899999999994 Td (usuario autenticado y obtiene una lista de medicamentos o necesidades terapéuticas) Tj ET
BT /F1 11.5 Tf 56 220.3699999999994 Td (relevantes. Posteriormente, un motor de contraste consulta un repositorio de stock) Tj ET
BT /F1 11.5 Tf 56 205.6499999999994 Td (de farmacias geolocalizadas y determina la coincidencia entre la necesidad detectada) Tj ET
BT /F1 11.5 Tf 56 190.9299999999994 Td (y la disponibilidad real en cada establecimiento. Si existe stock, el sistema) Tj ET
BT /F1 11.5 Tf 56 176.2099999999994 Td (prioriza las farmacias adecuadas y presenta una ruta de acceso o reserva. Si no) Tj ET
BT /F1 11.5 Tf 56 161.4899999999994 Td (existe stock, el sistema genera automáticamente una acción de aviso, pedido, reserva) Tj ET
BT /F1 11.5 Tf 56 146.7699999999994 Td (o derivación, conservando trazabilidad técnica del evento. La invención no se limita) Tj ET
BT /F1 11.5 Tf 56 132.04999999999941 Td (a una búsqueda manual, sino que integra captura, validación, georreferenciación,) Tj ET
BT /F1 11.5 Tf 56 117.32999999999942 Td (consulta de stock y respuesta automática en una única arquitectura técnica.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 2) Tj ET
endstream
endobj
8 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
9 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
10 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
13 0 obj
<</Length 3436 >> stream
BT /F2 13 Tf 56 781.89 Td (5. Breve descripción de los dibujos) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 734.53 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 717.81 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 701.0899999999999 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 682.3699999999999 Td (6. Descripción detallada de la invención) Tj ET
0.2 w 56 661.7299999999999 m 539.28 661.7299999999999 l S
BT /F2 11.5 Tf 56 651.7299999999999 Td (6.1 Arquitectura general) Tj ET
BT /F1 11.5 Tf 56 637.0099999999999 Td (El sistema comprende al menos los siguientes módulos:) Tj ET
BT /F1 11.2 Tf 64 620.2899999999998 Td (1. un módulo de captura de imagen o lectura óptica para escanear la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 64 605.9539999999998 Td ( individual;) Tj ET
BT /F1 11.2 Tf 64 591.6179999999998 Td (2. un módulo de extracción de datos para identificar los campos relevantes de la tarjeta;) Tj ET
BT /F1 11.2 Tf 64 577.2819999999998 Td (3. un módulo de verificación de identidad y autorización, vinculado al usuario) Tj ET
BT /F1 11.2 Tf 64 562.9459999999998 Td ( autenticado;) Tj ET
BT /F1 11.2 Tf 64 548.6099999999998 Td (4. un módulo de obtención de necesidades terapéuticas o medicamentos asociados;) Tj ET
BT /F1 11.2 Tf 64 534.2739999999998 Td (5. un módulo de geolocalización para obtener la posición del usuario o una posición de) Tj ET
BT /F1 11.2 Tf 64 519.9379999999998 Td ( referencia;) Tj ET
BT /F1 11.2 Tf 64 505.60199999999975 Td (6. un módulo de consulta de inventario para recuperar stock, precio y horario de) Tj ET
BT /F1 11.2 Tf 64 491.26599999999974 Td ( farmacias;) Tj ET
BT /F1 11.2 Tf 64 476.9299999999997 Td (7. un módulo de contraste y priorización para ordenar farmacias según disponibilidad y) Tj ET
BT /F1 11.2 Tf 64 462.5939999999997 Td ( cercanía;) Tj ET
BT /F1 11.2 Tf 64 448.2579999999997 Td (8. un módulo de generación de acciones para crear reserva, aviso, pedido o derivación;) Tj ET
BT /F1 11.2 Tf 64 433.9219999999997 Td (9. un módulo de trazabilidad y auditoría.) Tj ET
BT /F2 11.5 Tf 56 419.5859999999997 Td (6.2 Funcionamiento) Tj ET
BT /F1 11.5 Tf 56 404.86599999999964 Td (En una realización preferida, el usuario abre la aplicación y solicita el escaneo de) Tj ET
BT /F1 11.5 Tf 56 390.1459999999996 Td (su tarjeta sanitaria individual. El terminal captura la imagen o el código de la) Tj ET
BT /F1 11.5 Tf 56 375.4259999999996 Td (tarjeta y el módulo de extracción identifica los datos técnicos necesarios para) Tj ET
BT /F1 11.5 Tf 56 360.70599999999956 Td (enlazar el contexto sanitario del usuario con un conjunto de medicamentos o) Tj ET
BT /F1 11.5 Tf 56 345.98599999999954 Td (referencias de tratamiento.) Tj ET
BT /F1 11.5 Tf 56 329.2659999999995 Td (El sistema valida que la operación se ejecuta sobre un usuario autenticado y) Tj ET
BT /F1 11.5 Tf 56 314.5459999999995 Td (autorizado. A continuación, el motor de contraste consulta el inventario de) Tj ET
BT /F1 11.5 Tf 56 299.82599999999945 Td (farmacias cercanas, recuperando para cada farmacia al menos su ubicación, horario,) Tj ET
BT /F1 11.5 Tf 56 285.1059999999994 Td (stock y, opcionalmente, precio.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 3) Tj ET
endstream
endobj
16 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
17 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
18 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
22 0 obj
<</Length 3271 >> stream
BT /F2 11.5 Tf 56 783.89 Td (6.2 Funcionamiento \(continuación\)) Tj ET
BT /F1 11.5 Tf 56 769.17 Td (El módulo de priorización calcula una orden de resultados en base a criterios) Tj ET
BT /F1 11.5 Tf 56 754.4499999999999 Td (técnicos tales como existencia de stock, nivel de stock, distancia geográfica,) Tj ET
BT /F1 11.5 Tf 56 739.7299999999999 Td (horario de apertura y capacidad de reserva o solicitud de pedido.) Tj ET
BT /F1 11.5 Tf 56 723.0099999999999 Td (Cuando existe disponibilidad, el sistema muestra una lista priorizada de farmacias y) Tj ET
BT /F1 11.5 Tf 56 708.2899999999998 Td (puede iniciar una reserva o una indicación de recogida. Cuando no existe) Tj ET
BT /F1 11.5 Tf 56 693.5699999999998 Td (disponibilidad, el sistema no se limita a informar de la falta de stock, sino que) Tj ET
BT /F1 11.5 Tf 56 678.8499999999998 Td (genera una acción técnica adicional, por ejemplo alerta a la farmacia seleccionada,) Tj ET
BT /F1 11.5 Tf 56 664.1299999999998 Td (solicitud de pedido a distribución o proveedor, derivación automática a otra) Tj ET
BT /F1 11.5 Tf 56 649.4099999999997 Td (farmacia con stock o registro de una lista de espera o interés de suministro.) Tj ET
BT /F2 11.5 Tf 56 632.6899999999997 Td (6.3 Tratamiento de datos y seguridad) Tj ET
BT /F1 11.5 Tf 56 617.9699999999997 Td (La invención prevé que la información derivada del escaneo de la TSI se trate con) Tj ET
BT /F1 11.5 Tf 56 603.2499999999997 Td (medidas de minimización de datos, control de acceso, cifrado en tránsito y registro) Tj ET
BT /F1 11.5 Tf 56 588.5299999999996 Td (de eventos. El sistema no expone innecesariamente los datos de la tarjeta sanitaria) Tj ET
BT /F1 11.5 Tf 56 573.8099999999996 Td (y solo conserva los elementos imprescindibles para la operación descrita.) Tj ET
BT /F2 11.5 Tf 56 557.0899999999996 Td (6.4 Integración con la plataforma FarmaFinder) Tj ET
BT /F1 11.5 Tf 56 542.3699999999995 Td (En una implementación concreta, el sistema puede integrarse con una plataforma que) Tj ET
BT /F1 11.5 Tf 56 527.6499999999995 Td (ya disponga de catálogo de farmacias, geolocalización, stock por medicamento,) Tj ET
BT /F1 11.5 Tf 56 512.9299999999995 Td (autenticación de usuario, notificaciones push e histórico de acciones y) Tj ET
BT /F1 11.5 Tf 56 498.20999999999947 Td (preferencias.) Tj ET
BT /F1 11.5 Tf 56 481.48999999999944 Td (La integración permite que el resultado del escaneo de la TSI no se limite a una) Tj ET
BT /F1 11.5 Tf 56 466.7699999999994 Td (consulta pasiva, sino que se convierta en un disparador de decisiones técnicas sobre) Tj ET
BT /F1 11.5 Tf 56 452.0499999999994 Td (disponibilidad y suministro.) Tj ET
BT /F2 13 Tf 56 433.32999999999936 Td (7. Ejemplo de realización) Tj ET
0.2 w 56 412.6899999999994 m 539.28 412.6899999999994 l S
BT /F1 11.2 Tf 64 402.6899999999994 Td (1. El usuario inicia sesión en la aplicación.) Tj ET
BT /F1 11.2 Tf 64 388.35399999999936 Td (2. El usuario escanea su TSI con la cámara del terminal.) Tj ET
BT /F1 11.2 Tf 64 374.01799999999935 Td (3. El sistema extrae el identificador necesario y lo vincula al perfil autorizado.) Tj ET
BT /F1 11.2 Tf 64 359.68199999999933 Td (4. El motor terapéutico asocia una necesidad de medicamento o tratamiento.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 4) Tj ET
endstream
endobj
25 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
26 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
27 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
32 0 obj
<</Length 3689 >> stream
BT /F2 13 Tf 56 781.89 Td (7. Ejemplo de realización \(continuación\)) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.2 Tf 64 751.25 Td (1. El sistema consulta farmacias cercanas con stock del medicamento.) Tj ET
BT /F1 11.2 Tf 64 736.914 Td (2. Si hay stock, ordena las farmacias por proximidad y disponibilidad.) Tj ET
BT /F1 11.2 Tf 64 722.578 Td (3. Si no hay stock, genera una reserva, una alerta o una solicitud de pedido.) Tj ET
BT /F2 13 Tf 56 706.242 Td (8. Reivindicaciones) Tj ET
0.2 w 56 685.602 m 539.28 685.602 l S
BT /F1 11.2 Tf 56 675.602 Td (Reivindicación 1. Sistema implementado por ordenador para identificar una necesidad) Tj ET
BT /F1 11.2 Tf 56 661.266 Td (terapéutica a partir del escaneo de una tarjeta sanitaria individual, caracterizado) Tj ET
BT /F1 11.2 Tf 56 646.93 Td (porque comprende medios de captura para obtener datos de la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 56 632.5939999999999 Td (individual; medios de verificación para asociar dichos datos a un usuario autenticado;) Tj ET
BT /F1 11.2 Tf 56 618.2579999999999 Td (medios de obtención de una referencia de medicamento o necesidad terapéutica; medios) Tj ET
BT /F1 11.2 Tf 56 603.9219999999999 Td (de consulta de inventario para recuperar stock de farmacias geolocalizadas; medios de) Tj ET
BT /F1 11.2 Tf 56 589.5859999999999 Td (contraste para comparar la referencia de medicamento o necesidad terapéutica con el) Tj ET
BT /F1 11.2 Tf 56 575.2499999999999 Td (stock disponible; y medios de generación de acciones para producir una reserva, aviso,) Tj ET
BT /F1 11.2 Tf 56 560.9139999999999 Td (pedido o derivación cuando el stock disponible sea insuficiente.) Tj ET
BT /F1 11.2 Tf 56 544.5779999999999 Td (Reivindicación 2. Sistema según la reivindicación 1, en el que los medios de consulta) Tj ET
BT /F1 11.2 Tf 56 530.2419999999998 Td (de inventario ordenan las farmacias según una combinación de stock disponible,) Tj ET
BT /F1 11.2 Tf 56 515.9059999999998 Td (proximidad geográfica y horario de apertura.) Tj ET
BT /F1 11.2 Tf 56 499.5699999999998 Td (Reivindicación 3. Sistema según cualquiera de las reivindicaciones anteriores, en el) Tj ET
BT /F1 11.2 Tf 56 485.2339999999998 Td (que los datos de la tarjeta sanitaria individual son tratados mediante un módulo de) Tj ET
BT /F1 11.2 Tf 56 470.8979999999998 Td (minimización y cifrado para limitar su almacenamiento a los campos técnicamente) Tj ET
BT /F1 11.2 Tf 56 456.5619999999998 Td (necesarios.) Tj ET
BT /F1 11.2 Tf 56 440.2259999999998 Td (Reivindicación 4. Procedimiento implementado por ordenador para asistir en la) Tj ET
BT /F1 11.2 Tf 56 425.88999999999976 Td (localización y suministro de medicamentos, caracterizado porque comprende escanear una) Tj ET
BT /F1 11.2 Tf 56 411.55399999999975 Td (tarjeta sanitaria individual; verificar la autorización del usuario; derivar una) Tj ET
BT /F1 11.2 Tf 56 397.21799999999973 Td (necesidad terapéutica o referencia de medicamento; consultar stock de farmacias) Tj ET
BT /F1 11.2 Tf 56 382.8819999999997 Td (geolocalizadas; comparar la necesidad con el stock disponible; y generar una acción) Tj ET
BT /F1 11.2 Tf 56 368.5459999999997 Td (técnica de reserva, aviso, pedido o derivación cuando no exista disponibilidad) Tj ET
BT /F1 11.2 Tf 56 354.2099999999997 Td (suficiente.) Tj ET
BT /F1 11.2 Tf 56 337.8739999999997 Td (Reivindicación 5. Dispositivo móvil, terminal o sistema de lectura que comprende una) Tj ET
BT /F1 11.2 Tf 56 323.53799999999967 Td (cámara o lector óptico y que está configurado para ejecutar el sistema de cualquiera) Tj ET
BT /F1 11.2 Tf 56 309.20199999999966 Td (de las reivindicaciones anteriores.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 5) Tj ET
endstream
endobj
35 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
36 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
37 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
43 0 obj
<</Length 4216 >> stream
BT /F2 13 Tf 56 781.89 Td (9. Aplicación industrial) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención es susceptible de aplicación industrial en plataformas de asistencia) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (farmacéutica, sistemas de gestión de inventario, servicios de reserva o pedido de) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (medicamentos y terminales de consulta sanitaria.) Tj ET
BT /F2 13 Tf 56 703.0899999999999 Td (10. Resumen) Tj ET
0.2 w 56 682.4499999999999 m 539.28 682.4499999999999 l S
BT /F1 11.5 Tf 56 672.4499999999999 Td (La presente invención se refiere a un sistema y procedimiento implementados por) Tj ET
BT /F1 11.5 Tf 56 657.7299999999999 Td (ordenador que permiten, a partir del escaneo de una tarjeta sanitaria individual) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (mediante un terminal móvil o dispositivo equivalente, asociar de forma segura un) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (contexto de paciente a una necesidad terapéutica, obtener una referencia de) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (medicamentos relevantes, comparar dicha necesidad con el stock disponible en) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (farmacias geolocalizadas y generar, en caso de falta de disponibilidad, una acción) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (de reserva, aviso, solicitud de pedido o derivación a una farmacia alternativa. El) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (sistema integra captura de datos, verificación de identidad, geolocalización,) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (consulta de disponibilidad, gestión de stock y generación de eventos de suministro) Tj ET
BT /F1 11.5 Tf 56 539.9699999999997 Td (en una única cadena técnica de procesamiento.) Tj ET
BT /F2 13 Tf 56 521.2499999999997 Td (11. Anexo de dibujos) Tj ET
0.2 w 56 500.6099999999997 m 539.28 500.6099999999997 l S
BT /F1 11.5 Tf 56 490.6099999999997 Td (Las figuras indicadas en la presente memoria tienen carácter esquemático y se) Tj ET
BT /F1 11.5 Tf 56 475.88999999999965 Td (incorporan a efectos ilustrativos de la arquitectura, del flujo de captura, del) Tj ET
BT /F1 11.5 Tf 56 461.1699999999996 Td (contraste de stock y de la generación de acciones. En una presentación definitiva,) Tj ET
BT /F1 11.5 Tf 56 446.4499999999996 Td (dichas figuras podrán acompañarse como láminas separadas numeradas de forma) Tj ET
BT /F1 11.5 Tf 56 431.72999999999956 Td (consecutiva.) Tj ET
BT /F2 13 Tf 56 413.00999999999954 Td (12. Firma del solicitante) Tj ET
0.2 w 56 392.36999999999955 m 539.28 392.36999999999955 l S
BT /F1 11.5 Tf 56 382.36999999999955 Td (En Terrassa, a 22 de junio de 2026.) Tj ET
BT /F1 11.5 Tf 56 365.6499999999995 Td (Fdo.: Antoni Nuñez Romeu) Tj ET
BT /F1 11.5 Tf 56 348.9299999999995 Td (DNI/NIF: 45858029P) Tj ET
BT /F2 13 Tf 56 330.20999999999947 Td (13. Notas para presentación en OEPM) Tj ET
0.2 w 56 309.5699999999995 m 539.28 309.5699999999995 l S
BT /F1 11.2 Tf 64 299.5699999999995 Td (" La solicitud debe presentarse como una única invención o un único concepto inventivo) Tj ET
BT /F1 11.2 Tf 64 285.23399999999947 Td ( general.) Tj ET
BT /F1 11.2 Tf 64 270.89799999999946 Td (" La descripción, reivindicaciones y dibujos deben ser coherentes entre sí.) Tj ET
BT /F1 11.2 Tf 64 256.56199999999944 Td (" La protección en patente debe apoyarse en un efecto técnico concreto y no en una mera) Tj ET
BT /F1 11.2 Tf 64 242.22599999999943 Td ( regla comercial o método abstracto.) Tj ET
BT /F1 11.2 Tf 64 227.88999999999942 Td (" Esta redacción evita formular una reivindicación independiente de "programa de) Tj ET
BT /F1 11.2 Tf 64 213.5539999999994 Td ( ordenador como tal" y se centra en un sistema y un procedimiento implementados por) Tj ET
BT /F1 11.2 Tf 64 199.2179999999994 Td ( ordenador con interacción técnica sobre captura, contraste de inventario y generación) Tj ET
BT /F1 11.2 Tf 64 184.88199999999938 Td ( de acciones.) Tj ET
BT /F1 11.2 Tf 64 170.54599999999937 Td (" Antes de presentar la solicitud conviene una búsqueda de anterioridades y una revisión) Tj ET
BT /F1 11.2 Tf 64 156.20999999999935 Td ( por agente de propiedad industrial.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 6) Tj ET
endstream
endobj
46 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
47 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
48 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
50 0 obj
<</Length 1123 >> stream
BT /F2 18 Tf 231 785.89 Td (Dibujos) Tj ET
BT /F3 11.5 Tf 101 763.39 Td (Bloque limpio para la presentación de la) Tj ET
BT /F3 11.5 Tf 101 749.015 Td (solicitud) Tj ET
BT /F2 13 Tf 56 716.64 Td (Breve descripción de los dibujos) Tj ET
0.3 w 56 698.39 m 539.28 698.39 l S
BT /F1 11.5 Tf 56 690.39 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 674.015 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 657.64 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 641.265 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 612.89 Td (Nota) Tj ET
0.3 w 56 594.64 m 539.28 594.64 l S
BT /F1 11.5 Tf 56 586.64 Td (El documento fuente contiene referencias esquemáticas a las figuras, pero no) Tj ET
BT /F1 11.5 Tf 56 572.265 Td (incluye láminas técnicas dibujadas de forma separada. Si la Oficina requiere) Tj ET
BT /F1 11.5 Tf 56 557.89 Td (placas gráficas, habrá que añadirlas antes de la presentación definitiva.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Page 1) Tj ET
endstream
endobj
52 0 obj
<< /Type /Catalog /Pages 53 0 R>>
endobj
53 0 obj
<< /Type /Pages /Kids [ 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R 59 0 R ] /Count 6 >>
endobj
54 0 obj
<< /Type /Page /Parent 53 0 R /Contents 5 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
55 0 obj
<< /Type /Page /Parent 53 0 R /Contents 13 0 R /Resources <</Font <</F1 8 0 R /F2 9 0 R /F3 10 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
56 0 obj
<< /Type /Page /Parent 53 0 R /Contents 22 0 R /Resources <</Font <</F1 16 0 R /F2 17 0 R /F3 18 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
57 0 obj
<< /Type /Page /Parent 53 0 R /Contents 32 0 R /Resources <</Font <</F1 25 0 R /F2 26 0 R /F3 27 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
58 0 obj
<< /Type /Page /Parent 53 0 R /Contents 43 0 R /Resources <</Font <</F1 35 0 R /F2 36 0 R /F3 37 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
59 0 obj
<< /Type /Page /Parent 53 0 R /Contents 50 0 R /Resources <</Font <</F1 46 0 R /F2 47 0 R /F3 48 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 60
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000006 00000 f
0000000321 00000 n
0000000007 00000 f
0000000011 00000 f
0000005329 00000 n
0000005431 00000 n
0000005532 00000 n
0000000012 00000 f
0000000014 00000 f
0000005636 00000 n
0000000015 00000 f
0000000019 00000 f
0000009131 00000 n
0000009234 00000 n
0000009336 00000 n
0000000020 00000 f
0000000021 00000 f
0000000023 00000 f
0000009440 00000 n
0000000024 00000 f
0000000028 00000 f
0000012770 00000 n
0000012873 00000 n
0000012975 00000 n
0000000029 00000 f
0000000030 00000 f
0000000031 00000 f
0000000033 00000 f
0000013079 00000 n
0000000034 00000 f
0000000038 00000 f
0000016827 00000 n
0000016930 00000 n
0000017032 00000 n
0000000039 00000 f
0000000040 00000 f
0000000041 00000 f
0000000042 00000 f
0000000044 00000 f
0000017136 00000 n
0000000045 00000 f
0000000049 00000 f
0000021411 00000 n
0000021514 00000 n
0000021616 00000 n
0000000051 00000 f
0000021720 00000 n
0000000000 00000 f
0000022902 00000 n
0000022952 00000 n
0000023048 00000 n
0000023247 00000 n
0000023448 00000 n
0000023651 00000 n
0000023854 00000 n
0000024057 00000 n
trailer
<</Size 32 /ID [(ÍÃÔ£È\rù+ 43B X�) (ÍÃÔ£È\rù+ 43B X�) ] /Root 52 0 R >>
startxref
24260
%%EOF
@@ -0,0 +1,268 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
5 0 obj
<</Length 4950 >> stream
BT /F2 13 Tf 56 781.89 Td (1. Campo técnico de la invención) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención se encuadra en los sistemas informáticos aplicados al sector sanitario) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (y farmacéutico, en particular en soluciones de captura de identificadores) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (sanitarios, comparación automatizada de disponibilidad de medicamentos y asistencia) Tj ET
BT /F1 11.5 Tf 56 707.0899999999999 Td (digital a la dispensación y suministro.) Tj ET
BT /F2 13 Tf 56 688.3699999999999 Td (2. Antecedentes de la invención) Tj ET
0.2 w 56 667.7299999999999 m 539.28 667.7299999999999 l S
BT /F1 11.5 Tf 56 657.7299999999999 Td (Las soluciones actuales de búsqueda de medicamentos y farmacias permiten localizar) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (productos y mostrar stock aproximado, pero normalmente operan a partir de una) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (búsqueda manual por nombre de medicamento o principio activo. También existen) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (sistemas de geolocalización y de gestión de inventario, pero suelen estar) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (desacoplados del contexto sanitario del usuario y no integran, en una única cadena) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (técnica, la identificación mediante tarjeta sanitaria individual, la inferencia de) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (necesidades terapéuticas y la comparación en tiempo real con disponibilidad) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (farmacéutica local.) Tj ET
BT /F1 11.5 Tf 56 537.9699999999997 Td (Además, cuando no existe stock en la farmacia más próxima, el usuario debe iniciar) Tj ET
BT /F1 11.5 Tf 56 523.2499999999997 Td (manualmente nuevos procesos de búsqueda o desplazamiento, sin que el sistema) Tj ET
BT /F1 11.5 Tf 56 508.52999999999963 Td (proponga de forma automatizada acciones técnicas de reserva, pedido o derivación) Tj ET
BT /F1 11.5 Tf 56 493.8099999999996 Td (basadas en la disponibilidad real.) Tj ET
BT /F2 13 Tf 56 475.0899999999996 Td (3. Problema técnico a resolver) Tj ET
0.2 w 56 454.4499999999996 m 539.28 454.4499999999996 l S
BT /F1 11.5 Tf 56 444.4499999999996 Td (La invención resuelve el problema técnico de reducir el tiempo y la fricción entre) Tj ET
BT /F1 11.5 Tf 56 429.72999999999956 Td (la identificación de una necesidad terapéutica y la localización de una farmacia que) Tj ET
BT /F1 11.5 Tf 56 415.00999999999954 Td (disponga del medicamento adecuado, integrando:) Tj ET
BT /F1 11.2 Tf 64 398.2899999999995 Td (" lectura segura de identificadores sanitarios,) Tj ET
BT /F1 11.2 Tf 64 383.9539999999995 Td (" obtención de referencias de medicamento asociadas,) Tj ET
BT /F1 11.2 Tf 64 369.6179999999995 Td (" comparación automatizada con inventarios de farmacias,) Tj ET
BT /F1 11.2 Tf 64 355.28199999999947 Td (" priorización por proximidad y disponibilidad,) Tj ET
BT /F1 11.2 Tf 64 340.94599999999946 Td (" generación de acciones técnicas de suministro cuando no exista stock inmediato.) Tj ET
BT /F2 13 Tf 56 324.60999999999945 Td (4. Resumen de la invención) Tj ET
0.2 w 56 303.96999999999946 m 539.28 303.96999999999946 l S
BT /F1 11.5 Tf 56 293.96999999999946 Td (La invención propone un sistema y procedimiento implementados por ordenador que) Tj ET
BT /F1 11.5 Tf 56 279.24999999999943 Td (operan sobre un terminal móvil o dispositivo de lectura. El sistema captura la) Tj ET
BT /F1 11.5 Tf 56 264.5299999999994 Td (información de una tarjeta sanitaria individual mediante escaneo, valida la) Tj ET
BT /F1 11.5 Tf 56 249.8099999999994 Td (información mediante un módulo de verificación, asocia el resultado al perfil del) Tj ET
BT /F1 11.5 Tf 56 235.0899999999994 Td (usuario autenticado y obtiene una lista de medicamentos o necesidades terapéuticas) Tj ET
BT /F1 11.5 Tf 56 220.3699999999994 Td (relevantes. Posteriormente, un motor de contraste consulta un repositorio de stock) Tj ET
BT /F1 11.5 Tf 56 205.6499999999994 Td (de farmacias geolocalizadas y determina la coincidencia entre la necesidad detectada) Tj ET
BT /F1 11.5 Tf 56 190.9299999999994 Td (y la disponibilidad real en cada establecimiento. Si existe stock, el sistema) Tj ET
BT /F1 11.5 Tf 56 176.2099999999994 Td (prioriza las farmacias adecuadas y presenta una ruta de acceso o reserva. Si no) Tj ET
BT /F1 11.5 Tf 56 161.4899999999994 Td (existe stock, el sistema genera automáticamente una acción de aviso, pedido, reserva) Tj ET
BT /F1 11.5 Tf 56 146.7699999999994 Td (o derivación, conservando trazabilidad técnica del evento. La invención no se limita) Tj ET
BT /F1 11.5 Tf 56 132.04999999999941 Td (a una búsqueda manual, sino que integra captura, validación, georreferenciación,) Tj ET
BT /F1 11.5 Tf 56 117.32999999999942 Td (consulta de stock y respuesta automática en una única arquitectura técnica.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 2) Tj ET
endstream
endobj
8 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
9 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
10 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
13 0 obj
<</Length 3436 >> stream
BT /F2 13 Tf 56 781.89 Td (5. Breve descripción de los dibujos) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 734.53 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 717.81 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 701.0899999999999 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 682.3699999999999 Td (6. Descripción detallada de la invención) Tj ET
0.2 w 56 661.7299999999999 m 539.28 661.7299999999999 l S
BT /F2 11.5 Tf 56 651.7299999999999 Td (6.1 Arquitectura general) Tj ET
BT /F1 11.5 Tf 56 637.0099999999999 Td (El sistema comprende al menos los siguientes módulos:) Tj ET
BT /F1 11.2 Tf 64 620.2899999999998 Td (1. un módulo de captura de imagen o lectura óptica para escanear la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 64 605.9539999999998 Td ( individual;) Tj ET
BT /F1 11.2 Tf 64 591.6179999999998 Td (2. un módulo de extracción de datos para identificar los campos relevantes de la tarjeta;) Tj ET
BT /F1 11.2 Tf 64 577.2819999999998 Td (3. un módulo de verificación de identidad y autorización, vinculado al usuario) Tj ET
BT /F1 11.2 Tf 64 562.9459999999998 Td ( autenticado;) Tj ET
BT /F1 11.2 Tf 64 548.6099999999998 Td (4. un módulo de obtención de necesidades terapéuticas o medicamentos asociados;) Tj ET
BT /F1 11.2 Tf 64 534.2739999999998 Td (5. un módulo de geolocalización para obtener la posición del usuario o una posición de) Tj ET
BT /F1 11.2 Tf 64 519.9379999999998 Td ( referencia;) Tj ET
BT /F1 11.2 Tf 64 505.60199999999975 Td (6. un módulo de consulta de inventario para recuperar stock, precio y horario de) Tj ET
BT /F1 11.2 Tf 64 491.26599999999974 Td ( farmacias;) Tj ET
BT /F1 11.2 Tf 64 476.9299999999997 Td (7. un módulo de contraste y priorización para ordenar farmacias según disponibilidad y) Tj ET
BT /F1 11.2 Tf 64 462.5939999999997 Td ( cercanía;) Tj ET
BT /F1 11.2 Tf 64 448.2579999999997 Td (8. un módulo de generación de acciones para crear reserva, aviso, pedido o derivación;) Tj ET
BT /F1 11.2 Tf 64 433.9219999999997 Td (9. un módulo de trazabilidad y auditoría.) Tj ET
BT /F2 11.5 Tf 56 419.5859999999997 Td (6.2 Funcionamiento) Tj ET
BT /F1 11.5 Tf 56 404.86599999999964 Td (En una realización preferida, el usuario abre la aplicación y solicita el escaneo de) Tj ET
BT /F1 11.5 Tf 56 390.1459999999996 Td (su tarjeta sanitaria individual. El terminal captura la imagen o el código de la) Tj ET
BT /F1 11.5 Tf 56 375.4259999999996 Td (tarjeta y el módulo de extracción identifica los datos técnicos necesarios para) Tj ET
BT /F1 11.5 Tf 56 360.70599999999956 Td (enlazar el contexto sanitario del usuario con un conjunto de medicamentos o) Tj ET
BT /F1 11.5 Tf 56 345.98599999999954 Td (referencias de tratamiento.) Tj ET
BT /F1 11.5 Tf 56 329.2659999999995 Td (El sistema valida que la operación se ejecuta sobre un usuario autenticado y) Tj ET
BT /F1 11.5 Tf 56 314.5459999999995 Td (autorizado. A continuación, el motor de contraste consulta el inventario de) Tj ET
BT /F1 11.5 Tf 56 299.82599999999945 Td (farmacias cercanas, recuperando para cada farmacia al menos su ubicación, horario,) Tj ET
BT /F1 11.5 Tf 56 285.1059999999994 Td (stock y, opcionalmente, precio.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 3) Tj ET
endstream
endobj
16 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
17 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
18 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
22 0 obj
<</Length 3271 >> stream
BT /F2 11.5 Tf 56 783.89 Td (6.2 Funcionamiento \(continuación\)) Tj ET
BT /F1 11.5 Tf 56 769.17 Td (El módulo de priorización calcula una orden de resultados en base a criterios) Tj ET
BT /F1 11.5 Tf 56 754.4499999999999 Td (técnicos tales como existencia de stock, nivel de stock, distancia geográfica,) Tj ET
BT /F1 11.5 Tf 56 739.7299999999999 Td (horario de apertura y capacidad de reserva o solicitud de pedido.) Tj ET
BT /F1 11.5 Tf 56 723.0099999999999 Td (Cuando existe disponibilidad, el sistema muestra una lista priorizada de farmacias y) Tj ET
BT /F1 11.5 Tf 56 708.2899999999998 Td (puede iniciar una reserva o una indicación de recogida. Cuando no existe) Tj ET
BT /F1 11.5 Tf 56 693.5699999999998 Td (disponibilidad, el sistema no se limita a informar de la falta de stock, sino que) Tj ET
BT /F1 11.5 Tf 56 678.8499999999998 Td (genera una acción técnica adicional, por ejemplo alerta a la farmacia seleccionada,) Tj ET
BT /F1 11.5 Tf 56 664.1299999999998 Td (solicitud de pedido a distribución o proveedor, derivación automática a otra) Tj ET
BT /F1 11.5 Tf 56 649.4099999999997 Td (farmacia con stock o registro de una lista de espera o interés de suministro.) Tj ET
BT /F2 11.5 Tf 56 632.6899999999997 Td (6.3 Tratamiento de datos y seguridad) Tj ET
BT /F1 11.5 Tf 56 617.9699999999997 Td (La invención prevé que la información derivada del escaneo de la TSI se trate con) Tj ET
BT /F1 11.5 Tf 56 603.2499999999997 Td (medidas de minimización de datos, control de acceso, cifrado en tránsito y registro) Tj ET
BT /F1 11.5 Tf 56 588.5299999999996 Td (de eventos. El sistema no expone innecesariamente los datos de la tarjeta sanitaria) Tj ET
BT /F1 11.5 Tf 56 573.8099999999996 Td (y solo conserva los elementos imprescindibles para la operación descrita.) Tj ET
BT /F2 11.5 Tf 56 557.0899999999996 Td (6.4 Integración con la plataforma FarmaFinder) Tj ET
BT /F1 11.5 Tf 56 542.3699999999995 Td (En una implementación concreta, el sistema puede integrarse con una plataforma que) Tj ET
BT /F1 11.5 Tf 56 527.6499999999995 Td (ya disponga de catálogo de farmacias, geolocalización, stock por medicamento,) Tj ET
BT /F1 11.5 Tf 56 512.9299999999995 Td (autenticación de usuario, notificaciones push e histórico de acciones y) Tj ET
BT /F1 11.5 Tf 56 498.20999999999947 Td (preferencias.) Tj ET
BT /F1 11.5 Tf 56 481.48999999999944 Td (La integración permite que el resultado del escaneo de la TSI no se limite a una) Tj ET
BT /F1 11.5 Tf 56 466.7699999999994 Td (consulta pasiva, sino que se convierta en un disparador de decisiones técnicas sobre) Tj ET
BT /F1 11.5 Tf 56 452.0499999999994 Td (disponibilidad y suministro.) Tj ET
BT /F2 13 Tf 56 433.32999999999936 Td (7. Ejemplo de realización) Tj ET
0.2 w 56 412.6899999999994 m 539.28 412.6899999999994 l S
BT /F1 11.2 Tf 64 402.6899999999994 Td (1. El usuario inicia sesión en la aplicación.) Tj ET
BT /F1 11.2 Tf 64 388.35399999999936 Td (2. El usuario escanea su TSI con la cámara del terminal.) Tj ET
BT /F1 11.2 Tf 64 374.01799999999935 Td (3. El sistema extrae el identificador necesario y lo vincula al perfil autorizado.) Tj ET
BT /F1 11.2 Tf 64 359.68199999999933 Td (4. El motor terapéutico asocia una necesidad de medicamento o tratamiento.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 4) Tj ET
endstream
endobj
25 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
26 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
27 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
32 0 obj
<</Length 3689 >> stream
BT /F2 13 Tf 56 781.89 Td (7. Ejemplo de realización \(continuación\)) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.2 Tf 64 751.25 Td (1. El sistema consulta farmacias cercanas con stock del medicamento.) Tj ET
BT /F1 11.2 Tf 64 736.914 Td (2. Si hay stock, ordena las farmacias por proximidad y disponibilidad.) Tj ET
BT /F1 11.2 Tf 64 722.578 Td (3. Si no hay stock, genera una reserva, una alerta o una solicitud de pedido.) Tj ET
BT /F2 13 Tf 56 706.242 Td (8. Reivindicaciones) Tj ET
0.2 w 56 685.602 m 539.28 685.602 l S
BT /F1 11.2 Tf 56 675.602 Td (Reivindicación 1. Sistema implementado por ordenador para identificar una necesidad) Tj ET
BT /F1 11.2 Tf 56 661.266 Td (terapéutica a partir del escaneo de una tarjeta sanitaria individual, caracterizado) Tj ET
BT /F1 11.2 Tf 56 646.93 Td (porque comprende medios de captura para obtener datos de la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 56 632.5939999999999 Td (individual; medios de verificación para asociar dichos datos a un usuario autenticado;) Tj ET
BT /F1 11.2 Tf 56 618.2579999999999 Td (medios de obtención de una referencia de medicamento o necesidad terapéutica; medios) Tj ET
BT /F1 11.2 Tf 56 603.9219999999999 Td (de consulta de inventario para recuperar stock de farmacias geolocalizadas; medios de) Tj ET
BT /F1 11.2 Tf 56 589.5859999999999 Td (contraste para comparar la referencia de medicamento o necesidad terapéutica con el) Tj ET
BT /F1 11.2 Tf 56 575.2499999999999 Td (stock disponible; y medios de generación de acciones para producir una reserva, aviso,) Tj ET
BT /F1 11.2 Tf 56 560.9139999999999 Td (pedido o derivación cuando el stock disponible sea insuficiente.) Tj ET
BT /F1 11.2 Tf 56 544.5779999999999 Td (Reivindicación 2. Sistema según la reivindicación 1, en el que los medios de consulta) Tj ET
BT /F1 11.2 Tf 56 530.2419999999998 Td (de inventario ordenan las farmacias según una combinación de stock disponible,) Tj ET
BT /F1 11.2 Tf 56 515.9059999999998 Td (proximidad geográfica y horario de apertura.) Tj ET
BT /F1 11.2 Tf 56 499.5699999999998 Td (Reivindicación 3. Sistema según cualquiera de las reivindicaciones anteriores, en el) Tj ET
BT /F1 11.2 Tf 56 485.2339999999998 Td (que los datos de la tarjeta sanitaria individual son tratados mediante un módulo de) Tj ET
BT /F1 11.2 Tf 56 470.8979999999998 Td (minimización y cifrado para limitar su almacenamiento a los campos técnicamente) Tj ET
BT /F1 11.2 Tf 56 456.5619999999998 Td (necesarios.) Tj ET
BT /F1 11.2 Tf 56 440.2259999999998 Td (Reivindicación 4. Procedimiento implementado por ordenador para asistir en la) Tj ET
BT /F1 11.2 Tf 56 425.88999999999976 Td (localización y suministro de medicamentos, caracterizado porque comprende escanear una) Tj ET
BT /F1 11.2 Tf 56 411.55399999999975 Td (tarjeta sanitaria individual; verificar la autorización del usuario; derivar una) Tj ET
BT /F1 11.2 Tf 56 397.21799999999973 Td (necesidad terapéutica o referencia de medicamento; consultar stock de farmacias) Tj ET
BT /F1 11.2 Tf 56 382.8819999999997 Td (geolocalizadas; comparar la necesidad con el stock disponible; y generar una acción) Tj ET
BT /F1 11.2 Tf 56 368.5459999999997 Td (técnica de reserva, aviso, pedido o derivación cuando no exista disponibilidad) Tj ET
BT /F1 11.2 Tf 56 354.2099999999997 Td (suficiente.) Tj ET
BT /F1 11.2 Tf 56 337.8739999999997 Td (Reivindicación 5. Dispositivo móvil, terminal o sistema de lectura que comprende una) Tj ET
BT /F1 11.2 Tf 56 323.53799999999967 Td (cámara o lector óptico y que está configurado para ejecutar el sistema de cualquiera) Tj ET
BT /F1 11.2 Tf 56 309.20199999999966 Td (de las reivindicaciones anteriores.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 5) Tj ET
endstream
endobj
34 0 obj
<< /Type /Catalog /Pages 35 0 R>>
endobj
35 0 obj
<< /Type /Pages /Kids [ 36 0 R 37 0 R 38 0 R 39 0 R ] /Count 4 >>
endobj
36 0 obj
<< /Type /Page /Parent 35 0 R /Contents 5 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
37 0 obj
<< /Type /Page /Parent 35 0 R /Contents 13 0 R /Resources <</Font <</F1 8 0 R /F2 9 0 R /F3 10 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
38 0 obj
<< /Type /Page /Parent 35 0 R /Contents 22 0 R /Resources <</Font <</F1 16 0 R /F2 17 0 R /F3 18 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
39 0 obj
<< /Type /Page /Parent 35 0 R /Contents 32 0 R /Resources <</Font <</F1 25 0 R /F2 26 0 R /F3 27 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 40
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000006 00000 f
0000000321 00000 n
0000000007 00000 f
0000000011 00000 f
0000005329 00000 n
0000005431 00000 n
0000005532 00000 n
0000000012 00000 f
0000000014 00000 f
0000005636 00000 n
0000000015 00000 f
0000000019 00000 f
0000009131 00000 n
0000009234 00000 n
0000009336 00000 n
0000000020 00000 f
0000000021 00000 f
0000000023 00000 f
0000009440 00000 n
0000000024 00000 f
0000000028 00000 f
0000012770 00000 n
0000012873 00000 n
0000012975 00000 n
0000000029 00000 f
0000000030 00000 f
0000000031 00000 f
0000000033 00000 f
0000013079 00000 n
0000000000 00000 f
0000016827 00000 n
0000016877 00000 n
0000016959 00000 n
0000017158 00000 n
0000017359 00000 n
0000017562 00000 n
trailer
<</Size 22 /ID [(J®¡já¦�é÷¿\ng/) (J®¡já¦�é÷¿\ng/) ] /Root 34 0 R >>
startxref
17765
%%EOF
@@ -0,0 +1,206 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
5 0 obj
<</Length 4950 >> stream
BT /F2 13 Tf 56 781.89 Td (1. Campo técnico de la invención) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención se encuadra en los sistemas informáticos aplicados al sector sanitario) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (y farmacéutico, en particular en soluciones de captura de identificadores) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (sanitarios, comparación automatizada de disponibilidad de medicamentos y asistencia) Tj ET
BT /F1 11.5 Tf 56 707.0899999999999 Td (digital a la dispensación y suministro.) Tj ET
BT /F2 13 Tf 56 688.3699999999999 Td (2. Antecedentes de la invención) Tj ET
0.2 w 56 667.7299999999999 m 539.28 667.7299999999999 l S
BT /F1 11.5 Tf 56 657.7299999999999 Td (Las soluciones actuales de búsqueda de medicamentos y farmacias permiten localizar) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (productos y mostrar stock aproximado, pero normalmente operan a partir de una) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (búsqueda manual por nombre de medicamento o principio activo. También existen) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (sistemas de geolocalización y de gestión de inventario, pero suelen estar) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (desacoplados del contexto sanitario del usuario y no integran, en una única cadena) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (técnica, la identificación mediante tarjeta sanitaria individual, la inferencia de) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (necesidades terapéuticas y la comparación en tiempo real con disponibilidad) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (farmacéutica local.) Tj ET
BT /F1 11.5 Tf 56 537.9699999999997 Td (Además, cuando no existe stock en la farmacia más próxima, el usuario debe iniciar) Tj ET
BT /F1 11.5 Tf 56 523.2499999999997 Td (manualmente nuevos procesos de búsqueda o desplazamiento, sin que el sistema) Tj ET
BT /F1 11.5 Tf 56 508.52999999999963 Td (proponga de forma automatizada acciones técnicas de reserva, pedido o derivación) Tj ET
BT /F1 11.5 Tf 56 493.8099999999996 Td (basadas en la disponibilidad real.) Tj ET
BT /F2 13 Tf 56 475.0899999999996 Td (3. Problema técnico a resolver) Tj ET
0.2 w 56 454.4499999999996 m 539.28 454.4499999999996 l S
BT /F1 11.5 Tf 56 444.4499999999996 Td (La invención resuelve el problema técnico de reducir el tiempo y la fricción entre) Tj ET
BT /F1 11.5 Tf 56 429.72999999999956 Td (la identificación de una necesidad terapéutica y la localización de una farmacia que) Tj ET
BT /F1 11.5 Tf 56 415.00999999999954 Td (disponga del medicamento adecuado, integrando:) Tj ET
BT /F1 11.2 Tf 64 398.2899999999995 Td (" lectura segura de identificadores sanitarios,) Tj ET
BT /F1 11.2 Tf 64 383.9539999999995 Td (" obtención de referencias de medicamento asociadas,) Tj ET
BT /F1 11.2 Tf 64 369.6179999999995 Td (" comparación automatizada con inventarios de farmacias,) Tj ET
BT /F1 11.2 Tf 64 355.28199999999947 Td (" priorización por proximidad y disponibilidad,) Tj ET
BT /F1 11.2 Tf 64 340.94599999999946 Td (" generación de acciones técnicas de suministro cuando no exista stock inmediato.) Tj ET
BT /F2 13 Tf 56 324.60999999999945 Td (4. Resumen de la invención) Tj ET
0.2 w 56 303.96999999999946 m 539.28 303.96999999999946 l S
BT /F1 11.5 Tf 56 293.96999999999946 Td (La invención propone un sistema y procedimiento implementados por ordenador que) Tj ET
BT /F1 11.5 Tf 56 279.24999999999943 Td (operan sobre un terminal móvil o dispositivo de lectura. El sistema captura la) Tj ET
BT /F1 11.5 Tf 56 264.5299999999994 Td (información de una tarjeta sanitaria individual mediante escaneo, valida la) Tj ET
BT /F1 11.5 Tf 56 249.8099999999994 Td (información mediante un módulo de verificación, asocia el resultado al perfil del) Tj ET
BT /F1 11.5 Tf 56 235.0899999999994 Td (usuario autenticado y obtiene una lista de medicamentos o necesidades terapéuticas) Tj ET
BT /F1 11.5 Tf 56 220.3699999999994 Td (relevantes. Posteriormente, un motor de contraste consulta un repositorio de stock) Tj ET
BT /F1 11.5 Tf 56 205.6499999999994 Td (de farmacias geolocalizadas y determina la coincidencia entre la necesidad detectada) Tj ET
BT /F1 11.5 Tf 56 190.9299999999994 Td (y la disponibilidad real en cada establecimiento. Si existe stock, el sistema) Tj ET
BT /F1 11.5 Tf 56 176.2099999999994 Td (prioriza las farmacias adecuadas y presenta una ruta de acceso o reserva. Si no) Tj ET
BT /F1 11.5 Tf 56 161.4899999999994 Td (existe stock, el sistema genera automáticamente una acción de aviso, pedido, reserva) Tj ET
BT /F1 11.5 Tf 56 146.7699999999994 Td (o derivación, conservando trazabilidad técnica del evento. La invención no se limita) Tj ET
BT /F1 11.5 Tf 56 132.04999999999941 Td (a una búsqueda manual, sino que integra captura, validación, georreferenciación,) Tj ET
BT /F1 11.5 Tf 56 117.32999999999942 Td (consulta de stock y respuesta automática en una única arquitectura técnica.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 2) Tj ET
endstream
endobj
8 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
9 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
10 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
13 0 obj
<</Length 3436 >> stream
BT /F2 13 Tf 56 781.89 Td (5. Breve descripción de los dibujos) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 734.53 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 717.81 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 701.0899999999999 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 682.3699999999999 Td (6. Descripción detallada de la invención) Tj ET
0.2 w 56 661.7299999999999 m 539.28 661.7299999999999 l S
BT /F2 11.5 Tf 56 651.7299999999999 Td (6.1 Arquitectura general) Tj ET
BT /F1 11.5 Tf 56 637.0099999999999 Td (El sistema comprende al menos los siguientes módulos:) Tj ET
BT /F1 11.2 Tf 64 620.2899999999998 Td (1. un módulo de captura de imagen o lectura óptica para escanear la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 64 605.9539999999998 Td ( individual;) Tj ET
BT /F1 11.2 Tf 64 591.6179999999998 Td (2. un módulo de extracción de datos para identificar los campos relevantes de la tarjeta;) Tj ET
BT /F1 11.2 Tf 64 577.2819999999998 Td (3. un módulo de verificación de identidad y autorización, vinculado al usuario) Tj ET
BT /F1 11.2 Tf 64 562.9459999999998 Td ( autenticado;) Tj ET
BT /F1 11.2 Tf 64 548.6099999999998 Td (4. un módulo de obtención de necesidades terapéuticas o medicamentos asociados;) Tj ET
BT /F1 11.2 Tf 64 534.2739999999998 Td (5. un módulo de geolocalización para obtener la posición del usuario o una posición de) Tj ET
BT /F1 11.2 Tf 64 519.9379999999998 Td ( referencia;) Tj ET
BT /F1 11.2 Tf 64 505.60199999999975 Td (6. un módulo de consulta de inventario para recuperar stock, precio y horario de) Tj ET
BT /F1 11.2 Tf 64 491.26599999999974 Td ( farmacias;) Tj ET
BT /F1 11.2 Tf 64 476.9299999999997 Td (7. un módulo de contraste y priorización para ordenar farmacias según disponibilidad y) Tj ET
BT /F1 11.2 Tf 64 462.5939999999997 Td ( cercanía;) Tj ET
BT /F1 11.2 Tf 64 448.2579999999997 Td (8. un módulo de generación de acciones para crear reserva, aviso, pedido o derivación;) Tj ET
BT /F1 11.2 Tf 64 433.9219999999997 Td (9. un módulo de trazabilidad y auditoría.) Tj ET
BT /F2 11.5 Tf 56 419.5859999999997 Td (6.2 Funcionamiento) Tj ET
BT /F1 11.5 Tf 56 404.86599999999964 Td (En una realización preferida, el usuario abre la aplicación y solicita el escaneo de) Tj ET
BT /F1 11.5 Tf 56 390.1459999999996 Td (su tarjeta sanitaria individual. El terminal captura la imagen o el código de la) Tj ET
BT /F1 11.5 Tf 56 375.4259999999996 Td (tarjeta y el módulo de extracción identifica los datos técnicos necesarios para) Tj ET
BT /F1 11.5 Tf 56 360.70599999999956 Td (enlazar el contexto sanitario del usuario con un conjunto de medicamentos o) Tj ET
BT /F1 11.5 Tf 56 345.98599999999954 Td (referencias de tratamiento.) Tj ET
BT /F1 11.5 Tf 56 329.2659999999995 Td (El sistema valida que la operación se ejecuta sobre un usuario autenticado y) Tj ET
BT /F1 11.5 Tf 56 314.5459999999995 Td (autorizado. A continuación, el motor de contraste consulta el inventario de) Tj ET
BT /F1 11.5 Tf 56 299.82599999999945 Td (farmacias cercanas, recuperando para cada farmacia al menos su ubicación, horario,) Tj ET
BT /F1 11.5 Tf 56 285.1059999999994 Td (stock y, opcionalmente, precio.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 3) Tj ET
endstream
endobj
16 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
17 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
18 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
22 0 obj
<</Length 3271 >> stream
BT /F2 11.5 Tf 56 783.89 Td (6.2 Funcionamiento \(continuación\)) Tj ET
BT /F1 11.5 Tf 56 769.17 Td (El módulo de priorización calcula una orden de resultados en base a criterios) Tj ET
BT /F1 11.5 Tf 56 754.4499999999999 Td (técnicos tales como existencia de stock, nivel de stock, distancia geográfica,) Tj ET
BT /F1 11.5 Tf 56 739.7299999999999 Td (horario de apertura y capacidad de reserva o solicitud de pedido.) Tj ET
BT /F1 11.5 Tf 56 723.0099999999999 Td (Cuando existe disponibilidad, el sistema muestra una lista priorizada de farmacias y) Tj ET
BT /F1 11.5 Tf 56 708.2899999999998 Td (puede iniciar una reserva o una indicación de recogida. Cuando no existe) Tj ET
BT /F1 11.5 Tf 56 693.5699999999998 Td (disponibilidad, el sistema no se limita a informar de la falta de stock, sino que) Tj ET
BT /F1 11.5 Tf 56 678.8499999999998 Td (genera una acción técnica adicional, por ejemplo alerta a la farmacia seleccionada,) Tj ET
BT /F1 11.5 Tf 56 664.1299999999998 Td (solicitud de pedido a distribución o proveedor, derivación automática a otra) Tj ET
BT /F1 11.5 Tf 56 649.4099999999997 Td (farmacia con stock o registro de una lista de espera o interés de suministro.) Tj ET
BT /F2 11.5 Tf 56 632.6899999999997 Td (6.3 Tratamiento de datos y seguridad) Tj ET
BT /F1 11.5 Tf 56 617.9699999999997 Td (La invención prevé que la información derivada del escaneo de la TSI se trate con) Tj ET
BT /F1 11.5 Tf 56 603.2499999999997 Td (medidas de minimización de datos, control de acceso, cifrado en tránsito y registro) Tj ET
BT /F1 11.5 Tf 56 588.5299999999996 Td (de eventos. El sistema no expone innecesariamente los datos de la tarjeta sanitaria) Tj ET
BT /F1 11.5 Tf 56 573.8099999999996 Td (y solo conserva los elementos imprescindibles para la operación descrita.) Tj ET
BT /F2 11.5 Tf 56 557.0899999999996 Td (6.4 Integración con la plataforma FarmaFinder) Tj ET
BT /F1 11.5 Tf 56 542.3699999999995 Td (En una implementación concreta, el sistema puede integrarse con una plataforma que) Tj ET
BT /F1 11.5 Tf 56 527.6499999999995 Td (ya disponga de catálogo de farmacias, geolocalización, stock por medicamento,) Tj ET
BT /F1 11.5 Tf 56 512.9299999999995 Td (autenticación de usuario, notificaciones push e histórico de acciones y) Tj ET
BT /F1 11.5 Tf 56 498.20999999999947 Td (preferencias.) Tj ET
BT /F1 11.5 Tf 56 481.48999999999944 Td (La integración permite que el resultado del escaneo de la TSI no se limite a una) Tj ET
BT /F1 11.5 Tf 56 466.7699999999994 Td (consulta pasiva, sino que se convierta en un disparador de decisiones técnicas sobre) Tj ET
BT /F1 11.5 Tf 56 452.0499999999994 Td (disponibilidad y suministro.) Tj ET
BT /F2 13 Tf 56 433.32999999999936 Td (7. Ejemplo de realización) Tj ET
0.2 w 56 412.6899999999994 m 539.28 412.6899999999994 l S
BT /F1 11.2 Tf 64 402.6899999999994 Td (1. El usuario inicia sesión en la aplicación.) Tj ET
BT /F1 11.2 Tf 64 388.35399999999936 Td (2. El usuario escanea su TSI con la cámara del terminal.) Tj ET
BT /F1 11.2 Tf 64 374.01799999999935 Td (3. El sistema extrae el identificador necesario y lo vincula al perfil autorizado.) Tj ET
BT /F1 11.2 Tf 64 359.68199999999933 Td (4. El motor terapéutico asocia una necesidad de medicamento o tratamiento.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 4) Tj ET
endstream
endobj
24 0 obj
<< /Type /Catalog /Pages 25 0 R>>
endobj
25 0 obj
<< /Type /Pages /Kids [ 26 0 R 27 0 R 28 0 R ] /Count 3 >>
endobj
26 0 obj
<< /Type /Page /Parent 25 0 R /Contents 5 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
27 0 obj
<< /Type /Page /Parent 25 0 R /Contents 13 0 R /Resources <</Font <</F1 8 0 R /F2 9 0 R /F3 10 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
28 0 obj
<< /Type /Page /Parent 25 0 R /Contents 22 0 R /Resources <</Font <</F1 16 0 R /F2 17 0 R /F3 18 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 29
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000006 00000 f
0000000321 00000 n
0000000007 00000 f
0000000011 00000 f
0000005329 00000 n
0000005431 00000 n
0000005532 00000 n
0000000012 00000 f
0000000014 00000 f
0000005636 00000 n
0000000015 00000 f
0000000019 00000 f
0000009131 00000 n
0000009234 00000 n
0000009336 00000 n
0000000020 00000 f
0000000021 00000 f
0000000023 00000 f
0000009440 00000 n
0000000000 00000 f
0000012770 00000 n
0000012820 00000 n
0000012895 00000 n
0000013094 00000 n
0000013295 00000 n
trailer
<</Size 17 /ID [(=ÁI‚³ŸÁX’âÖ®) (=ÁI‚³ŸÁX’âÖ®) ] /Root 24 0 R >>
startxref
13498
%%EOF
@@ -0,0 +1,269 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
6 0 obj
<</Length 3436 >> stream
BT /F2 13 Tf 56 781.89 Td (5. Breve descripción de los dibujos) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 734.53 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 717.81 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 701.0899999999999 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 682.3699999999999 Td (6. Descripción detallada de la invención) Tj ET
0.2 w 56 661.7299999999999 m 539.28 661.7299999999999 l S
BT /F2 11.5 Tf 56 651.7299999999999 Td (6.1 Arquitectura general) Tj ET
BT /F1 11.5 Tf 56 637.0099999999999 Td (El sistema comprende al menos los siguientes módulos:) Tj ET
BT /F1 11.2 Tf 64 620.2899999999998 Td (1. un módulo de captura de imagen o lectura óptica para escanear la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 64 605.9539999999998 Td ( individual;) Tj ET
BT /F1 11.2 Tf 64 591.6179999999998 Td (2. un módulo de extracción de datos para identificar los campos relevantes de la tarjeta;) Tj ET
BT /F1 11.2 Tf 64 577.2819999999998 Td (3. un módulo de verificación de identidad y autorización, vinculado al usuario) Tj ET
BT /F1 11.2 Tf 64 562.9459999999998 Td ( autenticado;) Tj ET
BT /F1 11.2 Tf 64 548.6099999999998 Td (4. un módulo de obtención de necesidades terapéuticas o medicamentos asociados;) Tj ET
BT /F1 11.2 Tf 64 534.2739999999998 Td (5. un módulo de geolocalización para obtener la posición del usuario o una posición de) Tj ET
BT /F1 11.2 Tf 64 519.9379999999998 Td ( referencia;) Tj ET
BT /F1 11.2 Tf 64 505.60199999999975 Td (6. un módulo de consulta de inventario para recuperar stock, precio y horario de) Tj ET
BT /F1 11.2 Tf 64 491.26599999999974 Td ( farmacias;) Tj ET
BT /F1 11.2 Tf 64 476.9299999999997 Td (7. un módulo de contraste y priorización para ordenar farmacias según disponibilidad y) Tj ET
BT /F1 11.2 Tf 64 462.5939999999997 Td ( cercanía;) Tj ET
BT /F1 11.2 Tf 64 448.2579999999997 Td (8. un módulo de generación de acciones para crear reserva, aviso, pedido o derivación;) Tj ET
BT /F1 11.2 Tf 64 433.9219999999997 Td (9. un módulo de trazabilidad y auditoría.) Tj ET
BT /F2 11.5 Tf 56 419.5859999999997 Td (6.2 Funcionamiento) Tj ET
BT /F1 11.5 Tf 56 404.86599999999964 Td (En una realización preferida, el usuario abre la aplicación y solicita el escaneo de) Tj ET
BT /F1 11.5 Tf 56 390.1459999999996 Td (su tarjeta sanitaria individual. El terminal captura la imagen o el código de la) Tj ET
BT /F1 11.5 Tf 56 375.4259999999996 Td (tarjeta y el módulo de extracción identifica los datos técnicos necesarios para) Tj ET
BT /F1 11.5 Tf 56 360.70599999999956 Td (enlazar el contexto sanitario del usuario con un conjunto de medicamentos o) Tj ET
BT /F1 11.5 Tf 56 345.98599999999954 Td (referencias de tratamiento.) Tj ET
BT /F1 11.5 Tf 56 329.2659999999995 Td (El sistema valida que la operación se ejecuta sobre un usuario autenticado y) Tj ET
BT /F1 11.5 Tf 56 314.5459999999995 Td (autorizado. A continuación, el motor de contraste consulta el inventario de) Tj ET
BT /F1 11.5 Tf 56 299.82599999999945 Td (farmacias cercanas, recuperando para cada farmacia al menos su ubicación, horario,) Tj ET
BT /F1 11.5 Tf 56 285.1059999999994 Td (stock y, opcionalmente, precio.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 3) Tj ET
endstream
endobj
9 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
10 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
11 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
15 0 obj
<</Length 3271 >> stream
BT /F2 11.5 Tf 56 783.89 Td (6.2 Funcionamiento \(continuación\)) Tj ET
BT /F1 11.5 Tf 56 769.17 Td (El módulo de priorización calcula una orden de resultados en base a criterios) Tj ET
BT /F1 11.5 Tf 56 754.4499999999999 Td (técnicos tales como existencia de stock, nivel de stock, distancia geográfica,) Tj ET
BT /F1 11.5 Tf 56 739.7299999999999 Td (horario de apertura y capacidad de reserva o solicitud de pedido.) Tj ET
BT /F1 11.5 Tf 56 723.0099999999999 Td (Cuando existe disponibilidad, el sistema muestra una lista priorizada de farmacias y) Tj ET
BT /F1 11.5 Tf 56 708.2899999999998 Td (puede iniciar una reserva o una indicación de recogida. Cuando no existe) Tj ET
BT /F1 11.5 Tf 56 693.5699999999998 Td (disponibilidad, el sistema no se limita a informar de la falta de stock, sino que) Tj ET
BT /F1 11.5 Tf 56 678.8499999999998 Td (genera una acción técnica adicional, por ejemplo alerta a la farmacia seleccionada,) Tj ET
BT /F1 11.5 Tf 56 664.1299999999998 Td (solicitud de pedido a distribución o proveedor, derivación automática a otra) Tj ET
BT /F1 11.5 Tf 56 649.4099999999997 Td (farmacia con stock o registro de una lista de espera o interés de suministro.) Tj ET
BT /F2 11.5 Tf 56 632.6899999999997 Td (6.3 Tratamiento de datos y seguridad) Tj ET
BT /F1 11.5 Tf 56 617.9699999999997 Td (La invención prevé que la información derivada del escaneo de la TSI se trate con) Tj ET
BT /F1 11.5 Tf 56 603.2499999999997 Td (medidas de minimización de datos, control de acceso, cifrado en tránsito y registro) Tj ET
BT /F1 11.5 Tf 56 588.5299999999996 Td (de eventos. El sistema no expone innecesariamente los datos de la tarjeta sanitaria) Tj ET
BT /F1 11.5 Tf 56 573.8099999999996 Td (y solo conserva los elementos imprescindibles para la operación descrita.) Tj ET
BT /F2 11.5 Tf 56 557.0899999999996 Td (6.4 Integración con la plataforma FarmaFinder) Tj ET
BT /F1 11.5 Tf 56 542.3699999999995 Td (En una implementación concreta, el sistema puede integrarse con una plataforma que) Tj ET
BT /F1 11.5 Tf 56 527.6499999999995 Td (ya disponga de catálogo de farmacias, geolocalización, stock por medicamento,) Tj ET
BT /F1 11.5 Tf 56 512.9299999999995 Td (autenticación de usuario, notificaciones push e histórico de acciones y) Tj ET
BT /F1 11.5 Tf 56 498.20999999999947 Td (preferencias.) Tj ET
BT /F1 11.5 Tf 56 481.48999999999944 Td (La integración permite que el resultado del escaneo de la TSI no se limite a una) Tj ET
BT /F1 11.5 Tf 56 466.7699999999994 Td (consulta pasiva, sino que se convierta en un disparador de decisiones técnicas sobre) Tj ET
BT /F1 11.5 Tf 56 452.0499999999994 Td (disponibilidad y suministro.) Tj ET
BT /F2 13 Tf 56 433.32999999999936 Td (7. Ejemplo de realización) Tj ET
0.2 w 56 412.6899999999994 m 539.28 412.6899999999994 l S
BT /F1 11.2 Tf 64 402.6899999999994 Td (1. El usuario inicia sesión en la aplicación.) Tj ET
BT /F1 11.2 Tf 64 388.35399999999936 Td (2. El usuario escanea su TSI con la cámara del terminal.) Tj ET
BT /F1 11.2 Tf 64 374.01799999999935 Td (3. El sistema extrae el identificador necesario y lo vincula al perfil autorizado.) Tj ET
BT /F1 11.2 Tf 64 359.68199999999933 Td (4. El motor terapéutico asocia una necesidad de medicamento o tratamiento.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 4) Tj ET
endstream
endobj
18 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
19 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
20 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
25 0 obj
<</Length 3689 >> stream
BT /F2 13 Tf 56 781.89 Td (7. Ejemplo de realización \(continuación\)) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.2 Tf 64 751.25 Td (1. El sistema consulta farmacias cercanas con stock del medicamento.) Tj ET
BT /F1 11.2 Tf 64 736.914 Td (2. Si hay stock, ordena las farmacias por proximidad y disponibilidad.) Tj ET
BT /F1 11.2 Tf 64 722.578 Td (3. Si no hay stock, genera una reserva, una alerta o una solicitud de pedido.) Tj ET
BT /F2 13 Tf 56 706.242 Td (8. Reivindicaciones) Tj ET
0.2 w 56 685.602 m 539.28 685.602 l S
BT /F1 11.2 Tf 56 675.602 Td (Reivindicación 1. Sistema implementado por ordenador para identificar una necesidad) Tj ET
BT /F1 11.2 Tf 56 661.266 Td (terapéutica a partir del escaneo de una tarjeta sanitaria individual, caracterizado) Tj ET
BT /F1 11.2 Tf 56 646.93 Td (porque comprende medios de captura para obtener datos de la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 56 632.5939999999999 Td (individual; medios de verificación para asociar dichos datos a un usuario autenticado;) Tj ET
BT /F1 11.2 Tf 56 618.2579999999999 Td (medios de obtención de una referencia de medicamento o necesidad terapéutica; medios) Tj ET
BT /F1 11.2 Tf 56 603.9219999999999 Td (de consulta de inventario para recuperar stock de farmacias geolocalizadas; medios de) Tj ET
BT /F1 11.2 Tf 56 589.5859999999999 Td (contraste para comparar la referencia de medicamento o necesidad terapéutica con el) Tj ET
BT /F1 11.2 Tf 56 575.2499999999999 Td (stock disponible; y medios de generación de acciones para producir una reserva, aviso,) Tj ET
BT /F1 11.2 Tf 56 560.9139999999999 Td (pedido o derivación cuando el stock disponible sea insuficiente.) Tj ET
BT /F1 11.2 Tf 56 544.5779999999999 Td (Reivindicación 2. Sistema según la reivindicación 1, en el que los medios de consulta) Tj ET
BT /F1 11.2 Tf 56 530.2419999999998 Td (de inventario ordenan las farmacias según una combinación de stock disponible,) Tj ET
BT /F1 11.2 Tf 56 515.9059999999998 Td (proximidad geográfica y horario de apertura.) Tj ET
BT /F1 11.2 Tf 56 499.5699999999998 Td (Reivindicación 3. Sistema según cualquiera de las reivindicaciones anteriores, en el) Tj ET
BT /F1 11.2 Tf 56 485.2339999999998 Td (que los datos de la tarjeta sanitaria individual son tratados mediante un módulo de) Tj ET
BT /F1 11.2 Tf 56 470.8979999999998 Td (minimización y cifrado para limitar su almacenamiento a los campos técnicamente) Tj ET
BT /F1 11.2 Tf 56 456.5619999999998 Td (necesarios.) Tj ET
BT /F1 11.2 Tf 56 440.2259999999998 Td (Reivindicación 4. Procedimiento implementado por ordenador para asistir en la) Tj ET
BT /F1 11.2 Tf 56 425.88999999999976 Td (localización y suministro de medicamentos, caracterizado porque comprende escanear una) Tj ET
BT /F1 11.2 Tf 56 411.55399999999975 Td (tarjeta sanitaria individual; verificar la autorización del usuario; derivar una) Tj ET
BT /F1 11.2 Tf 56 397.21799999999973 Td (necesidad terapéutica o referencia de medicamento; consultar stock de farmacias) Tj ET
BT /F1 11.2 Tf 56 382.8819999999997 Td (geolocalizadas; comparar la necesidad con el stock disponible; y generar una acción) Tj ET
BT /F1 11.2 Tf 56 368.5459999999997 Td (técnica de reserva, aviso, pedido o derivación cuando no exista disponibilidad) Tj ET
BT /F1 11.2 Tf 56 354.2099999999997 Td (suficiente.) Tj ET
BT /F1 11.2 Tf 56 337.8739999999997 Td (Reivindicación 5. Dispositivo móvil, terminal o sistema de lectura que comprende una) Tj ET
BT /F1 11.2 Tf 56 323.53799999999967 Td (cámara o lector óptico y que está configurado para ejecutar el sistema de cualquiera) Tj ET
BT /F1 11.2 Tf 56 309.20199999999966 Td (de las reivindicaciones anteriores.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 5) Tj ET
endstream
endobj
28 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
29 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
30 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
36 0 obj
<</Length 4216 >> stream
BT /F2 13 Tf 56 781.89 Td (9. Aplicación industrial) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención es susceptible de aplicación industrial en plataformas de asistencia) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (farmacéutica, sistemas de gestión de inventario, servicios de reserva o pedido de) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (medicamentos y terminales de consulta sanitaria.) Tj ET
BT /F2 13 Tf 56 703.0899999999999 Td (10. Resumen) Tj ET
0.2 w 56 682.4499999999999 m 539.28 682.4499999999999 l S
BT /F1 11.5 Tf 56 672.4499999999999 Td (La presente invención se refiere a un sistema y procedimiento implementados por) Tj ET
BT /F1 11.5 Tf 56 657.7299999999999 Td (ordenador que permiten, a partir del escaneo de una tarjeta sanitaria individual) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (mediante un terminal móvil o dispositivo equivalente, asociar de forma segura un) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (contexto de paciente a una necesidad terapéutica, obtener una referencia de) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (medicamentos relevantes, comparar dicha necesidad con el stock disponible en) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (farmacias geolocalizadas y generar, en caso de falta de disponibilidad, una acción) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (de reserva, aviso, solicitud de pedido o derivación a una farmacia alternativa. El) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (sistema integra captura de datos, verificación de identidad, geolocalización,) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (consulta de disponibilidad, gestión de stock y generación de eventos de suministro) Tj ET
BT /F1 11.5 Tf 56 539.9699999999997 Td (en una única cadena técnica de procesamiento.) Tj ET
BT /F2 13 Tf 56 521.2499999999997 Td (11. Anexo de dibujos) Tj ET
0.2 w 56 500.6099999999997 m 539.28 500.6099999999997 l S
BT /F1 11.5 Tf 56 490.6099999999997 Td (Las figuras indicadas en la presente memoria tienen carácter esquemático y se) Tj ET
BT /F1 11.5 Tf 56 475.88999999999965 Td (incorporan a efectos ilustrativos de la arquitectura, del flujo de captura, del) Tj ET
BT /F1 11.5 Tf 56 461.1699999999996 Td (contraste de stock y de la generación de acciones. En una presentación definitiva,) Tj ET
BT /F1 11.5 Tf 56 446.4499999999996 Td (dichas figuras podrán acompañarse como láminas separadas numeradas de forma) Tj ET
BT /F1 11.5 Tf 56 431.72999999999956 Td (consecutiva.) Tj ET
BT /F2 13 Tf 56 413.00999999999954 Td (12. Firma del solicitante) Tj ET
0.2 w 56 392.36999999999955 m 539.28 392.36999999999955 l S
BT /F1 11.5 Tf 56 382.36999999999955 Td (En Terrassa, a 22 de junio de 2026.) Tj ET
BT /F1 11.5 Tf 56 365.6499999999995 Td (Fdo.: Antoni Nuñez Romeu) Tj ET
BT /F1 11.5 Tf 56 348.9299999999995 Td (DNI/NIF: 45858029P) Tj ET
BT /F2 13 Tf 56 330.20999999999947 Td (13. Notas para presentación en OEPM) Tj ET
0.2 w 56 309.5699999999995 m 539.28 309.5699999999995 l S
BT /F1 11.2 Tf 64 299.5699999999995 Td (" La solicitud debe presentarse como una única invención o un único concepto inventivo) Tj ET
BT /F1 11.2 Tf 64 285.23399999999947 Td ( general.) Tj ET
BT /F1 11.2 Tf 64 270.89799999999946 Td (" La descripción, reivindicaciones y dibujos deben ser coherentes entre sí.) Tj ET
BT /F1 11.2 Tf 64 256.56199999999944 Td (" La protección en patente debe apoyarse en un efecto técnico concreto y no en una mera) Tj ET
BT /F1 11.2 Tf 64 242.22599999999943 Td ( regla comercial o método abstracto.) Tj ET
BT /F1 11.2 Tf 64 227.88999999999942 Td (" Esta redacción evita formular una reivindicación independiente de "programa de) Tj ET
BT /F1 11.2 Tf 64 213.5539999999994 Td ( ordenador como tal" y se centra en un sistema y un procedimiento implementados por) Tj ET
BT /F1 11.2 Tf 64 199.2179999999994 Td ( ordenador con interacción técnica sobre captura, contraste de inventario y generación) Tj ET
BT /F1 11.2 Tf 64 184.88199999999938 Td ( de acciones.) Tj ET
BT /F1 11.2 Tf 64 170.54599999999937 Td (" Antes de presentar la solicitud conviene una búsqueda de anterioridades y una revisión) Tj ET
BT /F1 11.2 Tf 64 156.20999999999935 Td ( por agente de propiedad industrial.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 6) Tj ET
endstream
endobj
38 0 obj
<< /Type /Catalog /Pages 39 0 R>>
endobj
39 0 obj
<< /Type /Pages /Kids [ 40 0 R 41 0 R 42 0 R 43 0 R ] /Count 4 >>
endobj
40 0 obj
<< /Type /Page /Parent 39 0 R /Contents 6 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
41 0 obj
<< /Type /Page /Parent 39 0 R /Contents 15 0 R /Resources <</Font <</F1 9 0 R /F2 10 0 R /F3 11 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
42 0 obj
<< /Type /Page /Parent 39 0 R /Contents 25 0 R /Resources <</Font <</F1 18 0 R /F2 19 0 R /F3 20 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
43 0 obj
<< /Type /Page /Parent 39 0 R /Contents 36 0 R /Resources <</Font <</F1 28 0 R /F2 29 0 R /F3 30 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 44
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000005 00000 f
0000000007 00000 f
0000000321 00000 n
0000000008 00000 f
0000000012 00000 f
0000003815 00000 n
0000003917 00000 n
0000004019 00000 n
0000000013 00000 f
0000000014 00000 f
0000000016 00000 f
0000004123 00000 n
0000000017 00000 f
0000000021 00000 f
0000007453 00000 n
0000007556 00000 n
0000007658 00000 n
0000000022 00000 f
0000000023 00000 f
0000000024 00000 f
0000000026 00000 f
0000007762 00000 n
0000000027 00000 f
0000000031 00000 f
0000011510 00000 n
0000011613 00000 n
0000011715 00000 n
0000000032 00000 f
0000000033 00000 f
0000000034 00000 f
0000000035 00000 f
0000000037 00000 f
0000011819 00000 n
0000000000 00000 f
0000016094 00000 n
0000016144 00000 n
0000016226 00000 n
0000016425 00000 n
0000016627 00000 n
0000016830 00000 n
trailer
<</Size 22 /ID [(¦¼—¾ÄZ‚}×±<pÿ) (¦¼—¾ÄZ‚}×±<pÿ) ] /Root 38 0 R >>
startxref
17033
%%EOF
@@ -0,0 +1,53 @@
%PDF-1.4
1 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
4 0 obj
<< /Type /Pages /Kids [6 0 R] /Count 1 >>
endobj
5 0 obj
<< /Length 1123 >>
stream
BT /F2 18 Tf 231 785.89 Td (Dibujos) Tj ET
BT /F3 11.5 Tf 101 763.39 Td (Bloque limpio para la presentación de la) Tj ET
BT /F3 11.5 Tf 101 749.015 Td (solicitud) Tj ET
BT /F2 13 Tf 56 716.64 Td (Breve descripción de los dibujos) Tj ET
0.3 w 56 698.39 m 539.28 698.39 l S
BT /F1 11.5 Tf 56 690.39 Td (Figura 1. Diagrama de bloques general del sistema.) Tj ET
BT /F1 11.5 Tf 56 674.015 Td (Figura 2. Flujo de captura y validación del escaneo de la TSI.) Tj ET
BT /F1 11.5 Tf 56 657.64 Td (Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.) Tj ET
BT /F1 11.5 Tf 56 641.265 Td (Figura 4. Flujo de acción cuando no existe stock disponible.) Tj ET
BT /F2 13 Tf 56 612.89 Td (Nota) Tj ET
0.3 w 56 594.64 m 539.28 594.64 l S
BT /F1 11.5 Tf 56 586.64 Td (El documento fuente contiene referencias esquemáticas a las figuras, pero no) Tj ET
BT /F1 11.5 Tf 56 572.265 Td (incluye láminas técnicas dibujadas de forma separada. Si la Oficina requiere) Tj ET
BT /F1 11.5 Tf 56 557.89 Td (placas gráficas, habrá que añadirlas antes de la presentación definitiva.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Page 1) Tj ET
endstream
endobj
6 0 obj
<< /Type /Page /Parent 4 0 R /MediaBox [0 0 595.28 841.89] /Resources << /Font << /F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /Contents 5 0 R >>
endobj
7 0 obj
<< /Type /Catalog /Pages 4 0 R >>
endobj
xref
0 8
0000000000 65535 f
0000000009 00000 n
0000000108 00000 n
0000000206 00000 n
0000000306 00000 n
0000000363 00000 n
0000001538 00000 n
0000001690 00000 n
trailer << /Size 8 /Root 7 0 R >>
startxref
1739
%%EOF
@@ -0,0 +1,79 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
8 0 obj
<</Length 3689 >> stream
BT /F2 13 Tf 56 781.89 Td (7. Ejemplo de realización \(continuación\)) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.2 Tf 64 751.25 Td (1. El sistema consulta farmacias cercanas con stock del medicamento.) Tj ET
BT /F1 11.2 Tf 64 736.914 Td (2. Si hay stock, ordena las farmacias por proximidad y disponibilidad.) Tj ET
BT /F1 11.2 Tf 64 722.578 Td (3. Si no hay stock, genera una reserva, una alerta o una solicitud de pedido.) Tj ET
BT /F2 13 Tf 56 706.242 Td (8. Reivindicaciones) Tj ET
0.2 w 56 685.602 m 539.28 685.602 l S
BT /F1 11.2 Tf 56 675.602 Td (Reivindicación 1. Sistema implementado por ordenador para identificar una necesidad) Tj ET
BT /F1 11.2 Tf 56 661.266 Td (terapéutica a partir del escaneo de una tarjeta sanitaria individual, caracterizado) Tj ET
BT /F1 11.2 Tf 56 646.93 Td (porque comprende medios de captura para obtener datos de la tarjeta sanitaria) Tj ET
BT /F1 11.2 Tf 56 632.5939999999999 Td (individual; medios de verificación para asociar dichos datos a un usuario autenticado;) Tj ET
BT /F1 11.2 Tf 56 618.2579999999999 Td (medios de obtención de una referencia de medicamento o necesidad terapéutica; medios) Tj ET
BT /F1 11.2 Tf 56 603.9219999999999 Td (de consulta de inventario para recuperar stock de farmacias geolocalizadas; medios de) Tj ET
BT /F1 11.2 Tf 56 589.5859999999999 Td (contraste para comparar la referencia de medicamento o necesidad terapéutica con el) Tj ET
BT /F1 11.2 Tf 56 575.2499999999999 Td (stock disponible; y medios de generación de acciones para producir una reserva, aviso,) Tj ET
BT /F1 11.2 Tf 56 560.9139999999999 Td (pedido o derivación cuando el stock disponible sea insuficiente.) Tj ET
BT /F1 11.2 Tf 56 544.5779999999999 Td (Reivindicación 2. Sistema según la reivindicación 1, en el que los medios de consulta) Tj ET
BT /F1 11.2 Tf 56 530.2419999999998 Td (de inventario ordenan las farmacias según una combinación de stock disponible,) Tj ET
BT /F1 11.2 Tf 56 515.9059999999998 Td (proximidad geográfica y horario de apertura.) Tj ET
BT /F1 11.2 Tf 56 499.5699999999998 Td (Reivindicación 3. Sistema según cualquiera de las reivindicaciones anteriores, en el) Tj ET
BT /F1 11.2 Tf 56 485.2339999999998 Td (que los datos de la tarjeta sanitaria individual son tratados mediante un módulo de) Tj ET
BT /F1 11.2 Tf 56 470.8979999999998 Td (minimización y cifrado para limitar su almacenamiento a los campos técnicamente) Tj ET
BT /F1 11.2 Tf 56 456.5619999999998 Td (necesarios.) Tj ET
BT /F1 11.2 Tf 56 440.2259999999998 Td (Reivindicación 4. Procedimiento implementado por ordenador para asistir en la) Tj ET
BT /F1 11.2 Tf 56 425.88999999999976 Td (localización y suministro de medicamentos, caracterizado porque comprende escanear una) Tj ET
BT /F1 11.2 Tf 56 411.55399999999975 Td (tarjeta sanitaria individual; verificar la autorización del usuario; derivar una) Tj ET
BT /F1 11.2 Tf 56 397.21799999999973 Td (necesidad terapéutica o referencia de medicamento; consultar stock de farmacias) Tj ET
BT /F1 11.2 Tf 56 382.8819999999997 Td (geolocalizadas; comparar la necesidad con el stock disponible; y generar una acción) Tj ET
BT /F1 11.2 Tf 56 368.5459999999997 Td (técnica de reserva, aviso, pedido o derivación cuando no exista disponibilidad) Tj ET
BT /F1 11.2 Tf 56 354.2099999999997 Td (suficiente.) Tj ET
BT /F1 11.2 Tf 56 337.8739999999997 Td (Reivindicación 5. Dispositivo móvil, terminal o sistema de lectura que comprende una) Tj ET
BT /F1 11.2 Tf 56 323.53799999999967 Td (cámara o lector óptico y que está configurado para ejecutar el sistema de cualquiera) Tj ET
BT /F1 11.2 Tf 56 309.20199999999966 Td (de las reivindicaciones anteriores.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 5) Tj ET
endstream
endobj
10 0 obj
<< /Type /Catalog /Pages 11 0 R>>
endobj
11 0 obj
<< /Type /Pages /Kids [ 12 0 R ] /Count 1 >>
endobj
12 0 obj
<< /Type /Page /Parent 11 0 R /Contents 8 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 13
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000005 00000 f
0000000006 00000 f
0000000007 00000 f
0000000009 00000 f
0000000321 00000 n
0000000000 00000 f
0000004068 00000 n
0000004118 00000 n
0000004179 00000 n
trailer
<</Size 7 /ID [(ÑߊD€\\&l¹ŸÛ=�Á) (ÑߊD€\\&l¹ŸÛ=�Á) ] /Root 10 0 R >>
startxref
4378
%%EOF
@@ -0,0 +1,89 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
9 0 obj
<</Length 4216 >> stream
BT /F2 13 Tf 56 781.89 Td (9. Aplicación industrial) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención es susceptible de aplicación industrial en plataformas de asistencia) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (farmacéutica, sistemas de gestión de inventario, servicios de reserva o pedido de) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (medicamentos y terminales de consulta sanitaria.) Tj ET
BT /F2 13 Tf 56 703.0899999999999 Td (10. Resumen) Tj ET
0.2 w 56 682.4499999999999 m 539.28 682.4499999999999 l S
BT /F1 11.5 Tf 56 672.4499999999999 Td (La presente invención se refiere a un sistema y procedimiento implementados por) Tj ET
BT /F1 11.5 Tf 56 657.7299999999999 Td (ordenador que permiten, a partir del escaneo de una tarjeta sanitaria individual) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (mediante un terminal móvil o dispositivo equivalente, asociar de forma segura un) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (contexto de paciente a una necesidad terapéutica, obtener una referencia de) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (medicamentos relevantes, comparar dicha necesidad con el stock disponible en) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (farmacias geolocalizadas y generar, en caso de falta de disponibilidad, una acción) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (de reserva, aviso, solicitud de pedido o derivación a una farmacia alternativa. El) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (sistema integra captura de datos, verificación de identidad, geolocalización,) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (consulta de disponibilidad, gestión de stock y generación de eventos de suministro) Tj ET
BT /F1 11.5 Tf 56 539.9699999999997 Td (en una única cadena técnica de procesamiento.) Tj ET
BT /F2 13 Tf 56 521.2499999999997 Td (11. Anexo de dibujos) Tj ET
0.2 w 56 500.6099999999997 m 539.28 500.6099999999997 l S
BT /F1 11.5 Tf 56 490.6099999999997 Td (Las figuras indicadas en la presente memoria tienen carácter esquemático y se) Tj ET
BT /F1 11.5 Tf 56 475.88999999999965 Td (incorporan a efectos ilustrativos de la arquitectura, del flujo de captura, del) Tj ET
BT /F1 11.5 Tf 56 461.1699999999996 Td (contraste de stock y de la generación de acciones. En una presentación definitiva,) Tj ET
BT /F1 11.5 Tf 56 446.4499999999996 Td (dichas figuras podrán acompañarse como láminas separadas numeradas de forma) Tj ET
BT /F1 11.5 Tf 56 431.72999999999956 Td (consecutiva.) Tj ET
BT /F2 13 Tf 56 413.00999999999954 Td (12. Firma del solicitante) Tj ET
0.2 w 56 392.36999999999955 m 539.28 392.36999999999955 l S
BT /F1 11.5 Tf 56 382.36999999999955 Td (En Terrassa, a 22 de junio de 2026.) Tj ET
BT /F1 11.5 Tf 56 365.6499999999995 Td (Fdo.: Antoni Nuñez Romeu) Tj ET
BT /F1 11.5 Tf 56 348.9299999999995 Td (DNI/NIF: 45858029P) Tj ET
BT /F2 13 Tf 56 330.20999999999947 Td (13. Notas para presentación en OEPM) Tj ET
0.2 w 56 309.5699999999995 m 539.28 309.5699999999995 l S
BT /F1 11.2 Tf 64 299.5699999999995 Td (" La solicitud debe presentarse como una única invención o un único concepto inventivo) Tj ET
BT /F1 11.2 Tf 64 285.23399999999947 Td ( general.) Tj ET
BT /F1 11.2 Tf 64 270.89799999999946 Td (" La descripción, reivindicaciones y dibujos deben ser coherentes entre sí.) Tj ET
BT /F1 11.2 Tf 64 256.56199999999944 Td (" La protección en patente debe apoyarse en un efecto técnico concreto y no en una mera) Tj ET
BT /F1 11.2 Tf 64 242.22599999999943 Td ( regla comercial o método abstracto.) Tj ET
BT /F1 11.2 Tf 64 227.88999999999942 Td (" Esta redacción evita formular una reivindicación independiente de "programa de) Tj ET
BT /F1 11.2 Tf 64 213.5539999999994 Td ( ordenador como tal" y se centra en un sistema y un procedimiento implementados por) Tj ET
BT /F1 11.2 Tf 64 199.2179999999994 Td ( ordenador con interacción técnica sobre captura, contraste de inventario y generación) Tj ET
BT /F1 11.2 Tf 64 184.88199999999938 Td ( de acciones.) Tj ET
BT /F1 11.2 Tf 64 170.54599999999937 Td (" Antes de presentar la solicitud conviene una búsqueda de anterioridades y una revisión) Tj ET
BT /F1 11.2 Tf 64 156.20999999999935 Td ( por agente de propiedad industrial.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 6) Tj ET
endstream
endobj
11 0 obj
<< /Type /Catalog /Pages 12 0 R>>
endobj
12 0 obj
<< /Type /Pages /Kids [ 13 0 R ] /Count 1 >>
endobj
13 0 obj
<< /Type /Page /Parent 12 0 R /Contents 9 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 14
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000005 00000 f
0000000006 00000 f
0000000007 00000 f
0000000008 00000 f
0000000010 00000 f
0000000321 00000 n
0000000000 00000 f
0000004595 00000 n
0000004645 00000 n
0000004706 00000 n
trailer
<</Size 7 /ID [(†¾é^n³¼ù–ï¾xýÇ) (†¾é^n³¼ù–ï¾xýÇ) ] /Root 11 0 R >>
startxref
4905
%%EOF
@@ -0,0 +1,89 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
endobj
2 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>
endobj
3 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>
endobj
9 0 obj
<</Length 4216 >> stream
BT /F2 13 Tf 56 781.89 Td (9. Aplicación industrial) Tj ET
0.2 w 56 761.25 m 539.28 761.25 l S
BT /F1 11.5 Tf 56 751.25 Td (La invención es susceptible de aplicación industrial en plataformas de asistencia) Tj ET
BT /F1 11.5 Tf 56 736.53 Td (farmacéutica, sistemas de gestión de inventario, servicios de reserva o pedido de) Tj ET
BT /F1 11.5 Tf 56 721.81 Td (medicamentos y terminales de consulta sanitaria.) Tj ET
BT /F2 13 Tf 56 703.0899999999999 Td (10. Resumen) Tj ET
0.2 w 56 682.4499999999999 m 539.28 682.4499999999999 l S
BT /F1 11.5 Tf 56 672.4499999999999 Td (La presente invención se refiere a un sistema y procedimiento implementados por) Tj ET
BT /F1 11.5 Tf 56 657.7299999999999 Td (ordenador que permiten, a partir del escaneo de una tarjeta sanitaria individual) Tj ET
BT /F1 11.5 Tf 56 643.0099999999999 Td (mediante un terminal móvil o dispositivo equivalente, asociar de forma segura un) Tj ET
BT /F1 11.5 Tf 56 628.2899999999998 Td (contexto de paciente a una necesidad terapéutica, obtener una referencia de) Tj ET
BT /F1 11.5 Tf 56 613.5699999999998 Td (medicamentos relevantes, comparar dicha necesidad con el stock disponible en) Tj ET
BT /F1 11.5 Tf 56 598.8499999999998 Td (farmacias geolocalizadas y generar, en caso de falta de disponibilidad, una acción) Tj ET
BT /F1 11.5 Tf 56 584.1299999999998 Td (de reserva, aviso, solicitud de pedido o derivación a una farmacia alternativa. El) Tj ET
BT /F1 11.5 Tf 56 569.4099999999997 Td (sistema integra captura de datos, verificación de identidad, geolocalización,) Tj ET
BT /F1 11.5 Tf 56 554.6899999999997 Td (consulta de disponibilidad, gestión de stock y generación de eventos de suministro) Tj ET
BT /F1 11.5 Tf 56 539.9699999999997 Td (en una única cadena técnica de procesamiento.) Tj ET
BT /F2 13 Tf 56 521.2499999999997 Td (11. Anexo de dibujos) Tj ET
0.2 w 56 500.6099999999997 m 539.28 500.6099999999997 l S
BT /F1 11.5 Tf 56 490.6099999999997 Td (Las figuras indicadas en la presente memoria tienen carácter esquemático y se) Tj ET
BT /F1 11.5 Tf 56 475.88999999999965 Td (incorporan a efectos ilustrativos de la arquitectura, del flujo de captura, del) Tj ET
BT /F1 11.5 Tf 56 461.1699999999996 Td (contraste de stock y de la generación de acciones. En una presentación definitiva,) Tj ET
BT /F1 11.5 Tf 56 446.4499999999996 Td (dichas figuras podrán acompañarse como láminas separadas numeradas de forma) Tj ET
BT /F1 11.5 Tf 56 431.72999999999956 Td (consecutiva.) Tj ET
BT /F2 13 Tf 56 413.00999999999954 Td (12. Firma del solicitante) Tj ET
0.2 w 56 392.36999999999955 m 539.28 392.36999999999955 l S
BT /F1 11.5 Tf 56 382.36999999999955 Td (En Terrassa, a 22 de junio de 2026.) Tj ET
BT /F1 11.5 Tf 56 365.6499999999995 Td (Fdo.: Antoni Nuñez Romeu) Tj ET
BT /F1 11.5 Tf 56 348.9299999999995 Td (DNI/NIF: 45858029P) Tj ET
BT /F2 13 Tf 56 330.20999999999947 Td (13. Notas para presentación en OEPM) Tj ET
0.2 w 56 309.5699999999995 m 539.28 309.5699999999995 l S
BT /F1 11.2 Tf 64 299.5699999999995 Td (" La solicitud debe presentarse como una única invención o un único concepto inventivo) Tj ET
BT /F1 11.2 Tf 64 285.23399999999947 Td ( general.) Tj ET
BT /F1 11.2 Tf 64 270.89799999999946 Td (" La descripción, reivindicaciones y dibujos deben ser coherentes entre sí.) Tj ET
BT /F1 11.2 Tf 64 256.56199999999944 Td (" La protección en patente debe apoyarse en un efecto técnico concreto y no en una mera) Tj ET
BT /F1 11.2 Tf 64 242.22599999999943 Td ( regla comercial o método abstracto.) Tj ET
BT /F1 11.2 Tf 64 227.88999999999942 Td (" Esta redacción evita formular una reivindicación independiente de "programa de) Tj ET
BT /F1 11.2 Tf 64 213.5539999999994 Td ( ordenador como tal" y se centra en un sistema y un procedimiento implementados por) Tj ET
BT /F1 11.2 Tf 64 199.2179999999994 Td ( ordenador con interacción técnica sobre captura, contraste de inventario y generación) Tj ET
BT /F1 11.2 Tf 64 184.88199999999938 Td ( de acciones.) Tj ET
BT /F1 11.2 Tf 64 170.54599999999937 Td (" Antes de presentar la solicitud conviene una búsqueda de anterioridades y una revisión) Tj ET
BT /F1 11.2 Tf 64 156.20999999999935 Td ( por agente de propiedad industrial.) Tj ET
BT /F1 9 Tf 495.28 24 Td (Pagina 6) Tj ET
endstream
endobj
11 0 obj
<< /Type /Catalog /Pages 12 0 R>>
endobj
12 0 obj
<< /Type /Pages /Kids [ 13 0 R ] /Count 1 >>
endobj
13 0 obj
<< /Type /Page /Parent 12 0 R /Contents 9 0 R /Resources <</Font <</F1 1 0 R /F2 2 0 R /F3 3 0 R >> >> /MediaBox [0 0 595.28 841.89 ] /TrimBox [0 0 595.28 841.89 ] /Rotate 0 >>
endobj
xref
0 14
0000000004 65535 f
0000000015 00000 n
0000000117 00000 n
0000000218 00000 n
0000000005 00000 f
0000000006 00000 f
0000000007 00000 f
0000000008 00000 f
0000000010 00000 f
0000000321 00000 n
0000000000 00000 f
0000004595 00000 n
0000004645 00000 n
0000004706 00000 n
trailer
<</Size 7 /ID [(9@lj²×}^Äè…Ô,ôå) (9@lj²×}^Äè…Ô,ôå) ] /Root 11 0 R >>
startxref
4905
%%EOF
+11
View File
@@ -24,5 +24,16 @@ EXPO_ACCESS_TOKEN=
# Genera una cadena aleatoria fuerte, p.ej.: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
INGEST_API_KEY=dev-ingest-key-change-me
# SMTP for password reset / forgot username emails
# Development: use Mailpit (docker) — no auth needed
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USER=
SMTP_PASS=
SMTP_FROM=noreply@farmafinder.com
# Base URL for reset links in emails (your frontend URL)
APP_URL=http://localhost:3000
# Parapharmacy API
PARAPHARMACY_API_URL=http://localhost:3002
+4 -2
View File
@@ -1,7 +1,9 @@
FROM node:24-alpine
FROM node:slim
WORKDIR /app
COPY apps/backend/package*.json ./
RUN apk add --no-cache python3 make g++ && npm ci --omit=dev
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
RUN npm install
RUN npm ci --omit=dev
COPY apps/backend/ .
COPY apps/API/ /API/
RUN mkdir -p /app/data
+65
View File
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1785151771806" clover="3.2.0">
<project timestamp="1785151771806" name="All files">
<metrics statements="56" coveredstatements="56" conditionals="71" coveredconditionals="64" methods="5" coveredmethods="5" elements="132" coveredelements="125" complexity="0" loc="56" ncloc="56" packages="1" files="1" classes="1"/>
<file name="hours.js" path="/home/f80ans0/Projects/Webs/FarmaFinder/apps/backend/src/hours.js">
<metrics statements="56" coveredstatements="56" conditionals="71" coveredconditionals="64" methods="5" coveredmethods="5"/>
<line num="15" count="1" type="stmt"/>
<line num="18" count="40" type="cond" truecount="4" falsecount="0"/>
<line num="19" count="34" type="cond" truecount="2" falsecount="0"/>
<line num="20" count="4" type="stmt"/>
<line num="21" count="4" type="stmt"/>
<line num="23" count="2" type="stmt"/>
<line num="28" count="26" type="cond" truecount="1" falsecount="1"/>
<line num="29" count="26" type="stmt"/>
<line num="30" count="26" type="cond" truecount="1" falsecount="1"/>
<line num="31" count="26" type="stmt"/>
<line num="32" count="26" type="stmt"/>
<line num="33" count="26" type="cond" truecount="3" falsecount="1"/>
<line num="34" count="26" type="cond" truecount="2" falsecount="2"/>
<line num="35" count="26" type="cond" truecount="5" falsecount="1"/>
<line num="36" count="26" type="stmt"/>
<line num="40" count="4" type="stmt"/>
<line num="41" count="11" type="stmt"/>
<line num="42" count="11" type="stmt"/>
<line num="43" count="11" type="cond" truecount="4" falsecount="0"/>
<line num="44" count="3" type="stmt"/>
<line num="47" count="1" type="stmt"/>
<line num="51" count="22" type="stmt"/>
<line num="52" count="22" type="cond" truecount="4" falsecount="0"/>
<line num="53" count="18" type="stmt"/>
<line num="54" count="48" type="stmt"/>
<line num="55" count="48" type="cond" truecount="4" falsecount="0"/>
<line num="56" count="36" type="cond" truecount="4" falsecount="0"/>
<line num="58" count="5" type="stmt"/>
<line num="62" count="18" type="stmt"/>
<line num="63" count="18" type="cond" truecount="2" falsecount="0"/>
<line num="65" count="14" type="cond" truecount="2" falsecount="0"/>
<line num="66" count="3" type="stmt"/>
<line num="69" count="11" type="stmt"/>
<line num="70" count="11" type="stmt"/>
<line num="72" count="11" type="cond" truecount="4" falsecount="0"/>
<line num="73" count="2" type="stmt"/>
<line num="76" count="9" type="stmt"/>
<line num="77" count="9" type="stmt"/>
<line num="78" count="9" type="cond" truecount="3" falsecount="1"/>
<line num="80" count="9" type="stmt"/>
<line num="84" count="9" type="stmt"/>
<line num="85" count="9" type="stmt"/>
<line num="86" count="9" type="cond" truecount="4" falsecount="0"/>
<line num="87" count="4" type="stmt"/>
<line num="88" count="4" type="stmt"/>
<line num="89" count="4" type="cond" truecount="6" falsecount="0"/>
<line num="90" count="1" type="stmt"/>
<line num="95" count="8" type="cond" truecount="2" falsecount="0"/>
<line num="96" count="2" type="cond" truecount="2" falsecount="0"/>
<line num="97" count="1" type="stmt"/>
<line num="99" count="1" type="stmt"/>
<line num="102" count="6" type="cond" truecount="2" falsecount="0"/>
<line num="103" count="2" type="stmt"/>
<line num="105" count="4" type="cond" truecount="2" falsecount="0"/>
<line num="106" count="2" type="stmt"/>
<line num="108" count="2" type="stmt"/>
</file>
</project>
</coverage>
File diff suppressed because one or more lines are too long
+224
View File
@@ -0,0 +1,224 @@
body, html {
margin:0; padding: 0;
height: 100%;
}
body {
font-family: Helvetica Neue, Helvetica, Arial;
font-size: 14px;
color:#333;
}
.small { font-size: 12px; }
*, *:after, *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
h1 { font-size: 20px; margin: 0;}
h2 { font-size: 14px; }
pre {
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
margin: 0;
padding: 0;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
a { color:#0074D9; text-decoration:none; }
a:hover { text-decoration:underline; }
.strong { font-weight: bold; }
.space-top1 { padding: 10px 0 0 0; }
.pad2y { padding: 20px 0; }
.pad1y { padding: 10px 0; }
.pad2x { padding: 0 20px; }
.pad2 { padding: 20px; }
.pad1 { padding: 10px; }
.space-left2 { padding-left:55px; }
.space-right2 { padding-right:20px; }
.center { text-align:center; }
.clearfix { display:block; }
.clearfix:after {
content:'';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.fl { float: left; }
@media only screen and (max-width:640px) {
.col3 { width:100%; max-width:100%; }
.hide-mobile { display:none!important; }
}
.quiet {
color: #7f7f7f;
color: rgba(0,0,0,0.5);
}
.quiet a { opacity: 0.7; }
.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}
div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
padding: 0;
}
table.coverage td {
margin: 0;
padding: 0;
vertical-align: top;
}
table.coverage td.line-count {
text-align: right;
padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
text-align: right;
padding-right: 10px;
min-width:20px;
}
table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 100%;
}
.missing-if-branch {
display: inline-block;
margin-right: 5px;
border-radius: 3px;
position: relative;
padding: 0 4px;
background: #333;
color: yellow;
}
.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
.coverage-summary tr { border-bottom: 1px solid #bbb; }
.keyline-all { border: 1px solid #ddd; }
.coverage-summary td, .coverage-summary th { padding: 10px; }
.coverage-summary tbody { border: 1px solid #bbb; }
.coverage-summary td { border-right: 1px solid #bbb; }
.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
.coverage-summary th.file { border-right: none !important; }
.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
.coverage-summary td.abs { text-align: right; }
.coverage-summary td.file { white-space: nowrap; }
.coverage-summary td.pic { min-width: 120px !important; }
.coverage-summary tfoot td { }
.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.status-line { height: 10px; }
/* yellow */
.cbranch-no { background: yellow !important; color: #111; }
/* dark red */
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
.low .chart { border:1px solid #C21F39 }
.highlighted,
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
background: #C21F39 !important;
}
/* medium red */
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
.low, .cline-no { background:#FCE1E5 }
/* light green */
.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
.cstat-yes { background:rgb(161,215,106) }
/* dark green */
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
.high .chart { border:1px solid rgb(77,146,33) }
/* dark yellow (gold) */
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
.medium .chart { border:1px solid #f9cd0b; }
/* light yellow */
.medium { background: #fff4c2; }
.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }
span.cline-neutral { background: #eaeaea; }
.coverage-summary td.empty {
opacity: .5;
padding-top: 4px;
padding-bottom: 4px;
line-height: 1;
color: #888;
}
.cover-fill, .cover-empty {
display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
background: white;
}
.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -48px;
}
.footer, .push {
height: 48px;
}
@@ -0,0 +1,87 @@
/* eslint-disable */
var jumpToCode = (function init() {
// Classes of code we would like to highlight in the file view
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
// Elements to highlight in the file listing view
var fileListingElements = ['td.pct.low'];
// We don't want to select elements that are direct descendants of another match
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
// Selector that finds elements on the page to which we can jump
var selector =
fileListingElements.join(', ') +
', ' +
notSelector +
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
// The NodeList of matching elements
var missingCoverageElements = document.querySelectorAll(selector);
var currentIndex;
function toggleClass(index) {
missingCoverageElements
.item(currentIndex)
.classList.remove('highlighted');
missingCoverageElements.item(index).classList.add('highlighted');
}
function makeCurrent(index) {
toggleClass(index);
currentIndex = index;
missingCoverageElements.item(index).scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
}
function goToPrevious() {
var nextIndex = 0;
if (typeof currentIndex !== 'number' || currentIndex === 0) {
nextIndex = missingCoverageElements.length - 1;
} else if (missingCoverageElements.length > 1) {
nextIndex = currentIndex - 1;
}
makeCurrent(nextIndex);
}
function goToNext() {
var nextIndex = 0;
if (
typeof currentIndex === 'number' &&
currentIndex < missingCoverageElements.length - 1
) {
nextIndex = currentIndex + 1;
}
makeCurrent(nextIndex);
}
return function jump(event) {
if (
document.getElementById('fileSearch') === document.activeElement &&
document.activeElement != null
) {
// if we're currently focused on the search input, we don't want to navigate
return;
}
switch (event.which) {
case 78: // n
case 74: // j
goToNext();
break;
case 66: // b
case 75: // k
case 80: // p
goToPrevious();
break;
}
};
})();
window.addEventListener('keydown', jumpToCode);
Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

@@ -0,0 +1,412 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for hours.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="index.html">All files</a> hours.js</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">91.3% </span>
<span class="quiet">Statements</span>
<span class='fraction'>63/69</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">90.14% </span>
<span class="quiet">Branches</span>
<span class='fraction'>64/71</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>5/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>56/56</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">40x</span>
<span class="cline-any cline-yes">34x</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-yes">26x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">22x</span>
<span class="cline-any cline-yes">22x</span>
<span class="cline-any cline-yes">18x</span>
<span class="cline-any cline-yes">48x</span>
<span class="cline-any cline-yes">48x</span>
<span class="cline-any cline-yes">36x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">5x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">18x</span>
<span class="cline-any cline-yes">18x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">14x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">6x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/**
* Compute "open now" / "always open" for a pharmacy whose opening_hours is
* stored as JSON in the { mon, tue, ..., sun } shape produced by
* apps/API/opening-hours-osm.js.
*
* 24/7 is internally represented as every day ["00:00", "24:00"]. The
* literal "24:00" is not valid HH:mm ISO; we accept it as 1440 minutes
* (00:00 of the next day) so range math works, and treat a 24h week as
* the dedicated `kind: '24h'` result.
*
* Midnight-crossing ranges (close &lt;= open) are detected by comparing
* the previous day's range when "now" is in the early hours.
*/
&nbsp;
const DAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
&nbsp;
function parse(raw) {
if (raw == null || raw === '') return null;
if (typeof raw === 'object') return raw;
try {
return JSON.parse(raw);
} catch {
return null;
}
}
&nbsp;
function toMin(hm) {
<span class="missing-if-branch" title="if path not taken" >I</span>if (hm == null) <span class="cstat-no" title="statement not covered" >return null;</span>
const parts = String(hm).split(':');
<span class="missing-if-branch" title="if path not taken" >I</span>if (parts.length !== 2) <span class="cstat-no" title="statement not covered" >return null;</span>
const h = Number(parts[0]);
const m = Number(parts[1]);
<span class="missing-if-branch" title="if path not taken" >I</span>if (!Number.isFinite(h) || !Number.isFinite(m)) <span class="cstat-no" title="statement not covered" >return null;</span>
<span class="missing-if-branch" title="if path not taken" >I</span>if (h === 24 &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >m === 0)</span> <span class="cstat-no" title="statement not covered" >return 1440;</span>
<span class="missing-if-branch" title="if path not taken" >I</span>if (h &lt; 0 || h &gt; 24 || m &lt; 0 || m &gt;= 60) <span class="cstat-no" title="statement not covered" >return null;</span>
return h * 60 + m;
}
&nbsp;
function findNextOpen(h, now) {
for (let off = 1; off &lt;= 7; off++) {
const key = DAYS[(now.getDay() + off) % 7];
const r = h[key];
if (Array.isArray(r) &amp;&amp; r.length === 2) {
return { day: key, time: r[0] };
}
}
return null;
}
&nbsp;
export function isAlwaysOpen(rawHours) {
const h = parse(rawHours);
if (!h || typeof h !== 'object') return false;
for (const d of DAYS) {
const r = h[d];
if (!Array.isArray(r) || r.length !== 2) return false;
if (r[0] !== '00:00' || r[1] !== '24:00') return false;
}
return true;
}
&nbsp;
export function isOpenNow(rawHours, now = new Date()) {
const h = parse(rawHours);
if (!h) return null;
&nbsp;
if (isAlwaysOpen(h)) {
return { isOpen: true, kind: '24h' };
}
&nbsp;
const dayKey = DAYS[now.getDay()];
const range = h[dayKey];
&nbsp;
if (!Array.isArray(range) || range.length !== 2) {
return { isOpen: false, kind: 'closed', nextOpen: findNextOpen(h, now) };
}
&nbsp;
const openM = toMin(range[0]);
const closeM = toMin(range[1]);
<span class="missing-if-branch" title="if path not taken" >I</span>if (openM == null || closeM == null) <span class="cstat-no" title="statement not covered" >return null;</span>
&nbsp;
const nowM = now.getHours() * 60 + now.getMinutes();
&nbsp;
// First: is "now" still inside yesterday's midnight-crossing range?
// (Today is irrelevant while we're still in the previous day's after-midnight tail.)
const yestKey = DAYS[(now.getDay() + 6) % 7];
const yest = h[yestKey];
if (Array.isArray(yest) &amp;&amp; yest.length === 2) {
const yOpen = toMin(yest[0]);
const yClose = toMin(yest[1]);
if (yOpen != null &amp;&amp; yClose != null &amp;&amp; yClose &lt;= yOpen &amp;&amp; nowM &lt; yClose) {
return { isOpen: true, kind: 'open', closesAt: yest[1] };
}
}
&nbsp;
// Midnight-crossing: today's range spans past 24:00.
if (closeM &lt;= openM) {
if (nowM &gt;= openM) {
return { isOpen: true, kind: 'open', closesAt: range[1] };
}
return { isOpen: false, kind: 'before-open', opensAt: range[0], nextOpen: null };
}
&nbsp;
if (nowM &lt; openM) {
return { isOpen: false, kind: 'before-open', opensAt: range[0], nextOpen: null };
}
if (nowM &gt;= closeM) {
return { isOpen: false, kind: 'after-close', nextOpen: findNextOpen(h, now) };
}
return { isOpen: true, kind: 'open', closesAt: range[1] };
}
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-07-27T11:29:31.802Z
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
</html>
@@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>All files</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">91.3% </span>
<span class="quiet">Statements</span>
<span class='fraction'>63/69</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">90.14% </span>
<span class="quiet">Branches</span>
<span class='fraction'>64/71</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>5/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>56/56</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="hours.js"><a href="hours.js.html">hours.js</a></td>
<td data-value="91.3" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 91%"></div><div class="cover-empty" style="width: 9%"></div></div>
</td>
<td data-value="91.3" class="pct high">91.3%</td>
<td data-value="69" class="abs high">63/69</td>
<td data-value="90.14" class="pct high">90.14%</td>
<td data-value="71" class="abs high">64/71</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="5" class="abs high">5/5</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="56" class="abs high">56/56</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-07-27T11:29:31.802Z
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
</html>
@@ -0,0 +1 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

+210
View File
@@ -0,0 +1,210 @@
/* eslint-disable */
var addSorting = (function() {
'use strict';
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() {
return document.querySelector('.coverage-summary');
}
// returns the thead element of the summary table
function getTableHeader() {
return getTable().querySelector('thead tr');
}
// returns the tbody element of the summary table
function getTableBody() {
return getTable().querySelector('tbody');
}
// returns the th element for nth column
function getNthColumn(n) {
return getTableHeader().querySelectorAll('th')[n];
}
function onFilterInput() {
const searchValue = document.getElementById('fileSearch').value;
const rows = document.getElementsByTagName('tbody')[0].children;
// Try to create a RegExp from the searchValue. If it fails (invalid regex),
// it will be treated as a plain text search
let searchRegex;
try {
searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive
} catch (error) {
searchRegex = null;
}
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
let isMatch = false;
if (searchRegex) {
// If a valid regex was created, use it for matching
isMatch = searchRegex.test(row.textContent);
} else {
// Otherwise, fall back to the original plain text search
isMatch = row.textContent
.toLowerCase()
.includes(searchValue.toLowerCase());
}
row.style.display = isMatch ? '' : 'none';
}
}
// loads the search box
function addSearchBox() {
var template = document.getElementById('filterTemplate');
var templateClone = template.content.cloneNode(true);
templateClone.getElementById('fileSearch').oninput = onFilterInput;
template.parentElement.appendChild(templateClone);
}
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML =
colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function(a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function(a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc
? ' sorted-desc'
: ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function() {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i = 0; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function() {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData();
addSearchBox();
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);
+146
View File
@@ -0,0 +1,146 @@
TN:
SF:src/hours.js
FN:17,parse
FN:27,toMin
FN:39,findNextOpen
FN:50,isAlwaysOpen
FN:61,isOpenNow
FNF:5
FNH:5
FNDA:40,parse
FNDA:26,toMin
FNDA:4,findNextOpen
FNDA:22,isAlwaysOpen
FNDA:18,isOpenNow
DA:15,1
DA:18,40
DA:19,34
DA:20,4
DA:21,4
DA:23,2
DA:28,26
DA:29,26
DA:30,26
DA:31,26
DA:32,26
DA:33,26
DA:34,26
DA:35,26
DA:36,26
DA:40,4
DA:41,11
DA:42,11
DA:43,11
DA:44,3
DA:47,1
DA:51,22
DA:52,22
DA:53,18
DA:54,48
DA:55,48
DA:56,36
DA:58,5
DA:62,18
DA:63,18
DA:65,14
DA:66,3
DA:69,11
DA:70,11
DA:72,11
DA:73,2
DA:76,9
DA:77,9
DA:78,9
DA:80,9
DA:84,9
DA:85,9
DA:86,9
DA:87,4
DA:88,4
DA:89,4
DA:90,1
DA:95,8
DA:96,2
DA:97,1
DA:99,1
DA:102,6
DA:103,2
DA:105,4
DA:106,2
DA:108,2
LF:56
LH:56
BRDA:18,0,0,6
BRDA:18,0,1,34
BRDA:18,1,0,40
BRDA:18,1,1,36
BRDA:19,2,0,30
BRDA:19,2,1,4
BRDA:28,3,0,0
BRDA:28,3,1,26
BRDA:30,4,0,0
BRDA:30,4,1,26
BRDA:33,5,0,0
BRDA:33,5,1,26
BRDA:33,6,0,26
BRDA:33,6,1,26
BRDA:34,7,0,0
BRDA:34,7,1,26
BRDA:34,8,0,26
BRDA:34,8,1,0
BRDA:35,9,0,0
BRDA:35,9,1,26
BRDA:35,10,0,26
BRDA:35,10,1,26
BRDA:35,10,2,26
BRDA:35,10,3,26
BRDA:43,11,0,3
BRDA:43,11,1,8
BRDA:43,12,0,11
BRDA:43,12,1,3
BRDA:52,13,0,4
BRDA:52,13,1,18
BRDA:52,14,0,22
BRDA:52,14,1,18
BRDA:55,15,0,12
BRDA:55,15,1,36
BRDA:55,16,0,48
BRDA:55,16,1,36
BRDA:56,17,0,1
BRDA:56,17,1,35
BRDA:56,18,0,36
BRDA:56,18,1,36
BRDA:61,19,0,4
BRDA:63,20,0,4
BRDA:63,20,1,14
BRDA:65,21,0,3
BRDA:65,21,1,11
BRDA:72,22,0,2
BRDA:72,22,1,9
BRDA:72,23,0,11
BRDA:72,23,1,9
BRDA:78,24,0,0
BRDA:78,24,1,9
BRDA:78,25,0,9
BRDA:78,25,1,9
BRDA:86,26,0,4
BRDA:86,26,1,5
BRDA:86,27,0,9
BRDA:86,27,1,4
BRDA:89,28,0,1
BRDA:89,28,1,3
BRDA:89,29,0,4
BRDA:89,29,1,4
BRDA:89,29,2,4
BRDA:89,29,3,2
BRDA:95,30,0,2
BRDA:95,30,1,6
BRDA:96,31,0,1
BRDA:96,31,1,1
BRDA:102,32,0,2
BRDA:102,32,1,4
BRDA:105,33,0,2
BRDA:105,33,1,2
BRF:71
BRH:64
end_of_record
+1
View File
@@ -40,6 +40,7 @@
"express-rate-limit": "^8.5.2",
"express-session": "^1.17.3",
"multer": "^2.2.0",
"nodemailer": "^6.10.1",
"pg": "^8.13.0",
"pino": "^9.4.0",
"pino-http": "^10.3.0",
+110
View File
@@ -25,9 +25,11 @@ import pinoHttp from 'pino-http';
import multer from 'multer';
import { searchMedicines, getMedicineDetails, searchOTC } from './cima-service.js';
import { runFarmaciaWebhookImport, DEFAULT_FARMACIAS_WEBHOOK, importPharmaciesFromRows } from './farmacias-webhook-import.js';
import { sendPasswordResetEmail, sendForgotUsernameEmail } from './src/email.js';
import { fetchPharmaciesExternal } from '../API/index.js';
import { validateProductionEnv } from './src/config/required-env.js';
import { isOpenNow, isAlwaysOpen } from './src/hours.js';
import crypto from 'crypto';
validateProductionEnv();
@@ -119,6 +121,7 @@ const searchLimiter = rateLimit({ windowMs: 60_000, max: 30, standardHeaders: tr
const loginLimiter = rateLimit({ windowMs: 60_000, max: 5, standardHeaders: true, legacyHeaders: false, handler: limitHandler('login') });
const registerLimiter = rateLimit({ windowMs: 60 * 60_000, max: 10, standardHeaders: true, legacyHeaders: false, handler: limitHandler('register') });
const geocodeLimiter = rateLimit({ windowMs: 60_000, max: 10, standardHeaders: true, legacyHeaders: false, handler: limitHandler('geocode') });
const forgotPasswordLimiter = rateLimit({ windowMs: 60_000, max: 3, standardHeaders: true, legacyHeaders: false, handler: limitHandler('forgot-password') });
const VAPID_PUBLIC_KEY = process.env.VAPID_PUBLIC_KEY || '';
const VAPID_PRIVATE_KEY = process.env.VAPID_PRIVATE_KEY || '';
@@ -602,6 +605,33 @@ if (!pgPool) {
)
`);
}
// Password reset tokens table
if (pgPool) {
await pgPool.query(`
CREATE TABLE IF NOT EXISTS password_reset_tokens (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
used INTEGER NOT NULL DEFAULT 0,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
)
`);
await pgPool.query(`CREATE INDEX IF NOT EXISTS idx_pwd_reset_token ON password_reset_tokens(token)`);
} else {
await dbRun(`
CREATE TABLE IF NOT EXISTS password_reset_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
token TEXT NOT NULL UNIQUE,
used INTEGER NOT NULL DEFAULT 0,
expires_at DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
)
`);
await dbRun(`CREATE INDEX IF NOT EXISTS idx_pwd_reset_token ON password_reset_tokens(token)`);
}
} catch (err) {
console.error('initDatabase failed:', err);
throw err;
@@ -1204,6 +1234,86 @@ app.post('/api/auth/logout', (req, res) => {
});
});
// Forgot username — send username to user's email
app.post('/api/auth/forgot-username', forgotPasswordLimiter, async (req, res) => {
try {
const { email } = req.body || {};
if (!email || !String(email).trim()) {
return res.status(400).json({ error: 'Email is required' });
}
const user = await userDbGet('SELECT username, email FROM users WHERE email = ?', [String(email).trim()]);
if (user) {
try {
await sendForgotUsernameEmail(user.email, user.username);
} catch (err) {
console.error('Error sending forgot-username email:', err);
}
}
res.json({ message: 'If the email exists, you will receive a message with your username.' });
} catch (error) {
console.error('Error in forgot-username:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Forgot password — generate reset token and send email
app.post('/api/auth/forgot-password', forgotPasswordLimiter, async (req, res) => {
try {
const { email } = req.body || {};
if (!email || !String(email).trim()) {
return res.status(400).json({ error: 'Email is required' });
}
const user = await userDbGet('SELECT id, username, email FROM users WHERE email = ?', [String(email).trim()]);
if (user) {
const token = crypto.randomBytes(32).toString('hex');
const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour
await userDbRun(
'INSERT INTO password_reset_tokens (user_id, token, expires_at) VALUES (?, ?, ?)',
[user.id, token, expiresAt.toISOString()]
);
try {
await sendPasswordResetEmail(user.email, user.username, token);
} catch (err) {
console.error('Error sending password reset email:', err);
}
}
res.json({ message: 'If the email exists, you will receive a password reset link.' });
} catch (error) {
console.error('Error in forgot-password:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Reset password — validate token and update password
app.post('/api/auth/reset-password', forgotPasswordLimiter, async (req, res) => {
try {
const { token, password } = req.body || {};
if (!token) return res.status(400).json({ error: 'Token is required' });
if (!password || String(password).length < 8) {
return res.status(400).json({ error: 'Password must be at least 8 characters' });
}
const row = await userDbGet(
'SELECT id, user_id, used, expires_at FROM password_reset_tokens WHERE token = ?',
[token]
);
if (!row) return res.status(400).json({ error: 'Invalid or expired token' });
if (row.used) return res.status(400).json({ error: 'Token already used' });
const expiresAt = new Date(row.expires_at);
if (isNaN(expiresAt.getTime()) || expiresAt < new Date()) return res.status(400).json({ error: 'Token has expired' });
const passwordHash = await bcrypt.hash(String(password), 10);
await userDbRun('UPDATE users SET password_hash = ? WHERE id = ?', [passwordHash, row.user_id]);
await userDbRun('UPDATE password_reset_tokens SET used = 1 WHERE id = ?', [row.id]);
res.json({ message: 'Password updated successfully.' });
} catch (error) {
console.error('Error in reset-password:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Check authentication status — reads fresh user record so profile fields stay current
app.get('/api/auth/check', async (req, res) => {
try {
+86
View File
@@ -0,0 +1,86 @@
import nodemailer from 'nodemailer';
const SMTP_HOST = process.env.SMTP_HOST || 'localhost';
const SMTP_PORT = parseInt(process.env.SMTP_PORT || '1025', 10);
const SMTP_USER = process.env.SMTP_USER || '';
const SMTP_PASS = process.env.SMTP_PASS || '';
const SMTP_FROM = process.env.SMTP_FROM || 'noreply@farmafinder.com';
const APP_URL = process.env.APP_URL || 'http://localhost:3000';
let transporter = null;
function getTransporter() {
if (transporter) return transporter;
const auth = SMTP_USER && SMTP_PASS ? { user: SMTP_USER, pass: SMTP_PASS } : undefined;
transporter = nodemailer.createTransport({
host: SMTP_HOST,
port: SMTP_PORT,
secure: SMTP_PORT === 465,
auth,
ignoreTLS: SMTP_PORT !== 465 && !SMTP_USER,
});
return transporter;
}
export async function sendPasswordResetEmail(email, username, token) {
const resetUrl = `${APP_URL}/reset-password?token=${token}`;
const html = `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="font-family: sans-serif; max-width: 480px; margin: 0 auto; padding: 24px;">
<h2>Restablecer contraseña — FarmaFinder</h2>
<p>Hola <strong>${username}</strong>,</p>
<p>Has solicitado restablecer tu contraseña. Haz clic en el siguiente enlace para crear una nueva:</p>
<p style="text-align: center; margin: 32px 0;">
<a href="${resetUrl}"
style="background: #2563eb; color: #fff; padding: 12px 24px; border-radius: 6px; text-decoration: none; display: inline-block;">
Restablecer contraseña
</a>
</p>
<p>Si no has solicitado este cambio, ignora este mensaje.</p>
<p>El enlace caduca en 1 hora.</p>
<hr style="margin-top: 32px; border: none; border-top: 1px solid #e5e7eb;">
<p style="color: #6b7280; font-size: 12px;">FarmaFinder — Encuentra tus medicamentos en farmacias cercanas</p>
</body>
</html>
`;
const text = `Restablecer contraseña — FarmaFinder\n\nHola ${username},\n\nHas solicitado restablecer tu contraseña. Abre este enlace para crear una nueva:\n${resetUrl}\n\nSi no has solicitado este cambio, ignora este mensaje.\nEl enlace caduca en 1 hora.`;
await getTransporter().sendMail({
from: SMTP_FROM,
to: email,
subject: 'Restablece tu contraseña — FarmaFinder',
text,
html,
});
}
export async function sendForgotUsernameEmail(email, username) {
const html = `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="font-family: sans-serif; max-width: 480px; margin: 0 auto; padding: 24px;">
<h2>Tu usuario — FarmaFinder</h2>
<p>Has solicitado recordar tu nombre de usuario.</p>
<p style="font-size: 20px; text-align: center; padding: 16px; background: #f3f4f6; border-radius: 6px; margin: 24px 0;">
<strong>${username}</strong>
</p>
<p>Puedes iniciar sesión con este usuario y tu contraseña.</p>
<p>Si no has solicitado este dato, ignora este mensaje.</p>
<hr style="margin-top: 32px; border: none; border-top: 1px solid #e5e7eb;">
<p style="color: #6b7280; font-size: 12px;">FarmaFinder — Encuentra tus medicamentos en farmacias cercanas</p>
</body>
</html>
`;
const text = `Tu usuario — FarmaFinder\n\nHas solicitado recordar tu nombre de usuario.\n\nTu usuario es: ${username}\n\nSi no has solicitado este dato, ignora este mensaje.`;
await getTransporter().sendMail({
from: SMTP_FROM,
to: email,
subject: 'Tu nombre de usuario — FarmaFinder',
text,
html,
});
}
-5
View File
@@ -6,8 +6,3 @@ EXPO_PUBLIC_API_URL=http://localhost:3001/api
# For production builds, update this to:
# EXPO_PUBLIC_API_URL=https://api.yourdomain.com/api
# Grafana Alloy OTLP/HTTP endpoint for Faro RUM (must be reachable from the
# device — use a LAN IP / public hostname, NOT localhost).
# Open router :4318 → srv84-macos:4318, or proxy /faro via Nginx Proxy Manager.
EXPO_PUBLIC_FARO_URL=http://grafana.hacecalor.net:4318
+1 -7
View File
@@ -24,13 +24,7 @@
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#007AFF"
},
"package": "com.farmafinder.app",
"googleServicesFile": "./google-services.json",
"config": {
"googleMaps": {
"apiKey": ""
}
}
"package": "com.farmafinder.app"
},
"plugins": [
"expo-router",
+8 -1
View File
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import MapView, { Marker, UrlTile } from 'react-native-maps';
import { useRouter } from 'expo-router';
import { getPharmacies } from '../../services/pharmacies';
import { LoadingSpinner } from '../../components/LoadingSpinner';
@@ -54,11 +54,18 @@ export default function MapScreen() {
<View style={styles.container}>
<MapView
style={styles.map}
mapType="none"
region={region}
onRegionChangeComplete={setRegion}
showsUserLocation={true}
showsMyLocationButton={true}
>
<UrlTile
urlTemplate="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maximumZ={19}
flipY={false}
tileSize={256}
/>
{pharmacies.map((pharmacy) => (
<Marker
key={pharmacy.id}
+6 -5
View File
@@ -174,9 +174,11 @@ export default function SearchScreen() {
data={results}
keyExtractor={(item) => item.nregistro}
renderItem={({ item }) => <MedicineCard medicine={item} />}
ListHeaderComponent={
<>
{products.length > 0 && (
ListEmptyComponent={
products.length === 0 ? null : undefined
}
ListFooterComponent={
products.length > 0 ? (
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: colors.text }]}>{t('search.parapharmacy')}</Text>
{products.map((product) => (
@@ -198,8 +200,7 @@ export default function SearchScreen() {
</TouchableOpacity>
))}
</View>
)}
</>
) : null
}
contentContainerStyle={[styles.list, isTablet && styles.listTablet]}
showsVerticalScrollIndicator={false}
-4
View File
@@ -9,10 +9,6 @@ import { useAuthStore } from '../store/authStore';
import { registerForPushNotifications, addNotificationListener, addNotificationResponseListener } from '../services/notifications';
import { ThemeProvider, useThemeContext } from '../components/ThemeProvider';
import { LanguageProvider, useTranslation } from '../src/i18n';
import { initFaro } from '../services/faro';
// Boot Faro RUM once, as early as possible.
initFaro();
const queryClient = new QueryClient();
+35 -11
View File
@@ -3,6 +3,7 @@ import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Linking } from 'r
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import * as Location from 'expo-location';
import MapView, { Marker, UrlTile } from 'react-native-maps';
import { getMedicine, getMedicinePharmacies } from '../../services/medicines';
import { subscribeToMedicine, unsubscribeFromMedicine } from '../../services/notifications';
import { useAuth } from '../../hooks/useAuth';
@@ -205,11 +206,38 @@ export default function MedicineDetailScreen() {
</View>
{locatedPharmacies.length > 0 && (
<View style={[styles.mapContainer, { backgroundColor: colors.surfaceVariant }]}>
<Ionicons name="map-outline" size={48} color={colors.textSecondary} />
<Text style={[styles.mapPlaceholder, { color: colors.textSecondary }]}>
{t('medicine.mapSoon')}
</Text>
<View style={styles.mapContainer}>
<MapView
style={styles.map}
mapType="none"
initialRegion={{
latitude: mapCenter.latitude,
longitude: mapCenter.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
}}
>
<UrlTile
urlTemplate="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maximumZ={19}
flipY={false}
tileSize={256}
/>
{locatedPharmacies.map((pharm) => {
const lat = getPharmacyLat(pharm);
const lon = getPharmacyLon(pharm);
if (lat == null || lon == null) return null;
return (
<Marker
key={pharm.id}
coordinate={{ latitude: lat, longitude: lon }}
title={pharm.pharmacy?.name || pharm.name}
description={pharm.pharmacy?.address || pharm.address}
onCalloutPress={() => router.push(`/pharmacy/${pharm.pharmacy_id || pharm.id}`)}
/>
);
})}
</MapView>
</View>
)}
@@ -362,13 +390,9 @@ const styles = StyleSheet.create({
height: 200,
borderRadius: borderRadius.lg,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
gap: spacing.sm,
},
mapPlaceholder: {
fontSize: 14,
fontWeight: '500',
map: {
flex: 1,
},
pharmaciesSection: {
marginTop: spacing.sm,
+8 -1
View File
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Linking } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import MapView, { Marker } from 'react-native-maps';
import MapView, { Marker, UrlTile } from 'react-native-maps';
import { getPharmacy, getPharmacyMedicines } from '../../services/pharmacies';
import { subscribeToMedicine, unsubscribeFromMedicine } from '../../services/notifications';
import { useAuth } from '../../hooks/useAuth';
@@ -125,6 +125,7 @@ export default function PharmacyDetailScreen() {
<View style={styles.mapContainer}>
<MapView
style={styles.map}
mapType="none"
initialRegion={{
latitude: pharmacy.latitude,
longitude: pharmacy.longitude,
@@ -133,6 +134,12 @@ export default function PharmacyDetailScreen() {
}}
scrollEnabled={false}
>
<UrlTile
urlTemplate="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maximumZ={19}
flipY={false}
tileSize={256}
/>
<Marker
coordinate={{
latitude: pharmacy.latitude,
-1
View File
@@ -4,7 +4,6 @@
"main": "expo-router/entry",
"dependencies": {
"@expo/vector-icons": "^15.0.2",
"@grafana/faro-react-native": "^1.3.0",
"@react-native-async-storage/async-storage": "2.2.0",
"@tanstack/react-query": "^5.101.2",
"axios": "^1.18.1",
-36
View File
@@ -1,36 +0,0 @@
import { initializeFaro } from '@grafana/faro-react-native';
let initialized = false;
/**
* Initializes Grafana Faro RUM for the React Native app.
*
* Telemetry (crashes, JS errors, console, app-start, memory/ANR vitals,
* session + screen tracking) is sent OTLP/HTTP to the Alloy collector.
*
* NOTE: `@grafana/faro-react-native` ships a native module, so it requires a
* custom dev build / EAS build (not Expo Go). Initialization is wrapped in a
* try/catch so a collector outage never crashes the app.
*/
export function initFaro(): void {
if (initialized) return;
initialized = true;
try {
const url =
process.env.EXPO_PUBLIC_FARO_URL || 'http://grafana.hacecalor.net:4318';
initializeFaro({
app: {
name: 'farmafinder-mobile',
version: '1.0.0',
environment: __DEV__ ? 'development' : 'production',
},
url,
sessionTracking: { enabled: true },
// Crash/JS error/console capture are on by default.
});
} catch (err) {
console.warn('[faro] initialization failed:', err);
}
}
+44
View File
@@ -8,6 +8,8 @@ import AlertsView from './views/AlertsView';
import ProfileView from './views/ProfileView';
import AdminView from './views/AdminView';
import LoginModal from './components/LoginModal';
import ForgotPasswordModal from './components/ForgotPasswordModal';
import ResetPasswordView from './views/ResetPasswordView';
import SavedNotifications from './components/SavedNotifications';
import BottomNav from './components/BottomNav';
import { getFaro } from './utils/faro';
@@ -17,6 +19,9 @@ function App() {
const [currentUser, setCurrentUser] = useState(null);
const [authChecked, setAuthChecked] = useState(false);
const [showLogin, setShowLogin] = useState(false);
const [showForgot, setShowForgot] = useState(false);
const [forgotMode, setForgotMode] = useState('password');
const [resetToken, setResetToken] = useState(null);
const [showSaved, setShowSaved] = useState(false);
const [badgeCount, setBadgeCount] = useState(0);
const [prescriptionSearch, setPrescriptionSearch] = useState('');
@@ -67,6 +72,13 @@ function App() {
});
};
const params = new URLSearchParams(window.location.search);
const token = params.get('token');
if (token) {
setResetToken(token);
setScreen('reset-password');
}
fetch('/api/auth/check')
.then(r => r.json())
.then(data => { if (data.authenticated) setCurrentUser(data.user); })
@@ -126,6 +138,18 @@ function App() {
setCurrentUser(prev => ({ ...prev, ...updated }));
}
function handleForgotPassword(mode) {
setForgotMode(mode);
setShowLogin(false);
setShowForgot(true);
}
function handleResetComplete() {
setResetToken(null);
setShowLogin(true);
window.history.replaceState({}, '', '/');
}
function handleAdminClick() {
setScreen('admin');
}
@@ -211,6 +235,14 @@ function App() {
/>
);
break;
case 'reset-password':
activeView = (
<ResetPasswordView
token={resetToken}
onReset={handleResetComplete}
/>
);
break;
case 'admin':
activeView = <AdminView />;
break;
@@ -243,6 +275,18 @@ function App() {
<LoginModal
onLogin={handleLogin}
onClose={() => setShowLogin(false)}
onForgotPassword={handleForgotPassword}
/>
)}
{showForgot && (
<ForgotPasswordModal
initialMode={forgotMode}
onClose={() => setShowForgot(false)}
onBackToLogin={() => {
setShowForgot(false);
setShowLogin(true);
}}
/>
)}
@@ -0,0 +1,77 @@
.forgot-box {
max-width: 380px;
}
.forgot-back {
background: none;
border: none;
color: var(--primary);
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
padding: 0;
margin-bottom: 1rem;
display: inline-block;
}
.forgot-back:hover {
opacity: 0.8;
}
.forgot-options {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-top: 0.5rem;
}
.forgot-option {
display: flex;
align-items: center;
gap: 0.85rem;
background: var(--surface-muted);
border: 1px solid var(--border);
border-radius: calc(var(--radius) - 4px);
padding: 1rem;
cursor: pointer;
text-align: left;
transition: border-color 0.15s, background 0.15s;
font-size: 0.9rem;
color: var(--text-main);
}
.forgot-option:hover {
border-color: var(--primary);
background: var(--surface);
}
.forgot-option-icon {
font-size: 1.5rem;
flex-shrink: 0;
}
.forgot-option-text {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.forgot-option-desc {
font-size: 0.8rem;
color: var(--text-muted);
font-weight: 400;
}
.forgot-sent-icon {
text-align: center;
font-size: 2.5rem;
margin-bottom: 0.75rem;
}
.forgot-sent-text {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.5;
text-align: center;
margin: 0 0 0.5rem;
}
@@ -0,0 +1,159 @@
import React, { useState, useEffect, useRef } from 'react';
import { useTranslation } from '../i18n';
import './LoginModal.css';
import './ForgotPasswordModal.css';
function ForgotPasswordModal({ onClose, onBackToLogin, initialMode }) {
const { t } = useTranslation();
const [step, setStep] = useState(initialMode ? 'email' : 'choose'); // choose | email | sent
const [mode, setMode] = useState(initialMode || ''); // username | password
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const emailRef = useRef(null);
useEffect(() => {
emailRef.current?.focus();
function handleKey(e) { if (e.key === 'Escape') onClose(); }
document.addEventListener('keydown', handleKey);
return () => document.removeEventListener('keydown', handleKey);
}, [onClose]);
function handleChoose(m) {
setMode(m);
setStep('email');
setError('');
}
async function handleSubmit(e) {
e.preventDefault();
const em = email.trim();
if (!em) return;
setLoading(true);
setError('');
try {
const endpoint = mode === 'password'
? '/api/auth/forgot-password'
: '/api/auth/forgot-username';
const res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: em }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
setError(data.error || t('forgot.error'));
return;
}
setStep('sent');
} catch {
setError(t('login.networkError'));
} finally {
setLoading(false);
}
}
function handleBack() {
if (step === 'email') setStep('choose');
else onBackToLogin();
}
return (
<div className="modal-overlay" onClick={onClose}>
<div
className="modal-box forgot-box"
onClick={e => e.stopPropagation()}
role="dialog"
aria-modal="true"
aria-labelledby="forgot-title"
>
{step === 'choose' && (
<>
<button type="button" className="forgot-back" onClick={onBackToLogin} aria-label={t('forgot.back')}>
← {t('forgot.backToLogin')}
</button>
<h2 id="forgot-title">{t('forgot.title')}</h2>
<p className="modal-sub">{t('forgot.subtitle')}</p>
<div className="forgot-options">
<button
type="button"
className="forgot-option"
onClick={() => handleChoose('password')}
>
<span className="forgot-option-icon">🔑</span>
<span className="forgot-option-text">
<strong>{t('forgot.password')}</strong>
<span className="forgot-option-desc">{t('forgot.passwordDesc')}</span>
</span>
</button>
<button
type="button"
className="forgot-option"
onClick={() => handleChoose('username')}
>
<span className="forgot-option-icon">👤</span>
<span className="forgot-option-text">
<strong>{t('forgot.username')}</strong>
<span className="forgot-option-desc">{t('forgot.usernameDesc')}</span>
</span>
</button>
</div>
</>
)}
{step === 'email' && (
<>
<button type="button" className="forgot-back" onClick={handleBack} aria-label={t('forgot.back')}>
← {t('forgot.back')}
</button>
<h2 id="forgot-title">
{mode === 'password' ? t('forgot.resetTitle') : t('forgot.usernameTitle')}
</h2>
<p className="modal-sub">
{mode === 'password' ? t('forgot.resetDesc') : t('forgot.usernameDesc2')}
</p>
<form onSubmit={handleSubmit} noValidate>
<div className="modal-field">
<label htmlFor="forgot-email">{t('forgot.email')}</label>
<input
id="forgot-email"
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
autoComplete="email"
ref={emailRef}
disabled={loading}
placeholder={t('forgot.emailPlaceholder')}
/>
</div>
{error && <p className="modal-error">{error}</p>}
<div className="modal-actions">
<button type="button" className="modal-cancel" onClick={handleBack} disabled={loading}>
{t('forgot.cancel')}
</button>
<button type="submit" className="modal-submit" disabled={loading || !email.trim()}>
{loading ? t('forgot.sending') : t('forgot.send')}
</button>
</div>
</form>
</>
)}
{step === 'sent' && (
<>
<div className="forgot-sent-icon">✅</div>
<h2 id="forgot-title">{t('forgot.sentTitle')}</h2>
<p className="forgot-sent-text">{t('forgot.sentText')}</p>
<div className="modal-actions" style={{ justifyContent: 'center' }}>
<button type="button" className="modal-submit" onClick={onClose}>
{t('forgot.close')}
</button>
</div>
</>
)}
</div>
</div>
);
}
export default ForgotPasswordModal;
@@ -171,3 +171,27 @@
opacity: 0.5;
cursor: not-allowed;
}
.forgot-links {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
margin-top: 1rem;
}
.forgot-link {
background: none;
border: none;
color: var(--text-muted);
font-size: 0.8rem;
cursor: pointer;
padding: 0.2rem 0;
text-decoration: underline;
text-underline-offset: 2px;
transition: color 0.15s;
}
.forgot-link:hover {
color: #0a0b0a;
}
+19 -1
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import { useTranslation } from '../i18n';
import './LoginModal.css';
function LoginModal({ onLogin, onClose, initialMode = 'login' }) {
function LoginModal({ onLogin, onClose, initialMode = 'login', onForgotPassword }) {
const { t } = useTranslation();
const [mode, setMode] = useState(initialMode === 'register' ? 'register' : 'login');
const [username, setUsername] = useState('');
@@ -148,6 +148,24 @@ function LoginModal({ onLogin, onClose, initialMode = 'login' }) {
</button>
</div>
</form>
{!isRegister && (
<div className="forgot-links">
<button
type="button"
className="forgot-link"
onClick={() => onForgotPassword('password')}
>
{t('forgot.forgotPassword')}
</button>
<button
type="button"
className="forgot-link"
onClick={() => onForgotPassword('username')}
>
{t('forgot.forgotUsername')}
</button>
</div>
)}
</div>
</div>
);
+39
View File
@@ -251,6 +251,45 @@ const ca = {
'profile.languageCatalan': 'Català',
'profile.languageSpanish': 'Castellà',
// ForgotPasswordModal
'forgot.title': 'Problemes per accedir?',
'forgot.subtitle': 'Tria què necessites recuperar',
'forgot.password': 'He oblidat la meva contrasenya',
'forgot.passwordDesc': 'T\'enviarem un enllaç per crear-ne una de nova',
'forgot.username': 'He oblidat el meu usuari',
'forgot.usernameDesc': 'T\'enviarem el teu nom d\'usuari',
'forgot.resetTitle': 'Restablir contrasenya',
'forgot.resetDesc': 'Introdueix el teu correu electrònic i t\'enviarem un enllaç per crear una nova contrasenya.',
'forgot.usernameTitle': 'Recuperar usuari',
'forgot.usernameDesc2': 'Introdueix el teu correu electrònic i t\'enviarem el teu nom d\'usuari.',
'forgot.email': 'Correu electrònic',
'forgot.emailPlaceholder': 'el_teu@email.com',
'forgot.send': 'Enviar',
'forgot.sending': 'Enviant...',
'forgot.cancel': 'Cancel·lar',
'forgot.back': 'Tornar',
'forgot.backToLogin': 'Tornar a iniciar sessió',
'forgot.sentTitle': 'Correu enviat',
'forgot.sentText': 'Si l\'adreça de correu existeix a la nostra base de dades, rebràs un missatge en uns minuts. Revisa la teva safata d\'entrada.',
'forgot.close': 'Tancar',
'forgot.error': 'Error en processar la sol·licitud',
'forgot.forgotPassword': 'Has oblidat la contrasenya?',
'forgot.forgotUsername': 'Has oblidat l\'usuari?',
// ResetPasswordView
'reset.title': 'Crear nova contrasenya',
'reset.subtitle': 'Introdueix la teva nova contrasenya',
'reset.newPassword': 'Nova contrasenya',
'reset.confirmPassword': 'Confirmar contrasenya',
'reset.saveBtn': 'Canviar contrasenya',
'reset.saving': 'Desant...',
'reset.passwordsDontMatch': 'Les contrasenyes no coincideixen',
'reset.successTitle': 'Contrasenya actualitzada',
'reset.successText': 'La teva contrasenya s\'ha actualitzat correctament. Ja pots iniciar sessió amb la teva nova contrasenya.',
'reset.goToLogin': 'Anar a iniciar sessió',
'reset.error': 'Error en restablir la contrasenya',
'reset.invalidToken': 'Enllaç invàlid o caducat. Sol·licita un nou restabliment de contrasenya.',
// SavedNotifications
'savedNotifications.title': '🔔 Notificacions Desades',
'savedNotifications.close': 'Tancar',
+39
View File
@@ -251,6 +251,45 @@ const es = {
'profile.languageCatalan': 'Català',
'profile.languageSpanish': 'Castellano',
// ForgotPasswordModal
'forgot.title': '¿Problemas para acceder?',
'forgot.subtitle': 'Elige qué necesitas recuperar',
'forgot.password': 'He olvidado mi contraseña',
'forgot.passwordDesc': 'Te enviaremos un enlace para crear una nueva',
'forgot.username': 'He olvidado mi usuario',
'forgot.usernameDesc': 'Te enviaremos tu nombre de usuario',
'forgot.resetTitle': 'Restablecer contraseña',
'forgot.resetDesc': 'Introduce tu correo electrónico y te enviaremos un enlace para crear una nueva contraseña.',
'forgot.usernameTitle': 'Recuperar usuario',
'forgot.usernameDesc2': 'Introduce tu correo electrónico y te enviaremos tu nombre de usuario.',
'forgot.email': 'Correo electrónico',
'forgot.emailPlaceholder': 'tu@email.com',
'forgot.send': 'Enviar',
'forgot.sending': 'Enviando...',
'forgot.cancel': 'Cancelar',
'forgot.back': 'Volver',
'forgot.backToLogin': 'Volver a iniciar sesión',
'forgot.sentTitle': 'Correo enviado',
'forgot.sentText': 'Si la dirección de correo existe en nuestra base de datos, recibirás un mensaje en unos minutos. Revisa tu bandeja de entrada.',
'forgot.close': 'Cerrar',
'forgot.error': 'Error al procesar la solicitud',
'forgot.forgotPassword': '¿Olvidaste tu contraseña?',
'forgot.forgotUsername': '¿Olvidaste tu usuario?',
// ResetPasswordView
'reset.title': 'Crear nueva contraseña',
'reset.subtitle': 'Introduce tu nueva contraseña',
'reset.newPassword': 'Nueva contraseña',
'reset.confirmPassword': 'Confirmar contraseña',
'reset.saveBtn': 'Cambiar contraseña',
'reset.saving': 'Guardando...',
'reset.passwordsDontMatch': 'Las contraseñas no coinciden',
'reset.successTitle': 'Contraseña actualizada',
'reset.successText': 'Tu contraseña se ha actualizado correctamente. Ya puedes iniciar sesión con tu nueva contraseña.',
'reset.goToLogin': 'Ir a iniciar sesión',
'reset.error': 'Error al restablecer la contraseña',
'reset.invalidToken': 'Enlace inválido o expirado. Solicita un nuevo restablecimiento de contraseña.',
// SavedNotifications
'savedNotifications.title': '🔔 Notificaciones Guardadas',
'savedNotifications.close': 'Cerrar',
@@ -0,0 +1,100 @@
.reset-container {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 1.5rem;
}
.reset-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 2rem 2.25rem 1.75rem;
width: 100%;
max-width: 360px;
box-shadow: 0 20px 60px rgba(28, 25, 23, 0.12);
}
.reset-card h2 {
margin: 0 0 0.25rem;
font-size: 1.35rem;
font-weight: 700;
color: var(--text-main);
text-align: center;
}
.reset-sub {
margin: 0 0 1.5rem;
font-size: 0.875rem;
color: var(--text-muted);
text-align: center;
}
.reset-field {
display: flex;
flex-direction: column;
gap: 0.35rem;
margin-bottom: 1rem;
}
.reset-field label {
font-size: 0.82rem;
font-weight: 600;
color: var(--text-muted);
}
.reset-field input {
padding: 0.6rem 0.85rem;
border: 1px solid var(--border);
border-radius: calc(var(--radius) - 4px);
background: var(--surface-muted);
color: var(--text-main);
font-size: 0.95rem;
outline: none;
transition: border-color 0.15s;
}
.reset-field input:focus {
border-color: var(--primary);
}
.reset-field input:disabled {
opacity: 0.6;
}
.reset-error {
font-size: 0.82rem;
color: var(--error);
margin: 0.25rem 0 0.75rem;
text-align: center;
}
.reset-btn {
width: 100%;
background: var(--primary);
border: none;
color: var(--on-primary);
padding: 0.65rem 1.35rem;
border-radius: 999px;
cursor: pointer;
font-size: 0.95rem;
font-weight: 600;
transition: opacity 0.15s;
margin-top: 0.5rem;
}
.reset-btn:hover:not(:disabled) {
opacity: 0.88;
}
.reset-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.reset-icon {
text-align: center;
font-size: 2.5rem;
margin-bottom: 0.75rem;
}
@@ -0,0 +1,109 @@
import React, { useState, useEffect } from 'react';
import { useTranslation } from '../i18n';
import './ResetPasswordView.css';
function ResetPasswordView({ token, onReset }) {
const { t } = useTranslation();
const [password, setPassword] = useState('');
const [confirm, setConfirm] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [success, setSuccess] = useState(false);
useEffect(() => {
if (!token) setError(t('reset.invalidToken'));
}, [token, t]);
async function handleSubmit(e) {
e.preventDefault();
if (password.length < 8) {
setError(t('login.passwordError'));
return;
}
if (password !== confirm) {
setError(t('reset.passwordsDontMatch'));
return;
}
setLoading(true);
setError('');
try {
const res = await fetch('/api/auth/reset-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, password }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
setError(data.error || t('reset.error'));
return;
}
setSuccess(true);
} catch {
setError(t('login.networkError'));
} finally {
setLoading(false);
}
}
if (success) {
return (
<div className="reset-container">
<div className="reset-card">
<div className="reset-icon">✅</div>
<h2>{t('reset.successTitle')}</h2>
<p>{t('reset.successText')}</p>
<button type="button" className="reset-btn" onClick={onReset}>
{t('reset.goToLogin')}
</button>
</div>
</div>
);
}
return (
<div className="reset-container">
<div className="reset-card">
<h2>{t('reset.title')}</h2>
<p className="reset-sub">{t('reset.subtitle')}</p>
<form onSubmit={handleSubmit} noValidate>
<div className="reset-field">
<label htmlFor="reset-password">{t('reset.newPassword')}</label>
<input
id="reset-password"
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
autoComplete="new-password"
disabled={loading}
minLength={8}
placeholder="········"
/>
</div>
<div className="reset-field">
<label htmlFor="reset-confirm">{t('reset.confirmPassword')}</label>
<input
id="reset-confirm"
type="password"
value={confirm}
onChange={e => setConfirm(e.target.value)}
autoComplete="new-password"
disabled={loading}
minLength={8}
placeholder="········"
/>
</div>
{error && <p className="reset-error">{error}</p>}
<button
type="submit"
className="reset-btn"
disabled={loading || !password || !confirm}
>
{loading ? t('reset.saving') : t('reset.saveBtn')}
</button>
</form>
</div>
</div>
);
}
export default ResetPasswordView;
+45
View File
@@ -2,6 +2,11 @@ services:
redis:
image: redis:alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- internal
@@ -28,6 +33,12 @@ services:
context: .
dockerfile: apps/backend/Dockerfile
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/healthz', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
env_file:
- ./apps/backend/.env
environment:
@@ -68,6 +79,12 @@ services:
VITE_FARO_ENV: ${VITE_FARO_ENV:-production}
VITE_FARO_APP_VERSION: ${VITE_FARO_APP_VERSION:-1.0.0}
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO", "/dev/null", "http://localhost:80/"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
ports:
- "4000:80"
depends_on:
@@ -80,6 +97,11 @@ services:
redis-exporter:
image: oliver006/redis_exporter:latest
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO", "/dev/null", "http://localhost:9121/metrics"]
interval: 15s
timeout: 5s
retries: 3
environment:
REDIS_ADDR: redis://redis:6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
@@ -91,6 +113,11 @@ services:
postgres-exporter:
image: prometheuscommunity/postgres-exporter:latest
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO", "/dev/null", "http://localhost:9187/metrics"]
interval: 15s
timeout: 5s
retries: 3
environment:
DATA_SOURCE_NAME: postgresql://farmafinder:${PG_PASSWORD:?PG_PASSWORD must be set}@postgres:5432/farmafinder?sslmode=disable
depends_on:
@@ -125,10 +152,28 @@ services:
networks:
- internal
# --- Mailpit — SMTP server for development (captures all outgoing emails) ---
# Web UI: http://localhost:8025
# SMTP: smtp://localhost:1025 (no auth)
mailpit:
image: axllent/mailpit:latest
restart: unless-stopped
ports:
- "8025:8025" # Web UI
- "1025:1025" # SMTP
networks:
- internal
# --- MongoDB for Parapharmacy ---
mongodb:
image: mongo:7
restart: unless-stopped
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
volumes:
- mongodb_data:/data/db
networks:
+481
View File
@@ -0,0 +1,481 @@
# Plan — Horarios de farmacias, filtro "Abiertas Ahora" y farmacias 24h
> **TL;DR**
> - **El parser OSM ya existe** (`apps/API/opening-hours-osm.js`) y cubre `24/7`, rangos, splits, wrap-around y días cerrados. Su output (`["00:00", "24:00"]` para 24/7) ya entra en BD; lo que falta es **interpretar bien `24:00`**, centralizar el cálculo "abierto/24h" en el backend, exponer un endpoint "isOpenNow" y añadir el filtro en la UI.
> - Tres pasos incrementales, cada uno verificable de forma aislada. Ninguno rompe lo que ya hay: el campo `opening_hours` se conserva como JSON string y el admin ya lo edita.
> - **TDD obligatorio** (red-green-refactor, ≥80% cobertura en lo que se añada) por la regla global de testing.
---
## Estado actual (lo que ya tenemos)
| Pieza | Ubicación | Estado |
|-------|-----------|--------|
| Parser OSM → JSON `{day:[open,close] \| null}` | `apps/API/opening-hours-osm.js` | Hecho + tests en `apps/backend/__tests__/opening-hours-osm.test.js` |
| Almacenamiento `pharmacies.opening_hours TEXT` | `apps/backend/server.js` (`initDatabase`, `serializeOpeningHours`) | Hecho (PG + SQLite con migración) |
| Ingestión desde OSM, n8n webhook y URL openData | `apps/API/*`, `apps/backend/farmacias-webhook-import.js` | Hecho — los tres caminos ya parsean horarios |
| `getOpenStatus()` frontend | `apps/frontend/src/utils/hours.js` | Parcial: NO trata `24:00` como 24h (lo trata como "medianoche", farmacia cerrada); usa idioma hardcoded (es) en labels |
| Editor de horarios en admin | `apps/frontend/src/components/admin/PharmacyManagement.jsx` | Hecho (filas Lun–Dom, open/close, toggle "Cerrado") |
| Badge "Abierto / Cerrado" en lista | `apps/frontend/src/components/PharmacyList.jsx` | Hecho vía `getOpenStatus()` |
| Filtro "Abiertas Ahora" | — | **No existe** |
| Etiqueta "24h" en lista/mapa | — | **No existe** |
| Endpoint que devuelva estado calculado en servidor | — | **No existe** (todo se calcula en cliente) |
**Diagnóstico clave:**
1. `parseOsmOpeningHours('24/7')` produce `["00:00", "24:00"]` para todos los días. `getOpenStatus()` en frontend llama `parseHM("24:00")` → `24*60+0 = 1440`. Como `nowMins < 1440` casi siempre es `true`, nunca se evalúa el caso 24h. Necesita un caso explícito.
2. `getOpenStatus()` calcula "ahora" en el navegador. Si el usuario cambia de zona horaria o el servidor de test no está en hora local, el resultado puede no coincidir con el esperado.
3. `opening_hours` se devuelve crudo en `/api/medicines/:nregistro/pharmacies` y `/api/pharmacies`. El frontend debe parsearlo cada vez (ya lo hace, pero sin tests frontend).
---
## Enfoque general
1. **Mover el cálculo de "está abierta / es 24h" al backend**, exponerlo en un único helper puro y testable. Devolver ese dato precalculado en los endpoints públicos para que el frontend no tenga que duplicar la lógica ni parchear el caso `24:00`.
2. **Filtrar en cliente** (no en servidor): el dataset de farmacias por búsqueda es pequeño (decenas, no miles), y el filtro debe combinarse con "ordenar por distancia". Mantener la lógica en un solo reducer/hook.
3. **24h como estado derivado** del JSON, no como columna nueva: si todos los días son `["00:00", "24:00"]`, la farmacia es 24h. Evita migración, evita sincronización.
4. **i18n primero**: mover los strings hardcoded de `hours.js` y `PharmacyList.jsx` a los locales (`es.js`, `ca.js`).
---
## Paso 1 — Helper de estado abierto/24h en el backend (TDD)
> **Objetivo:** un solo módulo `apps/backend/src/hours.js` con `isOpenNow(rawHours, now)` y `isAlwaysOpen(rawHours)` que sea la fuente de verdad. Tests Jest con casos explícitos para `24:00`, medianoche cruzando, splits (no usados en almacenamiento, pero defensivo), días cerrados.
### 1.1 Tests primero (RED)
**`apps/backend/__tests__/hours.test.js`** (nuevo)
```
describe('isOpenNow')
✓ returns null when raw is null/empty
✓ returns null when JSON is malformed
✓ open window covers now → { isOpen: true, kind: 'open', closesAt: '21:00' }
✓ before opening today → { isOpen: false, kind: 'before-open', opensAt: '09:00' }
✓ after closing today but opens tomorrow → { isOpen: false, kind: 'after-close', nextOpen: { day: 'mon', time: '09:00' } }
✓ closed all week → { isOpen: false, kind: 'closed', nextOpen: null }
✓ 24/7 (["00:00","24:00"]) → { isOpen: true, kind: '24h' }
✓ 24:00 close at 23:59:59 → open; at 24:00:00 → close, nextOpen: tomorrow
✓ midnight-crossing range: mon ["22:00","02:00"] — at 01:00 monday → open (special-case: subtract 1 day from now, check sunday range)
— implement: if closeMins <= openMins, treat the range as [open, close+24h]; check nowMins against that
✓ all days closed → kind 'closed'
describe('isAlwaysOpen')
✓ all 7 days ["00:00","24:00"] → true
✓ one day different → false
✓ one day null → false
✓ null input → false
```
**Aceptación:** `npm test --workspace=farma-clic-backend` corre los nuevos tests y todos pasan **antes** de tocar otros archivos.
### 1.2 Implementación (GREEN)
**`apps/backend/src/hours.js`** (nuevo)
```js
const DAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
function parse(raw) {
if (!raw) return null;
if (typeof raw === 'object') return raw;
try { return JSON.parse(raw); } catch { return null; }
}
function toMin(hm) {
// accept "24:00" as 1440 only when minutes == 0
const [h, m] = String(hm).split(':').map(Number);
if (!Number.isFinite(h) || !Number.isFinite(m)) return null;
if (h === 24 && m === 0) return 1440;
if (h < 0 || h > 24 || m < 0 || m >= 60) return null;
return h * 60 + m;
}
export function isAlwaysOpen(rawHours) {
const h = parse(rawHours);
if (!h) return false;
for (const d of DAYS) {
const r = h[d];
if (!Array.isArray(r) || r.length !== 2) return false;
if (r[0] !== '00:00' || r[1] !== '24:00') return false;
}
return true;
}
export function isOpenNow(rawHours, now = new Date()) {
const h = parse(rawHours);
if (!h) return null;
if (isAlwaysOpen(h)) {
return { isOpen: true, kind: '24h' };
}
const day = DAYS[now.getDay()];
const range = h[day];
if (!Array.isArray(range) || range.length !== 2) {
return { isOpen: false, kind: 'closed', nextOpen: findNextOpen(h, now) };
}
const openM = toMin(range[0]);
const closeM = toMin(range[1]);
if (openM == null || closeM == null) return null;
const nowM = now.getHours() * 60 + now.getMinutes();
// Midnight-crossing: close <= open means the range spans past 24:00.
if (closeM <= openM) {
if (nowM >= openM) return { isOpen: true, kind: 'open', closesAt: range[1] };
// before open today — maybe it's open from yesterday's range
const yestKey = DAYS[(now.getDay() + 6) % 7];
const yest = h[yestKey];
if (Array.isArray(yest)) {
const yOpen = toMin(yest[0]);
const yClose = toMin(yest[1]);
if (yOpen != null && yClose != null && yClose <= yOpen && nowM < yClose) {
return { isOpen: true, kind: 'open', closesAt: yest[1] };
}
}
return { isOpen: false, kind: 'before-open', opensAt: range[0], nextOpen: null };
}
if (nowM < openM) {
return { isOpen: false, kind: 'before-open', opensAt: range[0], nextOpen: null };
}
if (nowM >= closeM) {
return { isOpen: false, kind: 'after-close', nextOpen: findNextOpen(h, now) };
}
return { isOpen: true, kind: 'open', closesAt: range[1] };
}
function findNextOpen(h, now) {
for (let off = 1; off <= 7; off++) {
const key = DAYS[(now.getDay() + off) % 7];
const r = h[key];
if (Array.isArray(r) && r.length === 2) {
return { day: key, time: r[0] };
}
}
return null;
}
```
### 1.3 Refactor (IMPROVE)
- Extraer `findNextOpen` como `export` y reusarlo en `apps/frontend/src/utils/hours.js` (vía copia local por ahora — son proyectos independientes). No mover al `apps/API/` para no introducir regresiones en el parser OSM, que ya está en producción.
- Añadir `coverage` config al `jest.config.js` para exigir 100% en `src/hours.js` (es un módulo puro, sin dependencias).
**Verificación (regla de evidence):**
```bash
npm test --workspace=farma-clic-backend -- --testPathPattern=hours
# Esperado: 10/10 tests pasados, coverage del nuevo módulo = 100%
```
---
## Paso 2 — Exponer el estado calculado en los endpoints públicos
> **Objetivo:** que `/api/medicines/:nregistro/pharmacies` y `/api/pharmacies` devuelvan `is_open` y `is_24h` precalculados, basados en la hora del **servidor**. Esto fija una única fuente de verdad y elimina el bug del frontend con `24:00`.
### 2.1 Tests primero (RED)
**Ampliar `apps/backend/__tests__/server.test.js`** (o crear `pharmacy-hours-endpoint.test.js`):
```
GET /api/medicines/:nregistro/pharmacies
✓ when DB has pharmacy with opening_hours = 24/7 JSON
→ response[0].is_open === true, is_24h === true
✓ when DB has pharmacy with closed day
→ response[0].is_open === false, is_24h === false
✓ when DB has pharmacy with null opening_hours
→ response[0].is_open === null, is_24h === false
```
Mockear `userDbAll` o sembrar una BD en memoria antes de cada test (ya se hace en `server.test.js` — seguir el mismo patrón).
### 2.2 Implementación (GREEN)
En `apps/backend/server.js`, en ambos endpoints (`/api/medicines/:id/pharmacies` y `/api/products/:source/:id/pharmacies` y `/api/pharmacies`), mapear las filas para añadir dos campos:
```js
import { isOpenNow, isAlwaysOpen } from './src/hours.js';
// tras obtener `pharmacies`:
const enriched = pharmacies.map(p => {
let isOpen = null;
let is24h = false;
if (p.opening_hours) {
const status = isOpenNow(p.opening_hours);
isOpen = status ? status.isOpen : null;
is24h = isAlwaysOpen(p.opening_hours);
}
return { ...p, is_open: isOpen, is_24h: is24h };
});
res.json(enriched);
```
Crear un helper local `enrichPharmacy(row)` para no duplicarlo tres veces.
### 2.3 Refactor (IMPROVE)
- Si el rendimiento fuera problema (no lo es con <1000 filas), cachear `isOpenNow` con un TTL de 60s por minuto actual. Por ahora se calcula en cada request; dejar un comentario `// TODO(cache): si el dataset crece, cache por minuto`.
**Verificación:**
```bash
npm test --workspace=farma-clic-backend
# Esperado: tests existentes verdes + los nuevos verdes
# Manual: curl a /api/medicines/<nregistro>/pharmacies y verificar is_open en JSON
```
---
## Paso 3 — Soporte nativo de farmacias 24h en admin y selector de horarios
> **Objetivo:** que el admin pueda marcar una farmacia como "24h" con un toggle que rellene los 7 días con `00:00–24:00`, y que el editor manual respete el shape exacto del parser OSM.
### 3.1 Tests primero (RED)
**`apps/frontend/src/components/admin/PharmacyManagement.test.jsx`** (nuevo, con `@testing-library/react` si está disponible, si no: tests de unidad sobre los helpers puros)
Tests del helper puro `hoursToDraft(raw)` y `draftToHours(draft)` (extraídos de `PharmacyManagement.jsx`):
```
hoursToDraft
✓ 24/7 JSON → all 7 days with closed: false, open: '00:00', close: '24:00'
✓ normal hours → mirror
✓ null → all days with closed: true, open: '09:00', close: '21:00' (default)
draftToHours
✓ 24/7 draft (all days closed:false, 00:00, 24:00) → all 7 days ["00:00","24:00"]
✓ mixed → matches input
✓ all closed → null
```
### 3.2 Refactor del admin (IMPROVE para testabilidad)
1. Mover `emptyHoursDraft`, `hoursToDraft`, `draftToHours` a `apps/frontend/src/utils/hours.js` (ya existe) y exportarlos.
2. En `PharmacyManagement.jsx`, importar desde el helper. Esto los hace testables sin renderizar el componente.
3. Añadir **toggle "24 horas"** encima del fieldset de horarios. Estado: `is24h`. Cuando se active, poblar `hoursDraft` con `00:00/24:00` en todos los días y `closed: false`. Cuando se desactive, volver a `emptyHoursDraft()` (pregunte `confirm()` antes de descartar).
4. CSS: el toggle debe ser visible y mostrar el estado actual. Estilo coherente con el resto del admin (`.hours-row--always-open`).
### 3.3 Verificación
```bash
npm test --workspace=farma-clic-frontend
# Esperado: tests verdes del helper + sin regresión en PharmacyList
# Manual: crear farmacia 24h desde admin → ver en /api/pharmacies que is_24h=true
```
---
## Paso 4 — Filtro "Abiertas Ahora" en la vista pública
> **Objetivo:** un toggle justo al lado del botón "Ordenar por distancia". Cuando esté activo, la lista y el mapa solo muestran farmacias con `is_open === true` (o `is_24h === true`). Si una farmacia no tiene horarios, se muestra siempre (no se penaliza al admin por no haber editado).
### 4.1 Tests primero (RED)
**`apps/frontend/src/views/PublicView.test.jsx`** (nuevo si no existe) o tests sobre un hook puro extraído.
Refactor sugerido: extraer un hook `usePharmacyFilters(pharmacies, { sortByDistance, userPosition, openNow })` que devuelva la lista filtrada y ordenada. Eso permite testearlo sin renderizar.
```
usePharmacyFilters
✓ openNow=false → todas las farmacias
✓ openNow=true → solo las con is_open === true
✓ openNow=true + pharmacy con opening_hours=null → incluida (no se filtra)
✓ openNow=true + is_24h=true → incluida
✓ sortByDistance=true → orden por km ascendente
✓ sortByDistance=true sin userPosition → orden estable (no rompe)
✓ sortByDistance=true + pharmacy sin lat/lng → al final
```
### 4.2 Implementación (GREEN)
**`apps/frontend/src/views/PublicView.jsx`**
1. Nuevo state: `const [openNow, setOpenNow] = useState(false);`
2. Nuevo botón en `.pharmacy-controls` (donde está "Ordenar por distancia"):
```jsx
<button
className={`open-now-toggle ${openNow ? 'active' : ''}`}
onClick={() => setOpenNow(o => !o)}
aria-pressed={openNow}
>
{openNow ? '🟢 Abiertas ahora' : '⏱ Mostrar solo abiertas ahora'}
</button>
```
3. Mover la lógica de `displayedPharmacies` al nuevo hook `usePharmacyFilters(pharmacies, { sortByDistance, userPosition, openNow })`.
4. Pasar `displayedPharmacies` a `<PharmacyMap />` y `<PharmacyList />` igual que ahora.
5. Si el filtro deja 0 resultados: mostrar mensaje "Ninguna farmacia abierta ahora — desactiva el filtro para ver todas".
### 4.3 Visualización del badge 24h
En `apps/frontend/src/components/PharmacyList.jsx`, dentro de `PharmacyCard`, añadir (cuando `pharmacy.is_24h === true`):
```jsx
<span className="pharmacy-badge pharmacy-badge--24h" aria-label="Abierta 24 horas">24h</span>
```
al lado del nombre, antes de la distancia.
En el popup de `PharmacyMap.jsx` (`apps/frontend/src/components/PharmacyMap.jsx`):
```jsx
{pharmacy.is_24h && <span className="map-badge-24h">24h</span>}
```
### 4.4 Verificación
```bash
npm test --workspace=farma-clic-frontend
# Manual:
# 1. Crear 2 farmacias: una 9-14h (cerrada ahora si son las 17h), otra 24h
# 2. Buscar un medicamento enlazado a las dos
# 3. Pulsar "Abiertas ahora" → solo aparece la 24h
# 4. Desactivar filtro → aparecen las dos
```
---
## Paso 5 — i18n de los labels de horario
> **Objetivo:** mover los strings hardcoded de `apps/frontend/src/utils/hours.js` ("Abierto · Cierra a las X", "Cerrado · Abre mañana…") a los locales `es.js` y `ca.js`. Idem para el nuevo "Abiertas ahora".
### 5.1 Tests / Tareas
1. Añadir claves en `apps/frontend/src/i18n/locales/es.js`:
```js
'pharmacy.openNow': 'Abierto · Cierra a las {{time}}',
'pharmacy.closedAllDay': 'Cerrado',
'pharmacy.opensAt': 'Cerrado · Abre a las {{time}}',
'pharmacy.opensTomorrow': 'Cerrado · Abre mañana a las {{time}}',
'pharmacy.opensDay': 'Cerrado · Abre el {{day}} a las {{time}}',
'pharmacy.alwaysOpen': 'Abierto 24h',
'pharmacy.filterOpenNow': 'Mostrar solo abiertas ahora',
'pharmacy.filterOpenNowActive': 'Solo abiertas ahora',
'pharmacy.badge24h': '24h',
'pharmacy.filterNoResults': 'Ninguna farmacia abierta ahora. Desactiva el filtro para ver todas.'
```
2. Replicar en `ca.js` con las traducciones catalanas correspondientes.
3. Refactor de `getOpenStatus()` para que devuelva `{ status, labelKey, labelParams }` en vez de strings listos, y que `PharmacyList.jsx` los pase por `t()`. Mantener compatibilidad: si no se puede traducir, devolver el string por defecto (defensivo).
### 5.2 Verificación
```bash
npm test --workspace=farma-clic-frontend
# Manual: cambiar idioma a catalán (ca) y verificar badges
```
---
## Paso 6 — Verificación end-to-end (cierre del plan)
> Antes de declarar el plan completo, ejecutar el set de verificaciones del repo (regla "evidence-based completion" — sin evidencia, no se afirma éxito).
### 6.1 Backend
```bash
# Linter
cd apps/backend && npx eslint .
# Tests
npm test --workspace=farma-clic-backend
# Cobertura ≥ 80% en modules nuevos:
# apps/backend/src/hours.js → 100%
# apps/backend/__tests__/hours.test.js → 100%
# endpoints pharmacy hours tests → ≥ 80%
```
### 6.2 Frontend
```bash
# Linter
cd apps/frontend && npx eslint .
# Tests
npm test --workspace=farma-clic-frontend
# Cobertura ≥ 80% en:
# apps/frontend/src/utils/hours.js (tras mover helpers de admin) → 100%
# apps/frontend/src/views/PublicView.jsx → ≥ 80%
# apps/frontend/src/components/PharmacyList.jsx → ≥ 80%
# apps/frontend/src/components/admin/PharmacyManagement.jsx → ≥ 80%
```
### 6.3 Integración real (Docker)
```bash
docker compose up --build -d
docker compose exec backend npm run seed # crea 3 farmacias: 24h, 9-21h, fines de semana
curl -s http://localhost:3001/api/pharmacies | jq '.[0] | {name, is_open, is_24h}'
# Esperado: 24h farmacia con is_open=true, is_24h=true
```
### 6.4 E2E manual (smoke)
1. Abrir `http://localhost:4000`, buscar "ibuprofeno", seleccionar.
2. Verificar: las 3 farmacias aparecen con su badge de estado correcto.
3. Pulsar "Mostrar solo abiertas ahora" → solo las abiertas.
4. Pulsar "Ordenar por distancia" → reordena, badge 24h visible.
5. Abrir el popup del mapa de la 24h → etiqueta "24h" visible.
### 6.5 Regresión
- `apps/backend/__tests__/opening-hours-osm.test.js` → sigue verde (no tocamos el parser).
- `apps/backend/__tests__/server.test.js` → sigue verde; los nuevos endpoints se añaden sin romper los existentes.
- `apps/frontend/src/components/PharmacyList.jsx` actual: si `is_24h` es `undefined` (respuesta legacy de la BD sin enriquecer), debe renderizarse igual que antes (compatibilidad).
---
## Orden de implementación recomendado
| Paso | Tiempo estimado | Bloquea |
|------|----------------|---------|
| 1. Helper backend `src/hours.js` (TDD) | 1.5 h | Pasos 2 y 4 |
| 2. Enriquecer endpoints públicos (TDD) | 1 h | Paso 4 (frontend necesita el campo) |
| 3. Admin 24h (refactor + test) | 1 h | — (paralelo a 4) |
| 4. Filtro "Abiertas ahora" + badge 24h (TDD) | 2 h | — |
| 5. i18n (es + ca) | 0.5 h | — |
| 6. Verificación + cobertura | 0.5 h | Cierre |
| **Total** | **~6.5 h** | |
Pasos 1 y 3 pueden hacerse en paralelo. Paso 5 puede entramparse con 4.
---
## Riesgos y mitigaciones
| Riesgo | Mitigación |
|--------|-----------|
| `parseOsmOpeningHours` cambia en producción y rompe el shape | Tests existentes en `opening-hours-osm.test.js` ya cubren los casos. No tocar. |
| Hora del servidor difiere del navegador del usuario | Devolver `server_now` en la respuesta (`{ is_open, server_now, server_tz }`) para que la UI pueda mostrar "estado según la hora del servidor". Si en el futuro se quiere client-side, aceptar `?at=<iso>` y calcular contra ese timestamp. |
| Performance: 1000 farmacias × 7 días de parseo por request | Trivial. Si crece, cachear por minuto (ver 2.3). |
| `24:00` no es `HH:mm` válido ISO y rompe parsers terceros | Mantener el shape interno pero, al exportar, escribir un campo derivado `is_24h` (booleano) en el JSON público. El shape interno sigue siendo el mismo. |
| i18n incompleta en ca.js | Mantener strings en `es.js` como fallback. Si una clave falta en `ca.js`, el `useTranslation` ya cae al idioma por defecto. |
---
## Archivos a crear / modificar (resumen)
**Crear:**
- `apps/backend/src/hours.js`
- `apps/backend/__tests__/hours.test.js`
- `apps/backend/__tests__/pharmacy-hours-endpoint.test.js`
- `apps/frontend/src/views/PublicView.test.jsx` (o `usePharmacyFilters.test.js` si se extrae el hook)
- `apps/frontend/src/components/admin/PharmacyManagement.test.jsx` (si testing-library está disponible; si no, tests sobre los helpers puros)
**Modificar:**
- `apps/backend/server.js` — enriquecer `pharmacies` en 3 endpoints; importar `src/hours.js`
- `apps/frontend/src/views/PublicView.jsx` — state `openNow`, hook de filtros, botón en `.pharmacy-controls`
- `apps/frontend/src/components/PharmacyList.jsx` — badge 24h, pasar por i18n
- `apps/frontend/src/components/PharmacyMap.jsx` — badge 24h en popup
- `apps/frontend/src/components/admin/PharmacyManagement.jsx` — toggle 24h, importar helpers
- `apps/frontend/src/utils/hours.js` — mover `emptyHoursDraft`/`hoursToDraft`/`draftToHours` desde admin; añadir `isAlwaysOpen`; cambiar `getOpenStatus` para devolver labelKey/params
- `apps/frontend/src/i18n/locales/es.js` — nuevas claves
- `apps/frontend/src/i18n/locales/ca.js` — nuevas claves
**No tocar:**
- `apps/API/opening-hours-osm.js` (parser OSM en producción)
- `apps/backend/farmacias-webhook-import.js` (ya parsea correctamente)
---
## Preguntas abiertas (a resolver antes de implementar)
1. **¿Soportar farmacias que cruzan medianoche?** (ej. `mon ["22:00","02:00"]`). El shape actual lo permite pero `getOpenStatus` actual no lo trata. **Propuesta:** sí, implementar el caso en el helper del backend (tests ya incluidos en 1.1).
2. **¿Borrar `getOpenStatus` del frontend o mantenerlo como fallback?** **Propuesta:** mantenerlo, pero hacer que la UI use `pharmacy.is_open` y `pharmacy.is_24h` cuando estén presentes (del backend) y solo caiga al cálculo local si faltan. Sin duplicación de lógica.
3. **¿Mostrar farmacia sin horarios cuando el filtro "abiertas ahora" está activo?** **Propuesta:** sí, mostrarlas (asumimos que no sabemos = no filtramos). Documentarlo en la UI.
4. **¿Refrescar el estado cada minuto?** (por si la lista queda abierta mucho tiempo y cambia el estado). **Propuesta:** no por ahora; si la lista se refresca al cambiar de medicamento, basta. Si se observa uso prolongado sin refresco, añadir `setInterval` de 60s para recalcular.
Cuando confirmes el plan (o pidas ajustes) lo paso a implementación siguiendo TDD estricto.
+9 -161
View File
@@ -54,6 +54,7 @@
"express-rate-limit": "^8.5.2",
"express-session": "^1.17.3",
"multer": "^2.2.0",
"nodemailer": "^6.10.1",
"pg": "^8.13.0",
"pino": "^9.4.0",
"pino-http": "^10.3.0",
@@ -138,7 +139,6 @@
"version": "1.0.0",
"dependencies": {
"@expo/vector-icons": "^15.0.2",
"@grafana/faro-react-native": "^1.3.0",
"@react-native-async-storage/async-storage": "2.2.0",
"@tanstack/react-query": "^5.101.2",
"axios": "^1.18.1",
@@ -403,158 +403,6 @@
"react-native": "*"
}
},
"apps/frontend-mobile/node_modules/@grafana/faro-core": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/@grafana/faro-core/-/faro-core-2.8.2.tgz",
"integrity": "sha512-63C6+N/P9/ySUMaGut8yVb1DkPuHS5I/lfRm97+aDXFj3+8pxRT/QzyXNe0r6KMU5O95wC9FLVGxG9ZvkwEhJQ==",
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/otlp-transformer": "^0.219.0"
}
},
"apps/frontend-mobile/node_modules/@grafana/faro-react-native": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.3.0.tgz",
"integrity": "sha512-KeohD3S2xmiukL5YW3mOGDqvFiwpD9IzJ2EtZ50ktEsnA+6EMt2AtJ8RzJfAamGTYqemBpDiw51rqB4ID+C2xg==",
"license": "Apache-2.0",
"dependencies": {
"@grafana/faro-core": "^2.7.0",
"@react-native-async-storage/async-storage": "^1.21.0",
"react-native-device-info": "^11.1.0"
},
"peerDependencies": {
"@react-navigation/native": ">=6.0.0",
"react": ">=18.0.0",
"react-native": ">=0.70.0",
"react-native-mmkv": ">=2.0.0"
},
"peerDependenciesMeta": {
"@react-navigation/native": {
"optional": true
},
"react-native-mmkv": {
"optional": true
}
}
},
"apps/frontend-mobile/node_modules/@grafana/faro-react-native/node_modules/@react-native-async-storage/async-storage": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.24.0.tgz",
"integrity": "sha512-W4/vbwUOYOjco0x3toB8QCr7EjIP6nE9G7o8PMguvvjYT5Awg09lyV4enACRx4s++PPulBiBSjL0KTFx2u0Z/g==",
"dependencies": {
"merge-options": "^3.0.4"
},
"peerDependencies": {
"react-native": "^0.0.0-0 || >=0.60 <1.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/api-logs": {
"version": "0.219.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz",
"integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==",
"dependencies": {
"@opentelemetry/api": "^1.3.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/core": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz",
"integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==",
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.0.0 <1.10.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/otlp-transformer": {
"version": "0.219.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.219.0.tgz",
"integrity": "sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==",
"dependencies": {
"@opentelemetry/api-logs": "0.219.0",
"@opentelemetry/core": "2.8.0",
"@opentelemetry/resources": "2.8.0",
"@opentelemetry/sdk-logs": "0.219.0",
"@opentelemetry/sdk-metrics": "2.8.0",
"@opentelemetry/sdk-trace-base": "2.8.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": "^1.3.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/resources": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz",
"integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==",
"dependencies": {
"@opentelemetry/core": "2.8.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/sdk-logs": {
"version": "0.219.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz",
"integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==",
"dependencies": {
"@opentelemetry/api-logs": "0.219.0",
"@opentelemetry/core": "2.8.0",
"@opentelemetry/resources": "2.8.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.4.0 <1.10.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/sdk-metrics": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.8.0.tgz",
"integrity": "sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==",
"dependencies": {
"@opentelemetry/core": "2.8.0",
"@opentelemetry/resources": "2.8.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.9.0 <1.10.0"
}
},
"apps/frontend-mobile/node_modules/@opentelemetry/sdk-trace-base": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz",
"integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==",
"dependencies": {
"@opentelemetry/core": "2.8.0",
"@opentelemetry/resources": "2.8.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"apps/frontend-mobile/node_modules/@react-native-async-storage/async-storage": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz",
@@ -1277,14 +1125,6 @@
}
}
},
"apps/frontend-mobile/node_modules/react-native-device-info": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-11.1.0.tgz",
"integrity": "sha512-hzXJSObJdezEz0hF7MAJ3tGeoesuQWenXXt9mrQR9Mjb8kXpZ09rqSsZ/quNpJdZpQ3rYiFa3/0GFG5KNn9PBg==",
"peerDependencies": {
"react-native": "*"
}
},
"apps/frontend-mobile/node_modules/react-native-drawer-layout": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/react-native-drawer-layout/-/react-native-drawer-layout-4.2.7.tgz",
@@ -22421,6 +22261,14 @@
"node": ">=18"
}
},
"node_modules/nodemailer": {
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz",
"integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
@@ -0,0 +1,198 @@
import fs from 'fs';
import path from 'path';
const outPath = path.resolve('Patentes/INTERNATIONAL_PRIOR_ART_SEARCH_FARMAFINDER_EN.pdf');
const sections = [
{ type: 'title', text: 'International Prior Art Search' },
{ type: 'subtitle', text: 'Project: FarmaFinder' },
{ type: 'spacer', lines: 1 },
{ type: 'paragraph', text: 'This document summarizes the international prior-art position for the technical concept described in the FarmaFinder patent materials.' },
{ type: 'heading', text: 'Technical scope' },
{ type: 'paragraph', text: 'The invention concerns a computer-implemented system and method for identifying a therapeutic need from the scan of an individual health card or equivalent patient-related identifier, associating that result with an authenticated user, comparing the inferred need with geolocated pharmacy inventory, and generating supply-related actions when the required product is not available.' },
{ type: 'heading', text: 'Main categories of prior art identified' },
{ type: 'number', text: 'Medicine search platforms that allow users to locate medicines by name or active ingredient.' },
{ type: 'number', text: 'Pharmacy locator systems that display nearby pharmacies and, in some cases, opening hours, distance, or approximate availability.' },
{ type: 'number', text: 'Inventory management systems used by pharmacies, distributors, or health-related retail networks.' },
{ type: 'number', text: 'Digital health applications that store user profiles, location data, reminders, or treatment-related information.' },
{ type: 'number', text: 'Reservation, referral, or notification workflows that allow a user or a pharmacy to react when a product is out of stock.' },
{ type: 'heading', text: 'Distinguishing technical features' },
{ type: 'bullet', text: 'secure capture of a health-card identifier or equivalent patient-related credential;' },
{ type: 'bullet', text: 'verification of the captured data and association with an authenticated user profile;' },
{ type: 'bullet', text: 'automatic derivation of a medicine reference or therapeutic need;' },
{ type: 'bullet', text: 'real-time comparison against pharmacy stock data from geolocated pharmacies;' },
{ type: 'bullet', text: 'ranking of pharmacies according to availability, distance, and opening status;' },
{ type: 'bullet', text: 'automatic generation of reserve, notification, order, or referral actions when stock is insufficient;' },
{ type: 'bullet', text: 'technical traceability of the event across the processing chain.' },
{ type: 'heading', text: 'Assessment' },
{ type: 'paragraph', text: 'The reviewed prior-art categories appear to disclose individual parts of the technical problem, but not the complete integrated architecture described above in a single system. In particular, the combination of patient-context capture through a health identifier, automated therapeutic inference, geolocated stock comparison, and machine-generated supply actions does not appear to be disclosed as a unified technical chain in the documents reviewed at the project level.' },
{ type: 'heading', text: 'Suggested search focus for the European search phase' },
{ type: 'bullet', text: 'patient identifier scanning;' },
{ type: 'bullet', text: 'health-card reading or optical capture of patient credentials;' },
{ type: 'bullet', text: 'medicine search and pharmacy availability systems;' },
{ type: 'bullet', text: 'pharmacy reservation and referral workflows;' },
{ type: 'bullet', text: 'location-based ranking of pharmacies;' },
{ type: 'bullet', text: 'automated inventory-driven notification or order generation;' },
{ type: 'bullet', text: 'health-related digital assistants that combine user context with stock availability.' },
{ type: 'heading', text: 'Working conclusion' },
{ type: 'paragraph', text: 'On the basis of the current project documentation, the invention is presented as a computer-implemented technical solution that integrates identification, verification, geolocation, inventory contrast, and automated supply actions in one workflow. No single reference has been identified at the project level that clearly discloses the full combination of these features.' },
{ type: 'heading', text: 'Date' },
{ type: 'paragraph', text: '13 August 2026' },
];
const PAGE_W = 595.28;
const PAGE_H = 841.89;
const MARGIN_L = 56;
const MARGIN_R = 56;
const MARGIN_T = 54;
const MARGIN_B = 50;
const CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R;
function esc(s) {
return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
}
function latin1(s) {
return Buffer.from(s, 'latin1').toString('latin1');
}
function wrapText(text, size) {
const avg = size * 0.48;
const max = Math.max(25, Math.floor(CONTENT_W / avg));
const words = String(text).split(/\s+/);
const lines = [];
let line = '';
for (const word of words) {
const candidate = line ? `${line} ${word}` : word;
if (candidate.length > max && line) {
lines.push(line);
line = word;
} else {
line = candidate;
}
}
if (line) lines.push(line);
return lines;
}
function buildPdf() {
const objects = [''];
const add = (body) => {
objects.push(body);
return objects.length - 1;
};
const fontReg = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>');
const fontBold = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>');
const fontIt = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>');
const pagesTree = add('');
const contentNums = [];
const pageNums = [];
const pageContent = (pageLines, pageNo) => {
const ops = [];
let y = PAGE_H - MARGIN_T;
let numberCounter = 1;
const write = (txt, x, size, font = 'F1') => {
ops.push(`BT /${font} ${size} Tf ${x} ${y} Td (${esc(latin1(txt))}) Tj ET`);
y -= size * 1.25;
};
const ensure = (needed) => y - needed > MARGIN_B;
for (const item of pageLines) {
if (item.type === 'title') {
const lines = wrapText(item.text, 18);
for (const line of lines) write(line, MARGIN_L + 82, 18, 'F2');
y -= 4;
continue;
}
if (item.type === 'subtitle') {
const lines = wrapText(item.text, 12);
for (const line of lines) write(line, MARGIN_L + 100, 12, 'F3');
y -= 8;
continue;
}
if (item.type === 'spacer') {
y -= (item.lines || 1) * 12;
continue;
}
if (item.type === 'heading') {
if (!ensure(24)) break;
write(item.text, MARGIN_L, 13, 'F2');
y -= 4;
ops.push(`0.3 w ${MARGIN_L} ${y} m ${PAGE_W - MARGIN_R} ${y} l S`);
y -= 8;
continue;
}
if (item.type === 'paragraph') {
const lines = wrapText(item.text, 11.5);
if (!ensure(lines.length * 14 + 6)) break;
for (const line of lines) write(line, MARGIN_L, 11.5, 'F1');
y -= 2;
continue;
}
if (item.type === 'bullet') {
const lines = wrapText(item.text, 11.2);
if (!ensure(lines.length * 14 + 6)) break;
let first = true;
for (const line of lines) {
write((first ? '- ' : ' ') + line, MARGIN_L + 8, 11.2, 'F1');
first = false;
}
continue;
}
if (item.type === 'number') {
const lines = wrapText(item.text, 11.2);
if (!ensure(lines.length * 14 + 6)) break;
let first = true;
for (const line of lines) {
write((first ? `${numberCounter}. ` : ' ') + line, MARGIN_L + 8, 11.2, 'F1');
first = false;
}
numberCounter += 1;
continue;
}
}
ops.push(`BT /F1 9 Tf ${PAGE_W - 100} ${24} Td (Page ${pageNo}) Tj ET`);
return ops.join('\n');
};
const page1 = sections.slice(0, 12);
const page2 = sections.slice(12);
const pages = [page1, page2];
for (let i = 0; i < pages.length; i += 1) {
const content = pageContent(pages[i], i + 1);
contentNums.push(add(`<< /Length ${Buffer.byteLength(content, 'latin1')} >>\nstream\n${content}\nendstream`));
}
for (let i = 0; i < pages.length; i += 1) {
pageNums.push(add(`<< /Type /Page /Parent ${pagesTree} 0 R /MediaBox [0 0 ${PAGE_W} ${PAGE_H}] /Resources << /Font << /F1 ${fontReg} 0 R /F2 ${fontBold} 0 R /F3 ${fontIt} 0 R >> >> /Contents ${contentNums[i]} 0 R >>`));
}
objects[pagesTree] = `<< /Type /Pages /Kids [${pageNums.map(n => `${n} 0 R`).join(' ')}] /Count ${pageNums.length} >>`;
const catalog = add(`<< /Type /Catalog /Pages ${pagesTree} 0 R >>`);
let pdf = '%PDF-1.4\n';
const offsets = [0];
for (let i = 1; i < objects.length; i += 1) {
offsets[i] = Buffer.byteLength(pdf, 'latin1');
pdf += `${i} 0 obj\n${objects[i]}\nendobj\n`;
}
const xref = Buffer.byteLength(pdf, 'latin1');
pdf += `xref\n0 ${objects.length}\n`;
pdf += '0000000000 65535 f \n';
for (let i = 1; i < objects.length; i += 1) {
pdf += `${String(offsets[i]).padStart(10, '0')} 00000 n \n`;
}
pdf += `trailer << /Size ${objects.length} /Root ${catalog} 0 R >>\nstartxref\n${xref}\n%%EOF\n`;
fs.writeFileSync(outPath, Buffer.from(pdf, 'latin1'));
}
buildPdf();
console.log(`Wrote ${outPath}`);
+150
View File
@@ -0,0 +1,150 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
import { execFileSync } from 'child_process';
const root = process.cwd();
const sourcePdf = path.join(root, 'Patentes', 'SOLICITUD_PATENTE_OEPM_FARMAFINDER.pdf');
const outDir = path.join(root, 'Patentes');
function ensureExists(file) {
if (!fs.existsSync(file)) {
throw new Error(`Missing file: ${file}`);
}
}
function run(cmd, args) {
execFileSync(cmd, args, { stdio: 'inherit' });
}
function mergePages(output, pages) {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'farmafinder-clean-'));
run('pdfseparate', [sourcePdf, path.join(tmp, 'page-%d.pdf')]);
const inputs = pages.map((n) => path.join(tmp, `page-${n}.pdf`));
run('pdfunite', [...inputs, output]);
}
function esc(s) {
return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
}
function latin1(s) {
return Buffer.from(s, 'latin1').toString('latin1');
}
function wrapText(text, widthChars) {
const words = String(text).split(/\s+/);
const lines = [];
let line = '';
for (const word of words) {
const candidate = line ? `${line} ${word}` : word;
if (candidate.length > widthChars && line) {
lines.push(line);
line = word;
} else {
line = candidate;
}
}
if (line) lines.push(line);
return lines;
}
function writeDrawingsPdf(output) {
const PAGE_W = 595.28;
const PAGE_H = 841.89;
const MARGIN_L = 56;
const MARGIN_R = 56;
const MARGIN_T = 56;
const MARGIN_B = 54;
const CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R;
const lines = [
{ type: 'title', text: 'Dibujos' },
{ type: 'subtitle', text: 'Bloque limpio para la presentación de la solicitud' },
{ type: 'spacer', lines: 1 },
{ type: 'heading', text: 'Breve descripción de los dibujos' },
{ type: 'paragraph', text: 'Figura 1. Diagrama de bloques general del sistema.' },
{ type: 'paragraph', text: 'Figura 2. Flujo de captura y validación del escaneo de la TSI.' },
{ type: 'paragraph', text: 'Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.' },
{ type: 'paragraph', text: 'Figura 4. Flujo de acción cuando no existe stock disponible.' },
{ type: 'spacer', lines: 1 },
{ type: 'heading', text: 'Nota' },
{ type: 'paragraph', text: 'El documento fuente contiene referencias esquemáticas a las figuras, pero no incluye láminas técnicas dibujadas de forma separada. Si la Oficina requiere placas gráficas, habrá que añadirlas antes de la presentación definitiva.' },
];
const objects = [''];
const add = (body) => {
objects.push(body);
return objects.length - 1;
};
const fontReg = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>');
const fontBold = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>');
const fontIt = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>');
const pagesTree = add('');
const ops = [];
let y = PAGE_H - MARGIN_T;
const write = (txt, x, size, font = 'F1') => {
ops.push(`BT /${font} ${size} Tf ${x} ${y} Td (${esc(latin1(txt))}) Tj ET`);
y -= size * 1.25;
};
const ensure = (needed) => y - needed > MARGIN_B;
for (const item of lines) {
if (item.type === 'title') {
for (const line of wrapText(item.text, 26)) write(line, MARGIN_L + 175, 18, 'F2');
continue;
}
if (item.type === 'subtitle') {
for (const line of wrapText(item.text, 40)) write(line, MARGIN_L + 45, 11.5, 'F3');
y -= 6;
continue;
}
if (item.type === 'spacer') {
y -= (item.lines || 1) * 12;
continue;
}
if (item.type === 'heading') {
if (!ensure(20)) break;
write(item.text, MARGIN_L, 13, 'F2');
y -= 2;
ops.push(`0.3 w ${MARGIN_L} ${y} m ${PAGE_W - MARGIN_R} ${y} l S`);
y -= 8;
continue;
}
if (item.type === 'paragraph') {
const wrapped = wrapText(item.text, 80);
if (!ensure(wrapped.length * 14 + 6)) break;
for (const line of wrapped) write(line, MARGIN_L, 11.5, 'F1');
y -= 2;
}
}
ops.push(`BT /F1 9 Tf ${PAGE_W - 100} ${24} Td (Page 1) Tj ET`);
const content = ops.join('\n');
const contentObj = add(`<< /Length ${Buffer.byteLength(content, 'latin1')} >>\nstream\n${content}\nendstream`);
const pageObj = add(`<< /Type /Page /Parent ${pagesTree} 0 R /MediaBox [0 0 ${PAGE_W} ${PAGE_H}] /Resources << /Font << /F1 ${fontReg} 0 R /F2 ${fontBold} 0 R /F3 ${fontIt} 0 R >> >> /Contents ${contentObj} 0 R >>`);
objects[pagesTree] = `<< /Type /Pages /Kids [${pageObj} 0 R] /Count 1 >>`;
const catalog = add(`<< /Type /Catalog /Pages ${pagesTree} 0 R >>`);
let pdf = '%PDF-1.4\n';
const offsets = [0];
for (let i = 1; i < objects.length; i += 1) {
offsets[i] = Buffer.byteLength(pdf, 'latin1');
pdf += `${i} 0 obj\n${objects[i]}\nendobj\n`;
}
const xref = Buffer.byteLength(pdf, 'latin1');
pdf += `xref\n0 ${objects.length}\n`;
pdf += '0000000000 65535 f \n';
for (let i = 1; i < objects.length; i += 1) {
pdf += `${String(offsets[i]).padStart(10, '0')} 00000 n \n`;
}
pdf += `trailer << /Size ${objects.length} /Root ${catalog} 0 R >>\nstartxref\n${xref}\n%%EOF\n`;
fs.writeFileSync(output, Buffer.from(pdf, 'latin1'));
}
ensureExists(sourcePdf);
mergePages(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_descripcion_limpia.pdf'), [2, 3, 4]);
mergePages(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_reivindicaciones_limpia.pdf'), [5]);
mergePages(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_resumen_limpia.pdf'), [6]);
writeDrawingsPdf(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_dibujos_limpia.pdf'));
console.log('Clean section PDFs generated.');