spr migrated books, and tester
This commit is contained in:
184
atlas/book/arch-model/01-backend-architecture.dot
Normal file
184
atlas/book/arch-model/01-backend-architecture.dot
Normal file
@@ -0,0 +1,184 @@
|
||||
digraph BackendArchitecture {
|
||||
// Graph settings
|
||||
rankdir=TB
|
||||
compound=true
|
||||
splines=ortho
|
||||
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=11]
|
||||
edge [fontname="Helvetica", fontsize=9]
|
||||
|
||||
// Color scheme
|
||||
// Django Core: #092E20 (dark green)
|
||||
// Mascotas: #4A90D9 (blue)
|
||||
// Productos: #50C878 (green)
|
||||
// Solicitudes: #FF6B6B (coral)
|
||||
// Common: #9B59B6 (purple)
|
||||
// External: #F39C12 (orange)
|
||||
// Payments: #E74C3C (red)
|
||||
|
||||
label="AMAR Mascotas - Backend Architecture (Django)\n\n"
|
||||
labelloc="t"
|
||||
fontsize=16
|
||||
fontname="Helvetica-Bold"
|
||||
|
||||
// Django Core Cluster
|
||||
subgraph cluster_django_core {
|
||||
label="Django Core"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E8F5E9"
|
||||
color="#2E7D32"
|
||||
|
||||
auth_user [label="auth.User\n(Django Auth)", fillcolor="#C8E6C9"]
|
||||
django_admin [label="Django Admin\nInterface", fillcolor="#C8E6C9"]
|
||||
drf [label="Django REST\nFramework", fillcolor="#C8E6C9"]
|
||||
jwt_auth [label="JWT Authentication\n(SimpleJWT)", fillcolor="#C8E6C9"]
|
||||
}
|
||||
|
||||
// Mascotas App Cluster
|
||||
subgraph cluster_mascotas {
|
||||
label="mascotas (Pets & Veterinarians)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E3F2FD"
|
||||
color="#1565C0"
|
||||
|
||||
petowner [label="PetOwner\n(Cliente/Tutor)", fillcolor="#BBDEFB"]
|
||||
pet [label="Pet\n(Mascota)", fillcolor="#BBDEFB"]
|
||||
veterinarian [label="Veterinarian\n(Profesional)", fillcolor="#BBDEFB"]
|
||||
vetvisit [label="VetVisit\n(Consulta)", fillcolor="#BBDEFB"]
|
||||
vetvisitreport [label="VetVisitReport\n(Informe Clinico)", fillcolor="#BBDEFB"]
|
||||
availability [label="Availability /\nUnavailability", fillcolor="#BBDEFB"]
|
||||
pet_health [label="PetVaccine /\nPetStudy", fillcolor="#BBDEFB"]
|
||||
}
|
||||
|
||||
// Productos App Cluster
|
||||
subgraph cluster_productos {
|
||||
label="productos (Services & Pricing)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E8F5E9"
|
||||
color="#2E7D32"
|
||||
|
||||
service [label="Service\n(Servicio)", fillcolor="#C8E6C9"]
|
||||
category [label="Category / Group\n(Categorias)", fillcolor="#C8E6C9"]
|
||||
prices [label="Prices\n(Precios)", fillcolor="#C8E6C9"]
|
||||
discounts [label="Discounts\n(Descuentos)", fillcolor="#C8E6C9"]
|
||||
cart [label="Cart / CartItem\n(Carrito)", fillcolor="#C8E6C9"]
|
||||
combo [label="ServiceCombo\n(Paquetes)", fillcolor="#C8E6C9"]
|
||||
}
|
||||
|
||||
// Solicitudes App Cluster
|
||||
subgraph cluster_solicitudes {
|
||||
label="solicitudes (Service Requests)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFEBEE"
|
||||
color="#C62828"
|
||||
|
||||
servicerequest [label="ServiceRequest\n(Solicitud)", fillcolor="#FFCDD2"]
|
||||
statehistory [label="StateHistory\n(Historial)", fillcolor="#FFCDD2"]
|
||||
vetasked [label="VeterinarianAsked\n(Vet Consultado)", fillcolor="#FFCDD2"]
|
||||
reminders [label="Reminders\n(Recordatorios)", fillcolor="#FFCDD2"]
|
||||
}
|
||||
|
||||
// Common App Cluster
|
||||
subgraph cluster_common {
|
||||
label="common (Shared Models)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#F3E5F5"
|
||||
color="#7B1FA2"
|
||||
|
||||
campaign [label="Campaign\n(Marketing)", fillcolor="#E1BEE7"]
|
||||
tag [label="Tag\n(Etiquetas)", fillcolor="#E1BEE7"]
|
||||
specialty [label="Specialty\n(Especialidades)", fillcolor="#E1BEE7"]
|
||||
medication [label="Medication\n(Medicamentos)", fillcolor="#E1BEE7"]
|
||||
breed [label="PetBreed / Vaccine\n/ Study", fillcolor="#E1BEE7"]
|
||||
neighborhood [label="Neighborhood /\nProvince / Locality", fillcolor="#E1BEE7"]
|
||||
turnfee [label="IndividualTurnFee\nGroup", fillcolor="#E1BEE7"]
|
||||
}
|
||||
|
||||
// Payments App Cluster
|
||||
subgraph cluster_payments {
|
||||
label="payments (Payment Processing)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FCE4EC"
|
||||
color="#AD1457"
|
||||
|
||||
mercadopago [label="MercadoPago\nAccount", fillcolor="#F8BBD9"]
|
||||
mpnotification [label="MP Notification\n(Webhooks)", fillcolor="#F8BBD9"]
|
||||
}
|
||||
|
||||
// External Integrations Cluster
|
||||
subgraph cluster_external {
|
||||
label="External Integrations"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFF3E0"
|
||||
color="#E65100"
|
||||
|
||||
google_cal [label="Google Calendar\n(Agenda)", fillcolor="#FFE0B2"]
|
||||
google_sheets [label="Google Sheets\n(Exports)", fillcolor="#FFE0B2"]
|
||||
mercately [label="Mercately\n(WhatsApp)", fillcolor="#FFE0B2"]
|
||||
afip [label="AFIP\n(Facturacion)", fillcolor="#FFE0B2"]
|
||||
celery [label="Celery\n(Async Tasks)", fillcolor="#FFE0B2"]
|
||||
}
|
||||
|
||||
// AFIP Integration
|
||||
subgraph cluster_afip {
|
||||
label="django_afip (Invoicing)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFFDE7"
|
||||
color="#F9A825"
|
||||
|
||||
receipt [label="Receipt\n(Comprobante)", fillcolor="#FFF9C4"]
|
||||
taxpayer [label="TaxPayer\n(Contribuyente)", fillcolor="#FFF9C4"]
|
||||
pos [label="PointOfSales\n(Punto de Venta)", fillcolor="#FFF9C4"]
|
||||
}
|
||||
|
||||
// Relationships - Core
|
||||
auth_user -> petowner [label="1:1 optional", color="#666"]
|
||||
auth_user -> veterinarian [label="1:1", color="#666"]
|
||||
drf -> jwt_auth [style=dashed, color="#999"]
|
||||
|
||||
// Relationships - Mascotas
|
||||
petowner -> pet [label="1:N", color="#1565C0"]
|
||||
petowner -> servicerequest [label="1:N", color="#1565C0"]
|
||||
pet -> vetvisit [label="N:M", color="#1565C0"]
|
||||
pet -> pet_health [label="1:N", color="#1565C0"]
|
||||
veterinarian -> vetvisit [label="1:N", color="#1565C0"]
|
||||
veterinarian -> availability [label="1:N", color="#1565C0"]
|
||||
vetvisit -> vetvisitreport [label="1:N", color="#1565C0"]
|
||||
vetvisit -> servicerequest [label="1:1", style=dashed, color="#1565C0"]
|
||||
|
||||
// Relationships - Productos
|
||||
service -> category [label="N:1", color="#2E7D32"]
|
||||
service -> prices [label="1:N", color="#2E7D32"]
|
||||
service -> discounts [label="1:N", color="#2E7D32"]
|
||||
cart -> petowner [label="N:1", color="#2E7D32"]
|
||||
cart -> veterinarian [label="N:1 optional", color="#2E7D32", style=dashed]
|
||||
service -> cart [label="via CartItem", color="#2E7D32"]
|
||||
combo -> service [label="contains", color="#2E7D32"]
|
||||
|
||||
// Relationships - Solicitudes
|
||||
servicerequest -> cart [label="1:1", color="#C62828"]
|
||||
servicerequest -> statehistory [label="1:N", color="#C62828"]
|
||||
servicerequest -> vetasked [label="1:N", color="#C62828"]
|
||||
vetasked -> reminders [label="1:N", color="#C62828"]
|
||||
veterinarian -> vetasked [label="N:1", color="#C62828"]
|
||||
|
||||
// Relationships - Common
|
||||
petowner -> neighborhood [label="N:1", color="#7B1FA2"]
|
||||
veterinarian -> specialty [label="N:M", color="#7B1FA2"]
|
||||
veterinarian -> neighborhood [label="N:M coverage", color="#7B1FA2"]
|
||||
servicerequest -> campaign [label="N:1 optional", color="#7B1FA2", style=dashed]
|
||||
servicerequest -> tag [label="N:M", color="#7B1FA2"]
|
||||
vetvisitreport -> medication [label="references", color="#7B1FA2", style=dashed]
|
||||
veterinarian -> turnfee [label="N:M", color="#7B1FA2"]
|
||||
|
||||
// Relationships - Payments & External
|
||||
servicerequest -> mercadopago [label="payment", color="#AD1457"]
|
||||
mpnotification -> servicerequest [label="confirms", color="#AD1457"]
|
||||
vetvisit -> google_cal [label="sync", color="#E65100", style=dashed]
|
||||
servicerequest -> mercately [label="notify", color="#E65100", style=dashed]
|
||||
reminders -> celery [label="scheduled", color="#E65100", style=dashed]
|
||||
|
||||
// AFIP relationships
|
||||
vetvisit -> receipt [label="1:1 optional", color="#F9A825", style=dashed]
|
||||
receipt -> taxpayer [label="N:1", color="#F9A825"]
|
||||
receipt -> pos [label="N:1", color="#F9A825"]
|
||||
}
|
||||
585
atlas/book/arch-model/01-backend-architecture.svg
Normal file
585
atlas/book/arch-model/01-backend-architecture.svg
Normal file
@@ -0,0 +1,585 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 14.0.5 (0)
|
||||
-->
|
||||
<!-- Title: BackendArchitecture Pages: 1 -->
|
||||
<svg width="1559pt" height="935pt"
|
||||
viewBox="0.00 0.00 1559.00 935.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 931.4)">
|
||||
<title>BackendArchitecture</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-931.4 1555,-931.4 1555,4 -4,4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="775.5" y="-908.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Backend Architecture (Django)</text>
|
||||
<g id="clust1" class="cluster">
|
||||
<title>cluster_django_core</title>
|
||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M169,-496.4C169,-496.4 449,-496.4 449,-496.4 455,-496.4 461,-502.4 461,-508.4 461,-508.4 461,-726 461,-726 461,-732 455,-738 449,-738 449,-738 169,-738 169,-738 163,-738 157,-732 157,-726 157,-726 157,-508.4 157,-508.4 157,-502.4 163,-496.4 169,-496.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="309" y="-718.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Django Core</text>
|
||||
</g>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_mascotas</title>
|
||||
<path fill="#e3f2fd" stroke="#1565c0" d="M481,-143.2C481,-143.2 733,-143.2 733,-143.2 739,-143.2 745,-149.2 745,-155.2 745,-155.2 745,-590.8 745,-590.8 745,-596.8 739,-602.8 733,-602.8 733,-602.8 481,-602.8 481,-602.8 475,-602.8 469,-596.8 469,-590.8 469,-590.8 469,-155.2 469,-155.2 469,-149.2 475,-143.2 481,-143.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="607" y="-583.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">mascotas (Pets & Veterinarians)</text>
|
||||
</g>
|
||||
<g id="clust3" class="cluster">
|
||||
<title>cluster_productos</title>
|
||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M765,-496.4C765,-496.4 1141,-496.4 1141,-496.4 1147,-496.4 1153,-502.4 1153,-508.4 1153,-508.4 1153,-861.2 1153,-861.2 1153,-867.2 1147,-873.2 1141,-873.2 1141,-873.2 765,-873.2 765,-873.2 759,-873.2 753,-867.2 753,-861.2 753,-861.2 753,-508.4 753,-508.4 753,-502.4 759,-496.4 765,-496.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-854" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">productos (Services & Pricing)</text>
|
||||
</g>
|
||||
<g id="clust4" class="cluster">
|
||||
<title>cluster_solicitudes</title>
|
||||
<path fill="#ffebee" stroke="#c62828" d="M1084,-143.2C1084,-143.2 1290,-143.2 1290,-143.2 1296,-143.2 1302,-149.2 1302,-155.2 1302,-155.2 1302,-455.6 1302,-455.6 1302,-461.6 1296,-467.6 1290,-467.6 1290,-467.6 1084,-467.6 1084,-467.6 1078,-467.6 1072,-461.6 1072,-455.6 1072,-455.6 1072,-155.2 1072,-155.2 1072,-149.2 1078,-143.2 1084,-143.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1187" y="-448.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">solicitudes (Service Requests)</text>
|
||||
</g>
|
||||
<g id="clust5" class="cluster">
|
||||
<title>cluster_common</title>
|
||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M20,-8C20,-8 774,-8 774,-8 780,-8 786,-14 786,-20 786,-20 786,-102.4 786,-102.4 786,-108.4 780,-114.4 774,-114.4 774,-114.4 20,-114.4 20,-114.4 14,-114.4 8,-108.4 8,-102.4 8,-102.4 8,-20 8,-20 8,-14 14,-8 20,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="397" y="-95.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">common (Shared Models)</text>
|
||||
</g>
|
||||
<g id="clust6" class="cluster">
|
||||
<title>cluster_payments</title>
|
||||
<path fill="#fce4ec" stroke="#ad1457" d="M1173,-496.4C1173,-496.4 1395,-496.4 1395,-496.4 1401,-496.4 1407,-502.4 1407,-508.4 1407,-508.4 1407,-590.8 1407,-590.8 1407,-596.8 1401,-602.8 1395,-602.8 1395,-602.8 1173,-602.8 1173,-602.8 1167,-602.8 1161,-596.8 1161,-590.8 1161,-590.8 1161,-508.4 1161,-508.4 1161,-502.4 1167,-496.4 1173,-496.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1284" y="-583.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">payments (Payment Processing)</text>
|
||||
</g>
|
||||
<g id="clust7" class="cluster">
|
||||
<title>cluster_external</title>
|
||||
<path fill="#fff3e0" stroke="#e65100" d="M1039,-8C1039,-8 1531,-8 1531,-8 1537,-8 1543,-14 1543,-20 1543,-20 1543,-102.4 1543,-102.4 1543,-108.4 1537,-114.4 1531,-114.4 1531,-114.4 1039,-114.4 1039,-114.4 1033,-114.4 1027,-108.4 1027,-102.4 1027,-102.4 1027,-20 1027,-20 1027,-14 1033,-8 1039,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1285" y="-95.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">External Integrations</text>
|
||||
</g>
|
||||
<g id="clust8" class="cluster">
|
||||
<title>cluster_afip</title>
|
||||
<path fill="#fffde7" stroke="#f9a825" d="M806,-8C806,-8 1007,-8 1007,-8 1013,-8 1019,-14 1019,-20 1019,-20 1019,-237.6 1019,-237.6 1019,-243.6 1013,-249.6 1007,-249.6 1007,-249.6 806,-249.6 806,-249.6 800,-249.6 794,-243.6 794,-237.6 794,-237.6 794,-20 794,-20 794,-14 800,-8 806,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="906.5" y="-230.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">django_afip (Invoicing)</text>
|
||||
</g>
|
||||
<!-- auth_user -->
|
||||
<g id="node1" class="node">
|
||||
<title>auth_user</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M440.93,-675.6C440.93,-675.6 381.07,-675.6 381.07,-675.6 375.07,-675.6 369.07,-669.6 369.07,-663.6 369.07,-663.6 369.07,-651.6 369.07,-651.6 369.07,-645.6 375.07,-639.6 381.07,-639.6 381.07,-639.6 440.93,-639.6 440.93,-639.6 446.93,-639.6 452.93,-645.6 452.93,-651.6 452.93,-651.6 452.93,-663.6 452.93,-663.6 452.93,-669.6 446.93,-675.6 440.93,-675.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="411" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">auth.User</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="411" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">(Django Auth)</text>
|
||||
</g>
|
||||
<!-- petowner -->
|
||||
<g id="node5" class="node">
|
||||
<title>petowner</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M725.15,-540.4C725.15,-540.4 662.85,-540.4 662.85,-540.4 656.85,-540.4 650.85,-534.4 650.85,-528.4 650.85,-528.4 650.85,-516.4 650.85,-516.4 650.85,-510.4 656.85,-504.4 662.85,-504.4 662.85,-504.4 725.15,-504.4 725.15,-504.4 731.15,-504.4 737.15,-510.4 737.15,-516.4 737.15,-516.4 737.15,-528.4 737.15,-528.4 737.15,-534.4 731.15,-540.4 725.15,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">PetOwner</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Cliente/Tutor)</text>
|
||||
</g>
|
||||
<!-- auth_user->petowner -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>auth_user->petowner</title>
|
||||
<path fill="none" stroke="#666666" d="M424.94,-639.31C424.94,-602.3 424.94,-522 424.94,-522 424.94,-522 638.96,-522 638.96,-522"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="638.96,-525.5 648.96,-522 638.96,-518.5 638.96,-525.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="645.27" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">1:1 optional</text>
|
||||
</g>
|
||||
<!-- veterinarian -->
|
||||
<g id="node7" class="node">
|
||||
<title>veterinarian</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M544.18,-405.2C544.18,-405.2 489.82,-405.2 489.82,-405.2 483.82,-405.2 477.82,-399.2 477.82,-393.2 477.82,-393.2 477.82,-381.2 477.82,-381.2 477.82,-375.2 483.82,-369.2 489.82,-369.2 489.82,-369.2 544.18,-369.2 544.18,-369.2 550.18,-369.2 556.18,-375.2 556.18,-381.2 556.18,-381.2 556.18,-393.2 556.18,-393.2 556.18,-399.2 550.18,-405.2 544.18,-405.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-390.5" font-family="Helvetica,sans-Serif" font-size="11.00">Veterinarian</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-377.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Profesional)</text>
|
||||
</g>
|
||||
<!-- auth_user->veterinarian -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>auth_user->veterinarian</title>
|
||||
<path fill="none" stroke="#666666" d="M453.39,-664C477.9,-664 503.94,-664 503.94,-664 503.94,-664 503.94,-416.88 503.94,-416.88"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="507.44,-416.88 503.94,-406.88 500.44,-416.88 507.44,-416.88"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="470.26" y="-519.7" font-family="Helvetica,sans-Serif" font-size="9.00">1:1</text>
|
||||
</g>
|
||||
<!-- django_admin -->
|
||||
<g id="node2" class="node">
|
||||
<title>django_admin</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M339.55,-675.6C339.55,-675.6 278.45,-675.6 278.45,-675.6 272.45,-675.6 266.45,-669.6 266.45,-663.6 266.45,-663.6 266.45,-651.6 266.45,-651.6 266.45,-645.6 272.45,-639.6 278.45,-639.6 278.45,-639.6 339.55,-639.6 339.55,-639.6 345.55,-639.6 351.55,-645.6 351.55,-651.6 351.55,-651.6 351.55,-663.6 351.55,-663.6 351.55,-669.6 345.55,-675.6 339.55,-675.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="309" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">Django Admin</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="309" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">Interface</text>
|
||||
</g>
|
||||
<!-- drf -->
|
||||
<g id="node3" class="node">
|
||||
<title>drf</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M236.63,-675.6C236.63,-675.6 177.37,-675.6 177.37,-675.6 171.37,-675.6 165.37,-669.6 165.37,-663.6 165.37,-663.6 165.37,-651.6 165.37,-651.6 165.37,-645.6 171.37,-639.6 177.37,-639.6 177.37,-639.6 236.63,-639.6 236.63,-639.6 242.63,-639.6 248.63,-645.6 248.63,-651.6 248.63,-651.6 248.63,-663.6 248.63,-663.6 248.63,-669.6 242.63,-675.6 236.63,-675.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="207" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">Django REST</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="207" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">Framework</text>
|
||||
</g>
|
||||
<!-- jwt_auth -->
|
||||
<g id="node4" class="node">
|
||||
<title>jwt_auth</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M264.69,-540.4C264.69,-540.4 177.31,-540.4 177.31,-540.4 171.31,-540.4 165.31,-534.4 165.31,-528.4 165.31,-528.4 165.31,-516.4 165.31,-516.4 165.31,-510.4 171.31,-504.4 177.31,-504.4 177.31,-504.4 264.69,-504.4 264.69,-504.4 270.69,-504.4 276.69,-510.4 276.69,-516.4 276.69,-516.4 276.69,-528.4 276.69,-528.4 276.69,-534.4 270.69,-540.4 264.69,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="221" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">JWT Authentication</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="221" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(SimpleJWT)</text>
|
||||
</g>
|
||||
<!-- drf->jwt_auth -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>drf->jwt_auth</title>
|
||||
<path fill="none" stroke="#999999" stroke-dasharray="5,2" d="M207,-639.37C207,-639.37 207,-552.29 207,-552.29"/>
|
||||
<polygon fill="#999999" stroke="#999999" points="210.5,-552.29 207,-542.29 203.5,-552.29 210.5,-552.29"/>
|
||||
</g>
|
||||
<!-- pet -->
|
||||
<g id="node6" class="node">
|
||||
<title>pet</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M714.45,-405.2C714.45,-405.2 673.55,-405.2 673.55,-405.2 667.55,-405.2 661.55,-399.2 661.55,-393.2 661.55,-393.2 661.55,-381.2 661.55,-381.2 661.55,-375.2 667.55,-369.2 673.55,-369.2 673.55,-369.2 714.45,-369.2 714.45,-369.2 720.45,-369.2 726.45,-375.2 726.45,-381.2 726.45,-381.2 726.45,-393.2 726.45,-393.2 726.45,-399.2 720.45,-405.2 714.45,-405.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-390.5" font-family="Helvetica,sans-Serif" font-size="11.00">Pet</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="694" y="-377.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Mascota)</text>
|
||||
</g>
|
||||
<!-- petowner->pet -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>petowner->pet</title>
|
||||
<path fill="none" stroke="#1565c0" d="M694,-504.17C694,-504.17 694,-417.09 694,-417.09"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="697.5,-417.09 694,-407.09 690.5,-417.09 697.5,-417.09"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="701" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- servicerequest -->
|
||||
<g id="node18" class="node">
|
||||
<title>servicerequest</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1163.82,-405.2C1163.82,-405.2 1094.18,-405.2 1094.18,-405.2 1088.18,-405.2 1082.18,-399.2 1082.18,-393.2 1082.18,-393.2 1082.18,-381.2 1082.18,-381.2 1082.18,-375.2 1088.18,-369.2 1094.18,-369.2 1094.18,-369.2 1163.82,-369.2 1163.82,-369.2 1169.82,-369.2 1175.82,-375.2 1175.82,-381.2 1175.82,-381.2 1175.82,-393.2 1175.82,-393.2 1175.82,-399.2 1169.82,-405.2 1163.82,-405.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1129" y="-390.5" font-family="Helvetica,sans-Serif" font-size="11.00">ServiceRequest</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1129" y="-377.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Solicitud)</text>
|
||||
</g>
|
||||
<!-- petowner->servicerequest -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>petowner->servicerequest</title>
|
||||
<path fill="none" stroke="#1565c0" d="M729.12,-504.02C729.12,-468.96 729.12,-396 729.12,-396 729.12,-396 1070.44,-396 1070.44,-396"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="1070.44,-399.5 1080.44,-396 1070.44,-392.5 1070.44,-399.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1005" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- neighborhood -->
|
||||
<g id="node27" class="node">
|
||||
<title>neighborhood</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M109.63,-52C109.63,-52 28.37,-52 28.37,-52 22.37,-52 16.37,-46 16.37,-40 16.37,-40 16.37,-28 16.37,-28 16.37,-22 22.37,-16 28.37,-16 28.37,-16 109.63,-16 109.63,-16 115.63,-16 121.63,-22 121.63,-28 121.63,-28 121.63,-40 121.63,-40 121.63,-46 115.63,-52 109.63,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="69" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Neighborhood /</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="69" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">Province / Locality</text>
|
||||
</g>
|
||||
<!-- petowner->neighborhood -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>petowner->neighborhood</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M655.77,-504.11C655.77,-441.12 655.77,-237 655.77,-237 655.77,-237 86.54,-237 86.54,-237 86.54,-237 86.54,-63.89 86.54,-63.89"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="90.04,-63.89 86.54,-53.89 83.04,-63.89 90.04,-63.89"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="86" y="-301.7" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
||||
</g>
|
||||
<!-- vetvisit -->
|
||||
<g id="node8" class="node">
|
||||
<title>vetvisit</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M725.37,-322.4C725.37,-322.4 682.63,-322.4 682.63,-322.4 676.63,-322.4 670.63,-316.4 670.63,-310.4 670.63,-310.4 670.63,-298.4 670.63,-298.4 670.63,-292.4 676.63,-286.4 682.63,-286.4 682.63,-286.4 725.37,-286.4 725.37,-286.4 731.37,-286.4 737.37,-292.4 737.37,-298.4 737.37,-298.4 737.37,-310.4 737.37,-310.4 737.37,-316.4 731.37,-322.4 725.37,-322.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="704" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">VetVisit</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="704" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Consulta)</text>
|
||||
</g>
|
||||
<!-- pet->vetvisit -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>pet->vetvisit</title>
|
||||
<path fill="none" stroke="#1565c0" d="M698.54,-368.82C698.54,-368.82 698.54,-334.24 698.54,-334.24"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="702.04,-334.24 698.54,-324.24 695.04,-334.24 702.04,-334.24"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="710.25" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
||||
</g>
|
||||
<!-- pet_health -->
|
||||
<g id="node11" class="node">
|
||||
<title>pet_health</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M640.88,-322.4C640.88,-322.4 587.12,-322.4 587.12,-322.4 581.12,-322.4 575.12,-316.4 575.12,-310.4 575.12,-310.4 575.12,-298.4 575.12,-298.4 575.12,-292.4 581.12,-286.4 587.12,-286.4 587.12,-286.4 640.88,-286.4 640.88,-286.4 646.88,-286.4 652.88,-292.4 652.88,-298.4 652.88,-298.4 652.88,-310.4 652.88,-310.4 652.88,-316.4 646.88,-322.4 640.88,-322.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="614" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">PetVaccine /</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="614" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">PetStudy</text>
|
||||
</g>
|
||||
<!-- pet->pet_health -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>pet->pet_health</title>
|
||||
<path fill="none" stroke="#1565c0" d="M666.09,-368.72C666.09,-342.59 666.09,-298 666.09,-298 666.09,-298 664.73,-298 664.73,-298"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="664.73,-294.5 654.73,-298 664.73,-301.5 664.73,-294.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="623" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- veterinarian->vetvisit -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>veterinarian->vetvisit</title>
|
||||
<path fill="none" stroke="#1565c0" d="M556.46,-387C598.38,-387 658.66,-387 658.66,-387 658.66,-387 658.66,-310 658.66,-310 658.66,-310 659.85,-310 659.85,-310"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="659.06,-313.5 669.06,-310 659.06,-306.5 659.06,-313.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="668" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- availability -->
|
||||
<g id="node10" class="node">
|
||||
<title>availability</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M545.4,-322.4C545.4,-322.4 488.6,-322.4 488.6,-322.4 482.6,-322.4 476.6,-316.4 476.6,-310.4 476.6,-310.4 476.6,-298.4 476.6,-298.4 476.6,-292.4 482.6,-286.4 488.6,-286.4 488.6,-286.4 545.4,-286.4 545.4,-286.4 551.4,-286.4 557.4,-292.4 557.4,-298.4 557.4,-298.4 557.4,-310.4 557.4,-310.4 557.4,-316.4 551.4,-322.4 545.4,-322.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">Availability /</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="517" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">Unavailability</text>
|
||||
</g>
|
||||
<!-- veterinarian->availability -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>veterinarian->availability</title>
|
||||
<path fill="none" stroke="#1565c0" d="M503.94,-368.82C503.94,-368.82 503.94,-334.24 503.94,-334.24"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="507.44,-334.24 503.94,-324.24 500.44,-334.24 507.44,-334.24"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="524" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetasked -->
|
||||
<g id="node20" class="node">
|
||||
<title>vetasked</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1173.94,-322.4C1173.94,-322.4 1092.06,-322.4 1092.06,-322.4 1086.06,-322.4 1080.06,-316.4 1080.06,-310.4 1080.06,-310.4 1080.06,-298.4 1080.06,-298.4 1080.06,-292.4 1086.06,-286.4 1092.06,-286.4 1092.06,-286.4 1173.94,-286.4 1173.94,-286.4 1179.94,-286.4 1185.94,-292.4 1185.94,-298.4 1185.94,-298.4 1185.94,-310.4 1185.94,-310.4 1185.94,-316.4 1179.94,-322.4 1173.94,-322.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1133" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">VeterinarianAsked</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1133" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Vet Consultado)</text>
|
||||
</g>
|
||||
<!-- veterinarian->vetasked -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>veterinarian->vetasked</title>
|
||||
<path fill="none" stroke="#c62828" d="M530.06,-368.88C530.06,-354.89 530.06,-338 530.06,-338 530.06,-338 1129,-338 1129,-338 1129,-338 1129,-334.29 1129,-334.29"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1132.5,-334.29 1129,-324.29 1125.5,-334.29 1132.5,-334.29"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="959" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
||||
</g>
|
||||
<!-- specialty -->
|
||||
<g id="node24" class="node">
|
||||
<title>specialty</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M224.66,-52C224.66,-52 151.34,-52 151.34,-52 145.34,-52 139.34,-46 139.34,-40 139.34,-40 139.34,-28 139.34,-28 139.34,-22 145.34,-16 151.34,-16 151.34,-16 224.66,-16 224.66,-16 230.66,-16 236.66,-22 236.66,-28 236.66,-28 236.66,-40 236.66,-40 236.66,-46 230.66,-52 224.66,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="188" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Specialty</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="188" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Especialidades)</text>
|
||||
</g>
|
||||
<!-- veterinarian->specialty -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>veterinarian->specialty</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M477.62,-387C392.9,-387 200.99,-387 200.99,-387 200.99,-387 200.99,-64 200.99,-64"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="204.49,-64 200.99,-54 197.49,-64 204.49,-64"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="289.25" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
||||
</g>
|
||||
<!-- veterinarian->neighborhood -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>veterinarian->neighborhood</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M477.71,-396C365.47,-396 51.46,-396 51.46,-396 51.46,-396 51.46,-63.96 51.46,-63.96"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="54.96,-63.96 51.46,-53.96 47.96,-63.96 54.96,-63.96"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="158.01" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M coverage</text>
|
||||
</g>
|
||||
<!-- turnfee -->
|
||||
<g id="node28" class="node">
|
||||
<title>turnfee</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M347.02,-52C347.02,-52 266.98,-52 266.98,-52 260.98,-52 254.98,-46 254.98,-40 254.98,-40 254.98,-28 254.98,-28 254.98,-22 260.98,-16 266.98,-16 266.98,-16 347.02,-16 347.02,-16 353.02,-16 359.02,-22 359.02,-28 359.02,-28 359.02,-40 359.02,-40 359.02,-46 353.02,-52 347.02,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="307" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">IndividualTurnFee</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="307" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">Group</text>
|
||||
</g>
|
||||
<!-- veterinarian->turnfee -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>veterinarian->turnfee</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M477.44,-378C429.56,-378 355.29,-378 355.29,-378 355.29,-378 355.29,-64.01 355.29,-64.01"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="358.79,-64.01 355.29,-54.01 351.79,-64.01 358.79,-64.01"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="364.25" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
||||
</g>
|
||||
<!-- vetvisitreport -->
|
||||
<g id="node9" class="node">
|
||||
<title>vetvisitreport</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M694.34,-187.2C694.34,-187.2 621.66,-187.2 621.66,-187.2 615.66,-187.2 609.66,-181.2 609.66,-175.2 609.66,-175.2 609.66,-163.2 609.66,-163.2 609.66,-157.2 615.66,-151.2 621.66,-151.2 621.66,-151.2 694.34,-151.2 694.34,-151.2 700.34,-151.2 706.34,-157.2 706.34,-163.2 706.34,-163.2 706.34,-175.2 706.34,-175.2 706.34,-181.2 700.34,-187.2 694.34,-187.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="658" y="-172.5" font-family="Helvetica,sans-Serif" font-size="11.00">VetVisitReport</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="658" y="-159.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Informe Clinico)</text>
|
||||
</g>
|
||||
<!-- vetvisit->vetvisitreport -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>vetvisit->vetvisitreport</title>
|
||||
<path fill="none" stroke="#1565c0" d="M688.49,-286.17C688.49,-286.17 688.49,-199.09 688.49,-199.09"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="691.99,-199.09 688.49,-189.09 684.99,-199.09 691.99,-199.09"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="671" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetvisit->servicerequest -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>vetvisit->servicerequest</title>
|
||||
<path fill="none" stroke="#1565c0" stroke-dasharray="5,2" d="M731.8,-322.73C731.8,-347.07 731.8,-387 731.8,-387 731.8,-387 1070.36,-387 1070.36,-387"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="1070.36,-390.5 1080.36,-387 1070.36,-383.5 1070.36,-390.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="749.26" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:1</text>
|
||||
</g>
|
||||
<!-- google_cal -->
|
||||
<g id="node31" class="node">
|
||||
<title>google_cal</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M1122.58,-52C1122.58,-52 1047.42,-52 1047.42,-52 1041.42,-52 1035.42,-46 1035.42,-40 1035.42,-40 1035.42,-28 1035.42,-28 1035.42,-22 1041.42,-16 1047.42,-16 1047.42,-16 1122.58,-16 1122.58,-16 1128.58,-16 1134.58,-22 1134.58,-28 1134.58,-28 1134.58,-40 1134.58,-40 1134.58,-46 1128.58,-52 1122.58,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1085" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Google Calendar</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1085" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Agenda)</text>
|
||||
</g>
|
||||
<!-- vetvisit->google_cal -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>vetvisit->google_cal</title>
|
||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M737.59,-304C824.11,-304 1047.82,-304 1047.82,-304 1047.82,-304 1047.82,-63.65 1047.82,-63.65"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1051.32,-63.65 1047.82,-53.65 1044.32,-63.65 1051.32,-63.65"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1054.25" y="-166.5" font-family="Helvetica,sans-Serif" font-size="9.00">sync</text>
|
||||
</g>
|
||||
<!-- receipt -->
|
||||
<g id="node36" class="node">
|
||||
<title>receipt</title>
|
||||
<path fill="#fff9c4" stroke="black" d="M880.99,-187.2C880.99,-187.2 815.01,-187.2 815.01,-187.2 809.01,-187.2 803.01,-181.2 803.01,-175.2 803.01,-175.2 803.01,-163.2 803.01,-163.2 803.01,-157.2 809.01,-151.2 815.01,-151.2 815.01,-151.2 880.99,-151.2 880.99,-151.2 886.99,-151.2 892.99,-157.2 892.99,-163.2 892.99,-163.2 892.99,-175.2 892.99,-175.2 892.99,-181.2 886.99,-187.2 880.99,-187.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-172.5" font-family="Helvetica,sans-Serif" font-size="11.00">Receipt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-159.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Comprobante)</text>
|
||||
</g>
|
||||
<!-- vetvisit->receipt -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>vetvisit->receipt</title>
|
||||
<path fill="none" stroke="#f9a825" stroke-dasharray="5,2" d="M722.89,-286.14C722.89,-249.18 722.89,-169 722.89,-169 722.89,-169 791.32,-169 791.32,-169"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="791.32,-172.5 801.32,-169 791.32,-165.5 791.32,-172.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="727.27" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:1 optional</text>
|
||||
</g>
|
||||
<!-- medication -->
|
||||
<g id="node25" class="node">
|
||||
<title>medication</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M678.43,-52C678.43,-52 607.57,-52 607.57,-52 601.57,-52 595.57,-46 595.57,-40 595.57,-40 595.57,-28 595.57,-28 595.57,-22 601.57,-16 607.57,-16 607.57,-16 678.43,-16 678.43,-16 684.43,-16 690.43,-22 690.43,-28 690.43,-28 690.43,-40 690.43,-40 690.43,-46 684.43,-52 678.43,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="643" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Medication</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="643" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Medicamentos)</text>
|
||||
</g>
|
||||
<!-- vetvisitreport->medication -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>vetvisitreport->medication</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M650.04,-150.97C650.04,-150.97 650.04,-63.89 650.04,-63.89"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="653.54,-63.89 650.04,-53.89 646.54,-63.89 653.54,-63.89"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="667.26" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">references</text>
|
||||
</g>
|
||||
<!-- service -->
|
||||
<g id="node12" class="node">
|
||||
<title>service</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M972.22,-675.6C972.22,-675.6 933.78,-675.6 933.78,-675.6 927.78,-675.6 921.78,-669.6 921.78,-663.6 921.78,-663.6 921.78,-651.6 921.78,-651.6 921.78,-645.6 927.78,-639.6 933.78,-639.6 933.78,-639.6 972.22,-639.6 972.22,-639.6 978.22,-639.6 984.22,-645.6 984.22,-651.6 984.22,-651.6 984.22,-663.6 984.22,-663.6 984.22,-669.6 978.22,-675.6 972.22,-675.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-660.9" font-family="Helvetica,sans-Serif" font-size="11.00">Service</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-647.7" font-family="Helvetica,sans-Serif" font-size="11.00">(Servicio)</text>
|
||||
</g>
|
||||
<!-- category -->
|
||||
<g id="node13" class="node">
|
||||
<title>category</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M849.19,-540.4C849.19,-540.4 772.81,-540.4 772.81,-540.4 766.81,-540.4 760.81,-534.4 760.81,-528.4 760.81,-528.4 760.81,-516.4 760.81,-516.4 760.81,-510.4 766.81,-504.4 772.81,-504.4 772.81,-504.4 849.19,-504.4 849.19,-504.4 855.19,-504.4 861.19,-510.4 861.19,-516.4 861.19,-516.4 861.19,-528.4 861.19,-528.4 861.19,-534.4 855.19,-540.4 849.19,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="811" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Category / Group</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="811" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Categorias)</text>
|
||||
</g>
|
||||
<!-- service->category -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>service->category</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M921.49,-652C879.65,-652 811,-652 811,-652 811,-652 811,-552.21 811,-552.21"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="814.5,-552.21 811,-542.21 807.5,-552.21 814.5,-552.21"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="835" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
||||
</g>
|
||||
<!-- prices -->
|
||||
<g id="node14" class="node">
|
||||
<title>prices</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M927,-540.4C927,-540.4 891,-540.4 891,-540.4 885,-540.4 879,-534.4 879,-528.4 879,-528.4 879,-516.4 879,-516.4 879,-510.4 885,-504.4 891,-504.4 891,-504.4 927,-504.4 927,-504.4 933,-504.4 939,-510.4 939,-516.4 939,-516.4 939,-528.4 939,-528.4 939,-534.4 933,-540.4 927,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="909" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Prices</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="909" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Precios)</text>
|
||||
</g>
|
||||
<!-- service->prices -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>service->prices</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M930.39,-639.37C930.39,-639.37 930.39,-552.29 930.39,-552.29"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="933.89,-552.29 930.39,-542.29 926.89,-552.29 933.89,-552.29"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="929" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- discounts -->
|
||||
<g id="node15" class="node">
|
||||
<title>discounts</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M1026.71,-540.4C1026.71,-540.4 969.29,-540.4 969.29,-540.4 963.29,-540.4 957.29,-534.4 957.29,-528.4 957.29,-528.4 957.29,-516.4 957.29,-516.4 957.29,-510.4 963.29,-504.4 969.29,-504.4 969.29,-504.4 1026.71,-504.4 1026.71,-504.4 1032.71,-504.4 1038.71,-510.4 1038.71,-516.4 1038.71,-516.4 1038.71,-528.4 1038.71,-528.4 1038.71,-534.4 1032.71,-540.4 1026.71,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="998" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Discounts</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="998" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Descuentos)</text>
|
||||
</g>
|
||||
<!-- service->discounts -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>service->discounts</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M970.76,-639.37C970.76,-639.37 970.76,-552.29 970.76,-552.29"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="974.26,-552.29 970.76,-542.29 967.26,-552.29 974.26,-552.29"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="992" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- cart -->
|
||||
<g id="node16" class="node">
|
||||
<title>cart</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M1133.06,-540.4C1133.06,-540.4 1068.94,-540.4 1068.94,-540.4 1062.94,-540.4 1056.94,-534.4 1056.94,-528.4 1056.94,-528.4 1056.94,-516.4 1056.94,-516.4 1056.94,-510.4 1062.94,-504.4 1068.94,-504.4 1068.94,-504.4 1133.06,-504.4 1133.06,-504.4 1139.06,-504.4 1145.06,-510.4 1145.06,-516.4 1145.06,-516.4 1145.06,-528.4 1145.06,-528.4 1145.06,-534.4 1139.06,-540.4 1133.06,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1101" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">Cart / CartItem</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1101" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Carrito)</text>
|
||||
</g>
|
||||
<!-- service->cart -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>service->cart</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M984.39,-658C1027.83,-658 1101,-658 1101,-658 1101,-658 1101,-552.2 1101,-552.2"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1104.5,-552.2 1101,-542.2 1097.5,-552.2 1104.5,-552.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1089.26" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">via CartItem</text>
|
||||
</g>
|
||||
<!-- cart->petowner -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>cart->petowner</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1064.64,-504.13C1064.64,-489.36 1064.64,-471 1064.64,-471 1064.64,-471 734.47,-471 734.47,-471 734.47,-471 734.47,-492.62 734.47,-492.62"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="730.97,-492.62 734.47,-502.62 737.97,-492.62 730.97,-492.62"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="871" y="-613.5" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
||||
</g>
|
||||
<!-- cart->veterinarian -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>cart->veterinarian</title>
|
||||
<path fill="none" stroke="#2e7d32" stroke-dasharray="5,2" d="M1072.35,-504.09C1072.35,-479.26 1072.35,-438 1072.35,-438 1072.35,-438 530.06,-438 530.06,-438 530.06,-438 530.06,-417.05 530.06,-417.05"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="533.56,-417.05 530.06,-407.05 526.56,-417.05 533.56,-417.05"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="886.02" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:1 optional</text>
|
||||
</g>
|
||||
<!-- combo -->
|
||||
<g id="node17" class="node">
|
||||
<title>combo</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M985.07,-810.8C985.07,-810.8 920.93,-810.8 920.93,-810.8 914.93,-810.8 908.93,-804.8 908.93,-798.8 908.93,-798.8 908.93,-786.8 908.93,-786.8 908.93,-780.8 914.93,-774.8 920.93,-774.8 920.93,-774.8 985.07,-774.8 985.07,-774.8 991.07,-774.8 997.07,-780.8 997.07,-786.8 997.07,-786.8 997.07,-798.8 997.07,-798.8 997.07,-804.8 991.07,-810.8 985.07,-810.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-796.1" font-family="Helvetica,sans-Serif" font-size="11.00">ServiceCombo</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="953" y="-782.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Paquetes)</text>
|
||||
</g>
|
||||
<!-- combo->service -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>combo->service</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M953,-774.57C953,-774.57 953,-687.49 953,-687.49"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="956.5,-687.49 953,-677.49 949.5,-687.49 956.5,-687.49"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="969.76" y="-748.7" font-family="Helvetica,sans-Serif" font-size="9.00">contains</text>
|
||||
</g>
|
||||
<!-- servicerequest->cart -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>servicerequest->cart</title>
|
||||
<path fill="none" stroke="#c62828" d="M1113.62,-405.43C1113.62,-405.43 1113.62,-492.51 1113.62,-492.51"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1110.12,-492.51 1113.62,-502.51 1117.12,-492.51 1110.12,-492.51"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1121.26" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:1</text>
|
||||
</g>
|
||||
<!-- statehistory -->
|
||||
<g id="node19" class="node">
|
||||
<title>statehistory</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1267.95,-322.4C1267.95,-322.4 1216.05,-322.4 1216.05,-322.4 1210.05,-322.4 1204.05,-316.4 1204.05,-310.4 1204.05,-310.4 1204.05,-298.4 1204.05,-298.4 1204.05,-292.4 1210.05,-286.4 1216.05,-286.4 1216.05,-286.4 1267.95,-286.4 1267.95,-286.4 1273.95,-286.4 1279.95,-292.4 1279.95,-298.4 1279.95,-298.4 1279.95,-310.4 1279.95,-310.4 1279.95,-316.4 1273.95,-322.4 1267.95,-322.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1242" y="-307.7" font-family="Helvetica,sans-Serif" font-size="11.00">StateHistory</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1242" y="-294.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Historial)</text>
|
||||
</g>
|
||||
<!-- servicerequest->statehistory -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>servicerequest->statehistory</title>
|
||||
<path fill="none" stroke="#c62828" d="M1176.2,-378C1201.95,-378 1228.64,-378 1228.64,-378 1228.64,-378 1228.64,-334.11 1228.64,-334.11"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1232.14,-334.11 1228.64,-324.11 1225.14,-334.11 1232.14,-334.11"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1212" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- servicerequest->vetasked -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>servicerequest->vetasked</title>
|
||||
<path fill="none" stroke="#c62828" d="M1152.41,-368.82C1152.41,-368.82 1152.41,-334.24 1152.41,-334.24"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1155.91,-334.24 1152.41,-324.24 1148.91,-334.24 1155.91,-334.24"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1137" y="-343.1" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- campaign -->
|
||||
<g id="node22" class="node">
|
||||
<title>campaign</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M436.81,-52C436.81,-52 389.19,-52 389.19,-52 383.19,-52 377.19,-46 377.19,-40 377.19,-40 377.19,-28 377.19,-28 377.19,-22 383.19,-16 389.19,-16 389.19,-16 436.81,-16 436.81,-16 442.81,-16 448.81,-22 448.81,-28 448.81,-28 448.81,-40 448.81,-40 448.81,-46 442.81,-52 436.81,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Campaign</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Marketing)</text>
|
||||
</g>
|
||||
<!-- servicerequest->campaign -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>servicerequest->campaign</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M1105.59,-369.05C1105.59,-361.29 1105.59,-354 1105.59,-354 1105.59,-354 401.06,-354 401.06,-354 401.06,-354 401.06,-63.63 401.06,-63.63"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="404.56,-63.63 401.06,-53.63 397.56,-63.64 404.56,-63.63"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="437.02" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:1 optional</text>
|
||||
</g>
|
||||
<!-- tag -->
|
||||
<g id="node23" class="node">
|
||||
<title>tag</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M765.59,-52C765.59,-52 720.41,-52 720.41,-52 714.41,-52 708.41,-46 708.41,-40 708.41,-40 708.41,-28 708.41,-28 708.41,-22 714.41,-16 720.41,-16 720.41,-16 765.59,-16 765.59,-16 771.59,-16 777.59,-22 777.59,-28 777.59,-28 777.59,-40 777.59,-40 777.59,-46 771.59,-52 765.59,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="743" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Tag</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="743" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Etiquetas)</text>
|
||||
</g>
|
||||
<!-- servicerequest->tag -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>servicerequest->tag</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M1081.96,-378C984.44,-378 769.2,-378 769.2,-378 769.2,-378 769.2,-64.01 769.2,-64.01"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="772.7,-64.01 769.2,-54.01 765.7,-64.01 772.7,-64.01"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="783.25" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">N:M</text>
|
||||
</g>
|
||||
<!-- mercadopago -->
|
||||
<g id="node29" class="node">
|
||||
<title>mercadopago</title>
|
||||
<path fill="#f8bbd9" stroke="black" d="M1241.24,-540.4C1241.24,-540.4 1180.76,-540.4 1180.76,-540.4 1174.76,-540.4 1168.76,-534.4 1168.76,-528.4 1168.76,-528.4 1168.76,-516.4 1168.76,-516.4 1168.76,-510.4 1174.76,-504.4 1180.76,-504.4 1180.76,-504.4 1241.24,-504.4 1241.24,-504.4 1247.24,-504.4 1253.24,-510.4 1253.24,-516.4 1253.24,-516.4 1253.24,-528.4 1253.24,-528.4 1253.24,-534.4 1247.24,-540.4 1241.24,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1211" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">MercadoPago</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1211" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">Account</text>
|
||||
</g>
|
||||
<!-- servicerequest->mercadopago -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>servicerequest->mercadopago</title>
|
||||
<path fill="none" stroke="#ad1457" d="M1172.29,-405.43C1172.29,-405.43 1172.29,-492.51 1172.29,-492.51"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="1168.79,-492.51 1172.29,-502.51 1175.79,-492.51 1168.79,-492.51"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1205.26" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">payment</text>
|
||||
</g>
|
||||
<!-- mercately -->
|
||||
<g id="node33" class="node">
|
||||
<title>mercately</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M1523.04,-52C1523.04,-52 1472.96,-52 1472.96,-52 1466.96,-52 1460.96,-46 1460.96,-40 1460.96,-40 1460.96,-28 1460.96,-28 1460.96,-22 1466.96,-16 1472.96,-16 1472.96,-16 1523.04,-16 1523.04,-16 1529.04,-16 1535.04,-22 1535.04,-28 1535.04,-28 1535.04,-40 1535.04,-40 1535.04,-46 1529.04,-52 1523.04,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1498" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Mercately</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1498" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(WhatsApp)</text>
|
||||
</g>
|
||||
<!-- servicerequest->mercately -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>servicerequest->mercately</title>
|
||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M1176.11,-387C1275.59,-387 1498,-387 1498,-387 1498,-387 1498,-64 1498,-64"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1501.5,-64 1498,-54 1494.5,-64 1501.5,-64"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1350.76" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">notify</text>
|
||||
</g>
|
||||
<!-- reminders -->
|
||||
<g id="node21" class="node">
|
||||
<title>reminders</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1249.9,-187.2C1249.9,-187.2 1182.1,-187.2 1182.1,-187.2 1176.1,-187.2 1170.1,-181.2 1170.1,-175.2 1170.1,-175.2 1170.1,-163.2 1170.1,-163.2 1170.1,-157.2 1176.1,-151.2 1182.1,-151.2 1182.1,-151.2 1249.9,-151.2 1249.9,-151.2 1255.9,-151.2 1261.9,-157.2 1261.9,-163.2 1261.9,-163.2 1261.9,-175.2 1261.9,-175.2 1261.9,-181.2 1255.9,-187.2 1249.9,-187.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1216" y="-172.5" font-family="Helvetica,sans-Serif" font-size="11.00">Reminders</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1216" y="-159.3" font-family="Helvetica,sans-Serif" font-size="11.00">(Recordatorios)</text>
|
||||
</g>
|
||||
<!-- vetasked->reminders -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>vetasked->reminders</title>
|
||||
<path fill="none" stroke="#c62828" d="M1178.02,-286.17C1178.02,-286.17 1178.02,-199.09 1178.02,-199.09"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1181.52,-199.09 1178.02,-189.09 1174.52,-199.09 1181.52,-199.09"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1181" y="-260.3" font-family="Helvetica,sans-Serif" font-size="9.00">1:N</text>
|
||||
</g>
|
||||
<!-- celery -->
|
||||
<g id="node35" class="node">
|
||||
<title>celery</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M1332.84,-52C1332.84,-52 1271.16,-52 1271.16,-52 1265.16,-52 1259.16,-46 1259.16,-40 1259.16,-40 1259.16,-28 1259.16,-28 1259.16,-22 1265.16,-16 1271.16,-16 1271.16,-16 1332.84,-16 1332.84,-16 1338.84,-16 1344.84,-22 1344.84,-28 1344.84,-28 1344.84,-40 1344.84,-40 1344.84,-46 1338.84,-52 1332.84,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1302" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Celery</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1302" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Async Tasks)</text>
|
||||
</g>
|
||||
<!-- reminders->celery -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>reminders->celery</title>
|
||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M1250.27,-150.97C1250.27,-114.06 1250.27,-34 1250.27,-34 1250.27,-34 1251.13,-34 1251.13,-34"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1247.3,-37.5 1257.3,-34 1247.3,-30.5 1247.3,-37.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1270.52" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">scheduled</text>
|
||||
</g>
|
||||
<!-- breed -->
|
||||
<g id="node26" class="node">
|
||||
<title>breed</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M565.08,-52C565.08,-52 478.92,-52 478.92,-52 472.92,-52 466.92,-46 466.92,-40 466.92,-40 466.92,-28 466.92,-28 466.92,-22 472.92,-16 478.92,-16 478.92,-16 565.08,-16 565.08,-16 571.08,-16 577.08,-22 577.08,-28 577.08,-28 577.08,-40 577.08,-40 577.08,-46 571.08,-52 565.08,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="522" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">PetBreed / Vaccine</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="522" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">/ Study</text>
|
||||
</g>
|
||||
<!-- mpnotification -->
|
||||
<g id="node30" class="node">
|
||||
<title>mpnotification</title>
|
||||
<path fill="#f8bbd9" stroke="black" d="M1348.99,-540.4C1348.99,-540.4 1283.01,-540.4 1283.01,-540.4 1277.01,-540.4 1271.01,-534.4 1271.01,-528.4 1271.01,-528.4 1271.01,-516.4 1271.01,-516.4 1271.01,-510.4 1277.01,-504.4 1283.01,-504.4 1283.01,-504.4 1348.99,-504.4 1348.99,-504.4 1354.99,-504.4 1360.99,-510.4 1360.99,-516.4 1360.99,-516.4 1360.99,-528.4 1360.99,-528.4 1360.99,-534.4 1354.99,-540.4 1348.99,-540.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1316" y="-525.7" font-family="Helvetica,sans-Serif" font-size="11.00">MP Notification</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1316" y="-512.5" font-family="Helvetica,sans-Serif" font-size="11.00">(Webhooks)</text>
|
||||
</g>
|
||||
<!-- mpnotification->servicerequest -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>mpnotification->servicerequest</title>
|
||||
<path fill="none" stroke="#ad1457" d="M1275.48,-504.02C1275.48,-468.96 1275.48,-396 1275.48,-396 1275.48,-396 1187.67,-396 1187.67,-396"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="1187.67,-392.5 1177.67,-396 1187.67,-399.5 1187.67,-392.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1305" y="-478.3" font-family="Helvetica,sans-Serif" font-size="9.00">confirms</text>
|
||||
</g>
|
||||
<!-- google_sheets -->
|
||||
<g id="node32" class="node">
|
||||
<title>google_sheets</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M1229.39,-52C1229.39,-52 1164.61,-52 1164.61,-52 1158.61,-52 1152.61,-46 1152.61,-40 1152.61,-40 1152.61,-28 1152.61,-28 1152.61,-22 1158.61,-16 1164.61,-16 1164.61,-16 1229.39,-16 1229.39,-16 1235.39,-16 1241.39,-22 1241.39,-28 1241.39,-28 1241.39,-40 1241.39,-40 1241.39,-46 1235.39,-52 1229.39,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1197" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">Google Sheets</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1197" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Exports)</text>
|
||||
</g>
|
||||
<!-- afip -->
|
||||
<g id="node34" class="node">
|
||||
<title>afip</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M1431.4,-52C1431.4,-52 1374.6,-52 1374.6,-52 1368.6,-52 1362.6,-46 1362.6,-40 1362.6,-40 1362.6,-28 1362.6,-28 1362.6,-22 1368.6,-16 1374.6,-16 1374.6,-16 1431.4,-16 1431.4,-16 1437.4,-16 1443.4,-22 1443.4,-28 1443.4,-28 1443.4,-40 1443.4,-40 1443.4,-46 1437.4,-52 1431.4,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1403" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">AFIP</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1403" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Facturacion)</text>
|
||||
</g>
|
||||
<!-- taxpayer -->
|
||||
<g id="node37" class="node">
|
||||
<title>taxpayer</title>
|
||||
<path fill="#fff9c4" stroke="black" d="M881.91,-52C881.91,-52 814.09,-52 814.09,-52 808.09,-52 802.09,-46 802.09,-40 802.09,-40 802.09,-28 802.09,-28 802.09,-22 808.09,-16 814.09,-16 814.09,-16 881.91,-16 881.91,-16 887.91,-16 893.91,-22 893.91,-28 893.91,-28 893.91,-40 893.91,-40 893.91,-46 887.91,-52 881.91,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">TaxPayer</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="848" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Contribuyente)</text>
|
||||
</g>
|
||||
<!-- receipt->taxpayer -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>receipt->taxpayer</title>
|
||||
<path fill="none" stroke="#f9a825" d="M848,-150.97C848,-150.97 848,-63.89 848,-63.89"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="851.5,-63.89 848,-53.89 844.5,-63.89 851.5,-63.89"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="855" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
||||
</g>
|
||||
<!-- pos -->
|
||||
<g id="node38" class="node">
|
||||
<title>pos</title>
|
||||
<path fill="#fff9c4" stroke="black" d="M998.58,-52C998.58,-52 923.42,-52 923.42,-52 917.42,-52 911.42,-46 911.42,-40 911.42,-40 911.42,-28 911.42,-28 911.42,-22 917.42,-16 923.42,-16 923.42,-16 998.58,-16 998.58,-16 1004.58,-16 1010.58,-22 1010.58,-28 1010.58,-28 1010.58,-40 1010.58,-40 1010.58,-46 1004.58,-52 998.58,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="961" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">PointOfSales</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="961" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">(Punto de Venta)</text>
|
||||
</g>
|
||||
<!-- receipt->pos -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>receipt->pos</title>
|
||||
<path fill="none" stroke="#f9a825" d="M893.36,-169C910.17,-169 925.21,-169 925.21,-169 925.21,-169 925.21,-64 925.21,-64"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="928.71,-64 925.21,-54 921.71,-64 928.71,-64"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="930" y="-125.1" font-family="Helvetica,sans-Serif" font-size="9.00">N:1</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 50 KiB |
184
atlas/book/arch-model/02-frontend-architecture.dot
Normal file
184
atlas/book/arch-model/02-frontend-architecture.dot
Normal file
@@ -0,0 +1,184 @@
|
||||
digraph FrontendArchitecture {
|
||||
// Graph settings
|
||||
rankdir=TB
|
||||
compound=true
|
||||
splines=ortho
|
||||
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=11]
|
||||
edge [fontname="Helvetica", fontsize=9]
|
||||
|
||||
label="AMAR Mascotas - Frontend Architecture (Next.js)\n\n"
|
||||
labelloc="t"
|
||||
fontsize=16
|
||||
fontname="Helvetica-Bold"
|
||||
|
||||
// Next.js Core Cluster
|
||||
subgraph cluster_nextjs {
|
||||
label="Next.js 13+ (App Router)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E3F2FD"
|
||||
color="#1565C0"
|
||||
|
||||
app_router [label="App Router\n/app/*", fillcolor="#BBDEFB"]
|
||||
layout [label="Layout Components\n(RootLayout, etc)", fillcolor="#BBDEFB"]
|
||||
middleware [label="Middleware\n(Auth redirect)", fillcolor="#BBDEFB"]
|
||||
}
|
||||
|
||||
// Public Frontend Cluster
|
||||
subgraph cluster_public {
|
||||
label="Public Frontend (/(frontend))"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E8F5E9"
|
||||
color="#2E7D32"
|
||||
|
||||
home [label="Home Page\n/", fillcolor="#C8E6C9"]
|
||||
services_page [label="Services Catalog\n/servicios", fillcolor="#C8E6C9"]
|
||||
cart_page [label="Cart\n/carrito", fillcolor="#C8E6C9"]
|
||||
login_page [label="Login/Register\n/login", fillcolor="#C8E6C9"]
|
||||
profile_page [label="User Profile\n/perfil", fillcolor="#C8E6C9"]
|
||||
pets_page [label="My Pets\n/mascotas", fillcolor="#C8E6C9"]
|
||||
requests_page [label="My Requests\n/solicitudes", fillcolor="#C8E6C9"]
|
||||
}
|
||||
|
||||
// Backoffice Cluster
|
||||
subgraph cluster_backoffice {
|
||||
label="Backoffice (/(backoffice)/admin)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFF3E0"
|
||||
color="#E65100"
|
||||
|
||||
admin_dash [label="Dashboard\n/admin", fillcolor="#FFE0B2"]
|
||||
admin_visits [label="Visits Management\n/admin/visits", fillcolor="#FFE0B2"]
|
||||
admin_pets [label="Pets Overview\n/admin/pets", fillcolor="#FFE0B2"]
|
||||
admin_requests [label="Service Requests\n/admin/solicitudes", fillcolor="#FFE0B2"]
|
||||
admin_calendar [label="Calendar View\n/admin/calendario", fillcolor="#FFE0B2"]
|
||||
}
|
||||
|
||||
// Components Cluster
|
||||
subgraph cluster_components {
|
||||
label="Shared Components (/components)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#F3E5F5"
|
||||
color="#7B1FA2"
|
||||
|
||||
sidebar [label="Sidebar\nNavigation", fillcolor="#E1BEE7"]
|
||||
navbar [label="NavbarBackoffice\nTop Bar", fillcolor="#E1BEE7"]
|
||||
visits_section [label="VisitsSection\n(List + Actions)", fillcolor="#E1BEE7"]
|
||||
drawer [label="VisitsDrawer\n(Side Panel)", fillcolor="#E1BEE7"]
|
||||
tables [label="DataTable\nComponents", fillcolor="#E1BEE7"]
|
||||
forms [label="Form Components\n(Pet, Visit, etc)", fillcolor="#E1BEE7"]
|
||||
}
|
||||
|
||||
// Services Layer Cluster
|
||||
subgraph cluster_services {
|
||||
label="Services Layer (/services)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFEBEE"
|
||||
color="#C62828"
|
||||
|
||||
http_service [label="HttpService\n(Axios wrapper)", fillcolor="#FFCDD2"]
|
||||
auth_api [label="authAPI\n(login/register)", fillcolor="#FFCDD2"]
|
||||
visits_api [label="visitsAPI\n(CRUD visits)", fillcolor="#FFCDD2"]
|
||||
orders_api [label="OrdersAPI\n(service requests)", fillcolor="#FFCDD2"]
|
||||
petowners_api [label="petOwnersAPI\n(clients)", fillcolor="#FFCDD2"]
|
||||
vets_api [label="VeterinariansAPI\n(professionals)", fillcolor="#FFCDD2"]
|
||||
services_api [label="servicesAPI\n(catalog)", fillcolor="#FFCDD2"]
|
||||
cart_api [label="CartAPI\n(shopping)", fillcolor="#FFCDD2"]
|
||||
}
|
||||
|
||||
// State Management Cluster
|
||||
subgraph cluster_state {
|
||||
label="State Management (/redux, /contexts)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E0F7FA"
|
||||
color="#00838F"
|
||||
|
||||
redux_store [label="Redux Store\n(Global State)", fillcolor="#B2EBF2"]
|
||||
auth_slice [label="Auth Slice\n(user, token)", fillcolor="#B2EBF2"]
|
||||
visits_slice [label="Visits Slice\n(visit data)", fillcolor="#B2EBF2"]
|
||||
cart_slice [label="Cart Slice\n(items)", fillcolor="#B2EBF2"]
|
||||
auth_context [label="AuthContext\n(Provider)", fillcolor="#B2EBF2"]
|
||||
}
|
||||
|
||||
// Backend API Cluster (external)
|
||||
subgraph cluster_backend {
|
||||
label="Django Backend API"
|
||||
style="rounded,filled"
|
||||
fillcolor="#ECEFF1"
|
||||
color="#455A64"
|
||||
|
||||
api_mascotas [label="/mascotas/api/v1/\n(Pets, Vets, Visits)", fillcolor="#CFD8DC"]
|
||||
api_productos [label="/productos/\n(Services, Prices)", fillcolor="#CFD8DC"]
|
||||
api_solicitudes [label="/solicitudes/\n(Requests)", fillcolor="#CFD8DC"]
|
||||
api_auth [label="/api/token/\n(JWT Auth)", fillcolor="#CFD8DC"]
|
||||
api_payments [label="/payments/\n(MercadoPago)", fillcolor="#CFD8DC"]
|
||||
}
|
||||
|
||||
// User Types
|
||||
subgraph cluster_users {
|
||||
label="User Types"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FCE4EC"
|
||||
color="#AD1457"
|
||||
|
||||
petowner_user [label="PetOwner\n(Cliente)", shape=ellipse, fillcolor="#F8BBD9"]
|
||||
vet_user [label="Veterinarian\n(Profesional)", shape=ellipse, fillcolor="#F8BBD9"]
|
||||
admin_user [label="Admin/Staff\n(Interno)", shape=ellipse, fillcolor="#F8BBD9"]
|
||||
}
|
||||
|
||||
// Relationships - Router
|
||||
app_router -> layout [color="#1565C0"]
|
||||
app_router -> middleware [color="#1565C0"]
|
||||
|
||||
// Public pages
|
||||
home -> services_page [color="#2E7D32"]
|
||||
services_page -> cart_page [color="#2E7D32"]
|
||||
cart_page -> login_page [label="if not auth", color="#2E7D32", style=dashed]
|
||||
login_page -> profile_page [color="#2E7D32"]
|
||||
profile_page -> pets_page [color="#2E7D32"]
|
||||
profile_page -> requests_page [color="#2E7D32"]
|
||||
|
||||
// Backoffice pages
|
||||
admin_dash -> admin_visits [color="#E65100"]
|
||||
admin_dash -> admin_pets [color="#E65100"]
|
||||
admin_dash -> admin_requests [color="#E65100"]
|
||||
admin_dash -> admin_calendar [color="#E65100"]
|
||||
|
||||
// Components used by pages
|
||||
admin_visits -> visits_section [color="#7B1FA2", style=dashed]
|
||||
admin_visits -> drawer [color="#7B1FA2", style=dashed]
|
||||
admin_dash -> sidebar [color="#7B1FA2", style=dashed]
|
||||
admin_dash -> navbar [color="#7B1FA2", style=dashed]
|
||||
|
||||
// Services to APIs
|
||||
http_service -> auth_api [color="#C62828"]
|
||||
http_service -> visits_api [color="#C62828"]
|
||||
http_service -> orders_api [color="#C62828"]
|
||||
http_service -> petowners_api [color="#C62828"]
|
||||
http_service -> vets_api [color="#C62828"]
|
||||
http_service -> services_api [color="#C62828"]
|
||||
http_service -> cart_api [color="#C62828"]
|
||||
|
||||
// Services to Backend
|
||||
auth_api -> api_auth [label="POST /api/token/", color="#455A64"]
|
||||
visits_api -> api_mascotas [label="CRUD /vet-visits/", color="#455A64"]
|
||||
orders_api -> api_solicitudes [label="CRUD /service-requests/", color="#455A64"]
|
||||
petowners_api -> api_mascotas [label="CRUD /pet-owners/", color="#455A64"]
|
||||
vets_api -> api_mascotas [label="GET /veterinarians/", color="#455A64"]
|
||||
services_api -> api_productos [label="GET /services/", color="#455A64"]
|
||||
cart_api -> api_productos [label="CRUD /cart/", color="#455A64"]
|
||||
|
||||
// State connections
|
||||
auth_slice -> redux_store [color="#00838F"]
|
||||
visits_slice -> redux_store [color="#00838F"]
|
||||
cart_slice -> redux_store [color="#00838F"]
|
||||
auth_context -> redux_store [color="#00838F", style=dashed]
|
||||
|
||||
// User access paths
|
||||
petowner_user -> home [label="public access", color="#AD1457"]
|
||||
petowner_user -> profile_page [label="authenticated", color="#AD1457", style=dashed]
|
||||
vet_user -> admin_dash [label="backoffice", color="#AD1457"]
|
||||
admin_user -> admin_dash [label="full access", color="#AD1457"]
|
||||
|
||||
// API routing through HttpService
|
||||
http_service -> api_auth [label="JWT refresh", color="#455A64", style=dashed]
|
||||
}
|
||||
595
atlas/book/arch-model/02-frontend-architecture.svg
Normal file
595
atlas/book/arch-model/02-frontend-architecture.svg
Normal file
@@ -0,0 +1,595 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 14.0.5 (0)
|
||||
-->
|
||||
<!-- Title: FrontendArchitecture Pages: 1 -->
|
||||
<svg width="2391pt" height="715pt"
|
||||
viewBox="0.00 0.00 2391.00 715.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 710.85)">
|
||||
<title>FrontendArchitecture</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-710.85 2387,-710.85 2387,4 -4,4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1191.5" y="-687.65" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Frontend Architecture (Next.js)</text>
|
||||
<g id="clust1" class="cluster">
|
||||
<title>cluster_nextjs</title>
|
||||
<path fill="#e3f2fd" stroke="#1565c0" d="M20,-452.8C20,-452.8 230,-452.8 230,-452.8 236,-452.8 242,-458.8 242,-464.8 242,-464.8 242,-634.32 242,-634.32 242,-640.32 236,-646.32 230,-646.32 230,-646.32 20,-646.32 20,-646.32 14,-646.32 8,-640.32 8,-634.32 8,-634.32 8,-464.8 8,-464.8 8,-458.8 14,-452.8 20,-452.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="125" y="-627.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Next.js 13+ (App Router)</text>
|
||||
</g>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_public</title>
|
||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M262,-8C262,-8 454,-8 454,-8 460,-8 466,-14 466,-20 466,-20 466,-520 466,-520 466,-526 460,-532 454,-532 454,-532 262,-532 262,-532 256,-532 250,-526 250,-520 250,-520 250,-20 250,-20 250,-14 256,-8 262,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="358" y="-512.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Public Frontend (/(frontend))</text>
|
||||
</g>
|
||||
<g id="clust3" class="cluster">
|
||||
<title>cluster_backoffice</title>
|
||||
<path fill="#fff3e0" stroke="#e65100" d="M647,-344.8C647,-344.8 1095,-344.8 1095,-344.8 1101,-344.8 1107,-350.8 1107,-356.8 1107,-356.8 1107,-520 1107,-520 1107,-526 1101,-532 1095,-532 1095,-532 647,-532 647,-532 641,-532 635,-526 635,-520 635,-520 635,-356.8 635,-356.8 635,-350.8 641,-344.8 647,-344.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="871" y="-512.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Backoffice (/(backoffice)/admin)</text>
|
||||
</g>
|
||||
<g id="clust4" class="cluster">
|
||||
<title>cluster_components</title>
|
||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M486,-236.8C486,-236.8 1091,-236.8 1091,-236.8 1097,-236.8 1103,-242.8 1103,-248.8 1103,-248.8 1103,-304 1103,-304 1103,-310 1097,-316 1091,-316 1091,-316 486,-316 486,-316 480,-316 474,-310 474,-304 474,-304 474,-248.8 474,-248.8 474,-242.8 480,-236.8 486,-236.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="788.5" y="-296.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Shared Components (/components)</text>
|
||||
</g>
|
||||
<g id="clust5" class="cluster">
|
||||
<title>cluster_services</title>
|
||||
<path fill="#ffebee" stroke="#c62828" d="M1222,-452.8C1222,-452.8 1922,-452.8 1922,-452.8 1928,-452.8 1934,-458.8 1934,-464.8 1934,-464.8 1934,-634.32 1934,-634.32 1934,-640.32 1928,-646.32 1922,-646.32 1922,-646.32 1222,-646.32 1222,-646.32 1216,-646.32 1210,-640.32 1210,-634.32 1210,-634.32 1210,-464.8 1210,-464.8 1210,-458.8 1216,-452.8 1222,-452.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1572" y="-627.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Services Layer (/services)</text>
|
||||
</g>
|
||||
<g id="clust6" class="cluster">
|
||||
<title>cluster_state</title>
|
||||
<path fill="#e0f7fa" stroke="#00838f" d="M2031,-452.8C2031,-452.8 2363,-452.8 2363,-452.8 2369,-452.8 2375,-458.8 2375,-464.8 2375,-464.8 2375,-634.32 2375,-634.32 2375,-640.32 2369,-646.32 2363,-646.32 2363,-646.32 2031,-646.32 2031,-646.32 2025,-646.32 2019,-640.32 2019,-634.32 2019,-634.32 2019,-464.8 2019,-464.8 2019,-458.8 2025,-452.8 2031,-452.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2197" y="-627.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">State Management (/redux, /contexts)</text>
|
||||
</g>
|
||||
<g id="clust7" class="cluster">
|
||||
<title>cluster_backend</title>
|
||||
<path fill="#eceff1" stroke="#455a64" d="M1382,-344.8C1382,-344.8 1892,-344.8 1892,-344.8 1898,-344.8 1904,-350.8 1904,-356.8 1904,-356.8 1904,-412 1904,-412 1904,-418 1898,-424 1892,-424 1892,-424 1382,-424 1382,-424 1376,-424 1370,-418 1370,-412 1370,-412 1370,-356.8 1370,-356.8 1370,-350.8 1376,-344.8 1382,-344.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1637" y="-404.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Django Backend API</text>
|
||||
</g>
|
||||
<g id="clust8" class="cluster">
|
||||
<title>cluster_users</title>
|
||||
<path fill="#fce4ec" stroke="#ad1457" d="M824,-560.8C824,-560.8 1158,-560.8 1158,-560.8 1164,-560.8 1170,-566.8 1170,-572.8 1170,-572.8 1170,-640.65 1170,-640.65 1170,-646.65 1164,-652.65 1158,-652.65 1158,-652.65 824,-652.65 824,-652.65 818,-652.65 812,-646.65 812,-640.65 812,-640.65 812,-572.8 812,-572.8 812,-566.8 818,-560.8 824,-560.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="991" y="-633.45" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">User Types</text>
|
||||
</g>
|
||||
<!-- app_router -->
|
||||
<g id="node1" class="node">
|
||||
<title>app_router</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M155.82,-611.12C155.82,-611.12 108.18,-611.12 108.18,-611.12 102.18,-611.12 96.18,-605.12 96.18,-599.12 96.18,-599.12 96.18,-587.12 96.18,-587.12 96.18,-581.12 102.18,-575.12 108.18,-575.12 108.18,-575.12 155.82,-575.12 155.82,-575.12 161.82,-575.12 167.82,-581.12 167.82,-587.12 167.82,-587.12 167.82,-599.12 167.82,-599.12 167.82,-605.12 161.82,-611.12 155.82,-611.12"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="132" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">App Router</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="132" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">/app/*</text>
|
||||
</g>
|
||||
<!-- layout -->
|
||||
<g id="node2" class="node">
|
||||
<title>layout</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M118.23,-496.8C118.23,-496.8 27.77,-496.8 27.77,-496.8 21.77,-496.8 15.77,-490.8 15.77,-484.8 15.77,-484.8 15.77,-472.8 15.77,-472.8 15.77,-466.8 21.77,-460.8 27.77,-460.8 27.77,-460.8 118.23,-460.8 118.23,-460.8 124.23,-460.8 130.23,-466.8 130.23,-472.8 130.23,-472.8 130.23,-484.8 130.23,-484.8 130.23,-490.8 124.23,-496.8 118.23,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="73" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Layout Components</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="73" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(RootLayout, etc)</text>
|
||||
</g>
|
||||
<!-- app_router->layout -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>app_router->layout</title>
|
||||
<path fill="none" stroke="#1565c0" d="M113.2,-574.76C113.2,-574.76 113.2,-508.64 113.2,-508.64"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="116.7,-508.64 113.2,-498.64 109.7,-508.64 116.7,-508.64"/>
|
||||
</g>
|
||||
<!-- middleware -->
|
||||
<g id="node3" class="node">
|
||||
<title>middleware</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M221.84,-496.8C221.84,-496.8 160.16,-496.8 160.16,-496.8 154.16,-496.8 148.16,-490.8 148.16,-484.8 148.16,-484.8 148.16,-472.8 148.16,-472.8 148.16,-466.8 154.16,-460.8 160.16,-460.8 160.16,-460.8 221.84,-460.8 221.84,-460.8 227.84,-460.8 233.84,-466.8 233.84,-472.8 233.84,-472.8 233.84,-484.8 233.84,-484.8 233.84,-490.8 227.84,-496.8 221.84,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="191" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Middleware</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="191" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Auth redirect)</text>
|
||||
</g>
|
||||
<!-- app_router->middleware -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>app_router->middleware</title>
|
||||
<path fill="none" stroke="#1565c0" d="M157.99,-574.76C157.99,-574.76 157.99,-508.64 157.99,-508.64"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="161.49,-508.64 157.99,-498.64 154.49,-508.64 161.49,-508.64"/>
|
||||
</g>
|
||||
<!-- home -->
|
||||
<g id="node4" class="node">
|
||||
<title>home</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M442.04,-496.8C442.04,-496.8 391.96,-496.8 391.96,-496.8 385.96,-496.8 379.96,-490.8 379.96,-484.8 379.96,-484.8 379.96,-472.8 379.96,-472.8 379.96,-466.8 385.96,-460.8 391.96,-460.8 391.96,-460.8 442.04,-460.8 442.04,-460.8 448.04,-460.8 454.04,-466.8 454.04,-472.8 454.04,-472.8 454.04,-484.8 454.04,-484.8 454.04,-490.8 448.04,-496.8 442.04,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Home Page</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">/</text>
|
||||
</g>
|
||||
<!-- services_page -->
|
||||
<g id="node5" class="node">
|
||||
<title>services_page</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M445.57,-388.8C445.57,-388.8 370.43,-388.8 370.43,-388.8 364.43,-388.8 358.43,-382.8 358.43,-376.8 358.43,-376.8 358.43,-364.8 358.43,-364.8 358.43,-358.8 364.43,-352.8 370.43,-352.8 370.43,-352.8 445.57,-352.8 445.57,-352.8 451.57,-352.8 457.57,-358.8 457.57,-364.8 457.57,-364.8 457.57,-376.8 457.57,-376.8 457.57,-382.8 451.57,-388.8 445.57,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="408" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Services Catalog</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="408" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/servicios</text>
|
||||
</g>
|
||||
<!-- home->services_page -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>home->services_page</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M417,-460.48C417,-460.48 417,-400.72 417,-400.72"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="420.5,-400.72 417,-390.72 413.5,-400.72 420.5,-400.72"/>
|
||||
</g>
|
||||
<!-- cart_page -->
|
||||
<g id="node6" class="node">
|
||||
<title>cart_page</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M425,-280.8C425,-280.8 395,-280.8 395,-280.8 389,-280.8 383,-274.8 383,-268.8 383,-268.8 383,-256.8 383,-256.8 383,-250.8 389,-244.8 395,-244.8 395,-244.8 425,-244.8 425,-244.8 431,-244.8 437,-250.8 437,-256.8 437,-256.8 437,-268.8 437,-268.8 437,-274.8 431,-280.8 425,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="410" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">Cart</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="410" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">/carrito</text>
|
||||
</g>
|
||||
<!-- services_page->cart_page -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>services_page->cart_page</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M410,-352.48C410,-352.48 410,-292.72 410,-292.72"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="413.5,-292.72 410,-282.72 406.5,-292.72 413.5,-292.72"/>
|
||||
</g>
|
||||
<!-- login_page -->
|
||||
<g id="node7" class="node">
|
||||
<title>login_page</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M444.47,-198C444.47,-198 381.53,-198 381.53,-198 375.53,-198 369.53,-192 369.53,-186 369.53,-186 369.53,-174 369.53,-174 369.53,-168 375.53,-162 381.53,-162 381.53,-162 444.47,-162 444.47,-162 450.47,-162 456.47,-168 456.47,-174 456.47,-174 456.47,-186 456.47,-186 456.47,-192 450.47,-198 444.47,-198"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-183.3" font-family="Helvetica,sans-Serif" font-size="11.00">Login/Register</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="413" y="-170.1" font-family="Helvetica,sans-Serif" font-size="11.00">/login</text>
|
||||
</g>
|
||||
<!-- cart_page->login_page -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>cart_page->login_page</title>
|
||||
<path fill="none" stroke="#2e7d32" stroke-dasharray="5,2" d="M410,-244.42C410,-244.42 410,-209.84 410,-209.84"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="413.5,-209.84 410,-199.84 406.5,-209.84 413.5,-209.84"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="432.76" y="-218.7" font-family="Helvetica,sans-Serif" font-size="9.00">if not auth</text>
|
||||
</g>
|
||||
<!-- profile_page -->
|
||||
<g id="node8" class="node">
|
||||
<title>profile_page</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M438.73,-125C438.73,-125 389.27,-125 389.27,-125 383.27,-125 377.27,-119 377.27,-113 377.27,-113 377.27,-101 377.27,-101 377.27,-95 383.27,-89 389.27,-89 389.27,-89 438.73,-89 438.73,-89 444.73,-89 450.73,-95 450.73,-101 450.73,-101 450.73,-113 450.73,-113 450.73,-119 444.73,-125 438.73,-125"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="414" y="-110.3" font-family="Helvetica,sans-Serif" font-size="11.00">User Profile</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="414" y="-97.1" font-family="Helvetica,sans-Serif" font-size="11.00">/perfil</text>
|
||||
</g>
|
||||
<!-- login_page->profile_page -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>login_page->profile_page</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M414,-161.58C414,-161.58 414,-136.93 414,-136.93"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="417.5,-136.93 414,-126.93 410.5,-136.93 417.5,-136.93"/>
|
||||
</g>
|
||||
<!-- pets_page -->
|
||||
<g id="node9" class="node">
|
||||
<title>pets_page</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M347.06,-52C347.06,-52 304.94,-52 304.94,-52 298.94,-52 292.94,-46 292.94,-40 292.94,-40 292.94,-28 292.94,-28 292.94,-22 298.94,-16 304.94,-16 304.94,-16 347.06,-16 347.06,-16 353.06,-16 359.06,-22 359.06,-28 359.06,-28 359.06,-40 359.06,-40 359.06,-46 353.06,-52 347.06,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="326" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">My Pets</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="326" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">/mascotas</text>
|
||||
</g>
|
||||
<!-- profile_page->pets_page -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>profile_page->pets_page</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M376.8,-107C352.66,-107 325.68,-107 325.68,-107 325.68,-107 325.68,-63.93 325.68,-63.93"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="329.18,-63.93 325.68,-53.93 322.18,-63.93 329.18,-63.93"/>
|
||||
</g>
|
||||
<!-- requests_page -->
|
||||
<g id="node10" class="node">
|
||||
<title>requests_page</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M445.09,-52C445.09,-52 388.91,-52 388.91,-52 382.91,-52 376.91,-46 376.91,-40 376.91,-40 376.91,-28 376.91,-28 376.91,-22 382.91,-16 388.91,-16 388.91,-16 445.09,-16 445.09,-16 451.09,-16 457.09,-22 457.09,-28 457.09,-28 457.09,-40 457.09,-40 457.09,-46 451.09,-52 445.09,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-37.3" font-family="Helvetica,sans-Serif" font-size="11.00">My Requests</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="417" y="-24.1" font-family="Helvetica,sans-Serif" font-size="11.00">/solicitudes</text>
|
||||
</g>
|
||||
<!-- profile_page->requests_page -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>profile_page->requests_page</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M414,-88.58C414,-88.58 414,-63.93 414,-63.93"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="417.5,-63.93 414,-53.93 410.5,-63.93 417.5,-63.93"/>
|
||||
</g>
|
||||
<!-- admin_dash -->
|
||||
<g id="node11" class="node">
|
||||
<title>admin_dash</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M892.91,-496.8C892.91,-496.8 847.09,-496.8 847.09,-496.8 841.09,-496.8 835.09,-490.8 835.09,-484.8 835.09,-484.8 835.09,-472.8 835.09,-472.8 835.09,-466.8 841.09,-460.8 847.09,-460.8 847.09,-460.8 892.91,-460.8 892.91,-460.8 898.91,-460.8 904.91,-466.8 904.91,-472.8 904.91,-472.8 904.91,-484.8 904.91,-484.8 904.91,-490.8 898.91,-496.8 892.91,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="870" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Dashboard</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="870" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin</text>
|
||||
</g>
|
||||
<!-- admin_visits -->
|
||||
<g id="node12" class="node">
|
||||
<title>admin_visits</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M740.77,-388.8C740.77,-388.8 655.23,-388.8 655.23,-388.8 649.23,-388.8 643.23,-382.8 643.23,-376.8 643.23,-376.8 643.23,-364.8 643.23,-364.8 643.23,-358.8 649.23,-352.8 655.23,-352.8 655.23,-352.8 740.77,-352.8 740.77,-352.8 746.77,-352.8 752.77,-358.8 752.77,-364.8 752.77,-364.8 752.77,-376.8 752.77,-376.8 752.77,-382.8 746.77,-388.8 740.77,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="698" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Visits Management</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="698" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/visits</text>
|
||||
</g>
|
||||
<!-- admin_dash->admin_visits -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>admin_dash->admin_visits</title>
|
||||
<path fill="none" stroke="#e65100" d="M834.78,-468C784.46,-468 698,-468 698,-468 698,-468 698,-400.6 698,-400.6"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="701.5,-400.6 698,-390.6 694.5,-400.6 701.5,-400.6"/>
|
||||
</g>
|
||||
<!-- admin_pets -->
|
||||
<g id="node13" class="node">
|
||||
<title>admin_pets</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M845.45,-388.8C845.45,-388.8 782.55,-388.8 782.55,-388.8 776.55,-388.8 770.55,-382.8 770.55,-376.8 770.55,-376.8 770.55,-364.8 770.55,-364.8 770.55,-358.8 776.55,-352.8 782.55,-352.8 782.55,-352.8 845.45,-352.8 845.45,-352.8 851.45,-352.8 857.45,-358.8 857.45,-364.8 857.45,-364.8 857.45,-376.8 857.45,-376.8 857.45,-382.8 851.45,-388.8 845.45,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="814" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Pets Overview</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="814" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/pets</text>
|
||||
</g>
|
||||
<!-- admin_dash->admin_pets -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>admin_dash->admin_pets</title>
|
||||
<path fill="none" stroke="#e65100" d="M846.27,-460.48C846.27,-460.48 846.27,-400.72 846.27,-400.72"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="849.77,-400.72 846.27,-390.72 842.77,-400.72 849.77,-400.72"/>
|
||||
</g>
|
||||
<!-- admin_requests -->
|
||||
<g id="node14" class="node">
|
||||
<title>admin_requests</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M966.72,-388.8C966.72,-388.8 887.28,-388.8 887.28,-388.8 881.28,-388.8 875.28,-382.8 875.28,-376.8 875.28,-376.8 875.28,-364.8 875.28,-364.8 875.28,-358.8 881.28,-352.8 887.28,-352.8 887.28,-352.8 966.72,-352.8 966.72,-352.8 972.72,-352.8 978.72,-358.8 978.72,-364.8 978.72,-364.8 978.72,-376.8 978.72,-376.8 978.72,-382.8 972.72,-388.8 966.72,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="927" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Service Requests</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="927" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/solicitudes</text>
|
||||
</g>
|
||||
<!-- admin_dash->admin_requests -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>admin_dash->admin_requests</title>
|
||||
<path fill="none" stroke="#e65100" d="M890.1,-460.48C890.1,-460.48 890.1,-400.72 890.1,-400.72"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="893.6,-400.72 890.1,-390.72 886.6,-400.72 893.6,-400.72"/>
|
||||
</g>
|
||||
<!-- admin_calendar -->
|
||||
<g id="node15" class="node">
|
||||
<title>admin_calendar</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M1087.41,-388.8C1087.41,-388.8 1008.59,-388.8 1008.59,-388.8 1002.59,-388.8 996.59,-382.8 996.59,-376.8 996.59,-376.8 996.59,-364.8 996.59,-364.8 996.59,-358.8 1002.59,-352.8 1008.59,-352.8 1008.59,-352.8 1087.41,-352.8 1087.41,-352.8 1093.41,-352.8 1099.41,-358.8 1099.41,-364.8 1099.41,-364.8 1099.41,-376.8 1099.41,-376.8 1099.41,-382.8 1093.41,-388.8 1087.41,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1048" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">Calendar View</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1048" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">/admin/calendario</text>
|
||||
</g>
|
||||
<!-- admin_dash->admin_calendar -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>admin_dash->admin_calendar</title>
|
||||
<path fill="none" stroke="#e65100" d="M905.19,-470C949.1,-470 1018.5,-470 1018.5,-470 1018.5,-470 1018.5,-400.51 1018.5,-400.51"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1022,-400.51 1018.5,-390.51 1015,-400.51 1022,-400.51"/>
|
||||
</g>
|
||||
<!-- sidebar -->
|
||||
<g id="node16" class="node">
|
||||
<title>sidebar</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M537.99,-280.8C537.99,-280.8 494.01,-280.8 494.01,-280.8 488.01,-280.8 482.01,-274.8 482.01,-268.8 482.01,-268.8 482.01,-256.8 482.01,-256.8 482.01,-250.8 488.01,-244.8 494.01,-244.8 494.01,-244.8 537.99,-244.8 537.99,-244.8 543.99,-244.8 549.99,-250.8 549.99,-256.8 549.99,-256.8 549.99,-268.8 549.99,-268.8 549.99,-274.8 543.99,-280.8 537.99,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="516" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">Sidebar</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="516" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">Navigation</text>
|
||||
</g>
|
||||
<!-- admin_dash->sidebar -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>admin_dash->sidebar</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M834.76,-482C745.22,-482 516,-482 516,-482 516,-482 516,-292.63 516,-292.63"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="519.5,-292.63 516,-282.63 512.5,-292.63 519.5,-292.63"/>
|
||||
</g>
|
||||
<!-- navbar -->
|
||||
<g id="node17" class="node">
|
||||
<title>navbar</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M658.1,-280.8C658.1,-280.8 579.9,-280.8 579.9,-280.8 573.9,-280.8 567.9,-274.8 567.9,-268.8 567.9,-268.8 567.9,-256.8 567.9,-256.8 567.9,-250.8 573.9,-244.8 579.9,-244.8 579.9,-244.8 658.1,-244.8 658.1,-244.8 664.1,-244.8 670.1,-250.8 670.1,-256.8 670.1,-256.8 670.1,-268.8 670.1,-268.8 670.1,-274.8 664.1,-280.8 658.1,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="619" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">NavbarBackoffice</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="619" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">Top Bar</text>
|
||||
</g>
|
||||
<!-- admin_dash->navbar -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>admin_dash->navbar</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M834.61,-475C762.55,-475 605.56,-475 605.56,-475 605.56,-475 605.56,-292.77 605.56,-292.77"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="609.06,-292.77 605.56,-282.77 602.06,-292.77 609.06,-292.77"/>
|
||||
</g>
|
||||
<!-- visits_section -->
|
||||
<g id="node18" class="node">
|
||||
<title>visits_section</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M765.53,-280.8C765.53,-280.8 700.47,-280.8 700.47,-280.8 694.47,-280.8 688.47,-274.8 688.47,-268.8 688.47,-268.8 688.47,-256.8 688.47,-256.8 688.47,-250.8 694.47,-244.8 700.47,-244.8 700.47,-244.8 765.53,-244.8 765.53,-244.8 771.53,-244.8 777.53,-250.8 777.53,-256.8 777.53,-256.8 777.53,-268.8 777.53,-268.8 777.53,-274.8 771.53,-280.8 765.53,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="733" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">VisitsSection</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="733" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">(List + Actions)</text>
|
||||
</g>
|
||||
<!-- admin_visits->visits_section -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>admin_visits->visits_section</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M709.91,-352.48C709.91,-352.48 709.91,-292.72 709.91,-292.72"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="713.41,-292.72 709.91,-282.72 706.41,-292.72 713.41,-292.72"/>
|
||||
</g>
|
||||
<!-- drawer -->
|
||||
<g id="node19" class="node">
|
||||
<title>drawer</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M860.86,-280.8C860.86,-280.8 807.14,-280.8 807.14,-280.8 801.14,-280.8 795.14,-274.8 795.14,-268.8 795.14,-268.8 795.14,-256.8 795.14,-256.8 795.14,-250.8 801.14,-244.8 807.14,-244.8 807.14,-244.8 860.86,-244.8 860.86,-244.8 866.86,-244.8 872.86,-250.8 872.86,-256.8 872.86,-256.8 872.86,-268.8 872.86,-268.8 872.86,-274.8 866.86,-280.8 860.86,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="834" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">VisitsDrawer</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="834" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Side Panel)</text>
|
||||
</g>
|
||||
<!-- admin_visits->drawer -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>admin_visits->drawer</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M731.34,-352.59C731.34,-336.98 731.34,-317 731.34,-317 731.34,-317 826.29,-317 826.29,-317 826.29,-317 826.29,-292.66 826.29,-292.66"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="829.79,-292.66 826.29,-282.66 822.79,-292.66 829.79,-292.66"/>
|
||||
</g>
|
||||
<!-- tables -->
|
||||
<g id="node20" class="node">
|
||||
<title>tables</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M957.18,-280.8C957.18,-280.8 902.82,-280.8 902.82,-280.8 896.82,-280.8 890.82,-274.8 890.82,-268.8 890.82,-268.8 890.82,-256.8 890.82,-256.8 890.82,-250.8 896.82,-244.8 902.82,-244.8 902.82,-244.8 957.18,-244.8 957.18,-244.8 963.18,-244.8 969.18,-250.8 969.18,-256.8 969.18,-256.8 969.18,-268.8 969.18,-268.8 969.18,-274.8 963.18,-280.8 957.18,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="930" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">DataTable</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="930" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">Components</text>
|
||||
</g>
|
||||
<!-- forms -->
|
||||
<g id="node21" class="node">
|
||||
<title>forms</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M1082.54,-280.8C1082.54,-280.8 999.46,-280.8 999.46,-280.8 993.46,-280.8 987.46,-274.8 987.46,-268.8 987.46,-268.8 987.46,-256.8 987.46,-256.8 987.46,-250.8 993.46,-244.8 999.46,-244.8 999.46,-244.8 1082.54,-244.8 1082.54,-244.8 1088.54,-244.8 1094.54,-250.8 1094.54,-256.8 1094.54,-256.8 1094.54,-268.8 1094.54,-268.8 1094.54,-274.8 1088.54,-280.8 1082.54,-280.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1041" y="-266.1" font-family="Helvetica,sans-Serif" font-size="11.00">Form Components</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1041" y="-252.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Pet, Visit, etc)</text>
|
||||
</g>
|
||||
<!-- http_service -->
|
||||
<g id="node22" class="node">
|
||||
<title>http_service</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1644.51,-611.12C1644.51,-611.12 1575.49,-611.12 1575.49,-611.12 1569.49,-611.12 1563.49,-605.12 1563.49,-599.12 1563.49,-599.12 1563.49,-587.12 1563.49,-587.12 1563.49,-581.12 1569.49,-575.12 1575.49,-575.12 1575.49,-575.12 1644.51,-575.12 1644.51,-575.12 1650.51,-575.12 1656.51,-581.12 1656.51,-587.12 1656.51,-587.12 1656.51,-599.12 1656.51,-599.12 1656.51,-605.12 1650.51,-611.12 1644.51,-611.12"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1610" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">HttpService</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1610" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Axios wrapper)</text>
|
||||
</g>
|
||||
<!-- auth_api -->
|
||||
<g id="node23" class="node">
|
||||
<title>auth_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1914.15,-496.8C1914.15,-496.8 1851.85,-496.8 1851.85,-496.8 1845.85,-496.8 1839.85,-490.8 1839.85,-484.8 1839.85,-484.8 1839.85,-472.8 1839.85,-472.8 1839.85,-466.8 1845.85,-460.8 1851.85,-460.8 1851.85,-460.8 1914.15,-460.8 1914.15,-460.8 1920.15,-460.8 1926.15,-466.8 1926.15,-472.8 1926.15,-472.8 1926.15,-484.8 1926.15,-484.8 1926.15,-490.8 1920.15,-496.8 1914.15,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1883" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">authAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1883" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(login/register)</text>
|
||||
</g>
|
||||
<!-- http_service->auth_api -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>http_service->auth_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1656.93,-602C1734.99,-602 1883,-602 1883,-602 1883,-602 1883,-508.76 1883,-508.76"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1886.5,-508.76 1883,-498.76 1879.5,-508.76 1886.5,-508.76"/>
|
||||
</g>
|
||||
<!-- visits_api -->
|
||||
<g id="node24" class="node">
|
||||
<title>visits_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1810.3,-496.8C1810.3,-496.8 1751.7,-496.8 1751.7,-496.8 1745.7,-496.8 1739.7,-490.8 1739.7,-484.8 1739.7,-484.8 1739.7,-472.8 1739.7,-472.8 1739.7,-466.8 1745.7,-460.8 1751.7,-460.8 1751.7,-460.8 1810.3,-460.8 1810.3,-460.8 1816.3,-460.8 1822.3,-466.8 1822.3,-472.8 1822.3,-472.8 1822.3,-484.8 1822.3,-484.8 1822.3,-490.8 1816.3,-496.8 1810.3,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1781" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">visitsAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1781" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(CRUD visits)</text>
|
||||
</g>
|
||||
<!-- http_service->visits_api -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>http_service->visits_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1656.73,-593C1707.36,-593 1781,-593 1781,-593 1781,-593 1781,-508.66 1781,-508.66"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1784.5,-508.66 1781,-498.66 1777.5,-508.66 1784.5,-508.66"/>
|
||||
</g>
|
||||
<!-- orders_api -->
|
||||
<g id="node25" class="node">
|
||||
<title>orders_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1309.71,-496.8C1309.71,-496.8 1230.29,-496.8 1230.29,-496.8 1224.29,-496.8 1218.29,-490.8 1218.29,-484.8 1218.29,-484.8 1218.29,-472.8 1218.29,-472.8 1218.29,-466.8 1224.29,-460.8 1230.29,-460.8 1230.29,-460.8 1309.71,-460.8 1309.71,-460.8 1315.71,-460.8 1321.71,-466.8 1321.71,-472.8 1321.71,-472.8 1321.71,-484.8 1321.71,-484.8 1321.71,-490.8 1315.71,-496.8 1309.71,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1270" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">OrdersAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1270" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(service requests)</text>
|
||||
</g>
|
||||
<!-- http_service->orders_api -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>http_service->orders_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1563.08,-602C1469.81,-602 1270,-602 1270,-602 1270,-602 1270,-508.76 1270,-508.76"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1273.5,-508.76 1270,-498.76 1266.5,-508.76 1273.5,-508.76"/>
|
||||
</g>
|
||||
<!-- petowners_api -->
|
||||
<g id="node26" class="node">
|
||||
<title>petowners_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1709.46,-496.8C1709.46,-496.8 1646.54,-496.8 1646.54,-496.8 1640.54,-496.8 1634.54,-490.8 1634.54,-484.8 1634.54,-484.8 1634.54,-472.8 1634.54,-472.8 1634.54,-466.8 1640.54,-460.8 1646.54,-460.8 1646.54,-460.8 1709.46,-460.8 1709.46,-460.8 1715.46,-460.8 1721.46,-466.8 1721.46,-472.8 1721.46,-472.8 1721.46,-484.8 1721.46,-484.8 1721.46,-490.8 1715.46,-496.8 1709.46,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">petOwnersAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(clients)</text>
|
||||
</g>
|
||||
<!-- http_service->petowners_api -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>http_service->petowners_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1645.53,-574.76C1645.53,-574.76 1645.53,-508.64 1645.53,-508.64"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1649.03,-508.64 1645.53,-498.64 1642.03,-508.64 1649.03,-508.64"/>
|
||||
</g>
|
||||
<!-- vets_api -->
|
||||
<g id="node27" class="node">
|
||||
<title>vets_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1604.27,-496.8C1604.27,-496.8 1529.73,-496.8 1529.73,-496.8 1523.73,-496.8 1517.73,-490.8 1517.73,-484.8 1517.73,-484.8 1517.73,-472.8 1517.73,-472.8 1517.73,-466.8 1523.73,-460.8 1529.73,-460.8 1529.73,-460.8 1604.27,-460.8 1604.27,-460.8 1610.27,-460.8 1616.27,-466.8 1616.27,-472.8 1616.27,-472.8 1616.27,-484.8 1616.27,-484.8 1616.27,-490.8 1610.27,-496.8 1604.27,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1567" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">VeterinariansAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1567" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(professionals)</text>
|
||||
</g>
|
||||
<!-- http_service->vets_api -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>http_service->vets_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1589.88,-574.76C1589.88,-574.76 1589.88,-508.64 1589.88,-508.64"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1593.38,-508.64 1589.88,-498.64 1586.38,-508.64 1593.38,-508.64"/>
|
||||
</g>
|
||||
<!-- services_api -->
|
||||
<g id="node28" class="node">
|
||||
<title>services_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1488.04,-496.8C1488.04,-496.8 1437.96,-496.8 1437.96,-496.8 1431.96,-496.8 1425.96,-490.8 1425.96,-484.8 1425.96,-484.8 1425.96,-472.8 1425.96,-472.8 1425.96,-466.8 1431.96,-460.8 1437.96,-460.8 1437.96,-460.8 1488.04,-460.8 1488.04,-460.8 1494.04,-460.8 1500.04,-466.8 1500.04,-472.8 1500.04,-472.8 1500.04,-484.8 1500.04,-484.8 1500.04,-490.8 1494.04,-496.8 1488.04,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1463" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">servicesAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1463" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(catalog)</text>
|
||||
</g>
|
||||
<!-- http_service->services_api -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>http_service->services_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1563.08,-584C1520.06,-584 1463,-584 1463,-584 1463,-584 1463,-508.64 1463,-508.64"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1466.5,-508.64 1463,-498.64 1459.5,-508.64 1466.5,-508.64"/>
|
||||
</g>
|
||||
<!-- cart_api -->
|
||||
<g id="node29" class="node">
|
||||
<title>cart_api</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M1395.99,-496.8C1395.99,-496.8 1352.01,-496.8 1352.01,-496.8 1346.01,-496.8 1340.01,-490.8 1340.01,-484.8 1340.01,-484.8 1340.01,-472.8 1340.01,-472.8 1340.01,-466.8 1346.01,-460.8 1352.01,-460.8 1352.01,-460.8 1395.99,-460.8 1395.99,-460.8 1401.99,-460.8 1407.99,-466.8 1407.99,-472.8 1407.99,-472.8 1407.99,-484.8 1407.99,-484.8 1407.99,-490.8 1401.99,-496.8 1395.99,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1374" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">CartAPI</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1374" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(shopping)</text>
|
||||
</g>
|
||||
<!-- http_service->cart_api -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>http_service->cart_api</title>
|
||||
<path fill="none" stroke="#c62828" d="M1563.09,-593C1494.27,-593 1374,-593 1374,-593 1374,-593 1374,-508.66 1374,-508.66"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1377.5,-508.66 1374,-498.66 1370.5,-508.66 1377.5,-508.66"/>
|
||||
</g>
|
||||
<!-- api_auth -->
|
||||
<g id="node38" class="node">
|
||||
<title>api_auth</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1773.81,-388.8C1773.81,-388.8 1726.19,-388.8 1726.19,-388.8 1720.19,-388.8 1714.19,-382.8 1714.19,-376.8 1714.19,-376.8 1714.19,-364.8 1714.19,-364.8 1714.19,-358.8 1720.19,-352.8 1726.19,-352.8 1726.19,-352.8 1773.81,-352.8 1773.81,-352.8 1779.81,-352.8 1785.81,-358.8 1785.81,-364.8 1785.81,-364.8 1785.81,-376.8 1785.81,-376.8 1785.81,-382.8 1779.81,-388.8 1773.81,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1750" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/api/token/</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1750" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(JWT Auth)</text>
|
||||
</g>
|
||||
<!-- http_service->api_auth -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>http_service->api_auth</title>
|
||||
<path fill="none" stroke="#455a64" stroke-dasharray="5,2" d="M1656.96,-584C1690.82,-584 1730.58,-584 1730.58,-584 1730.58,-584 1730.58,-400.49 1730.58,-400.49"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1734.08,-400.49 1730.58,-390.49 1727.08,-400.49 1734.08,-400.49"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1986.5" y="-476.1" font-family="Helvetica,sans-Serif" font-size="9.00">JWT refresh</text>
|
||||
</g>
|
||||
<!-- auth_api->api_auth -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>auth_api->api_auth</title>
|
||||
<path fill="none" stroke="#455a64" d="M1867.88,-460.31C1867.88,-439.04 1867.88,-407 1867.88,-407 1867.88,-407 1770.44,-407 1770.44,-407 1770.44,-407 1770.44,-400.41 1770.44,-400.41"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1773.94,-400.41 1770.44,-390.41 1766.94,-400.41 1773.94,-400.41"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1909.27" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">POST /api/token/</text>
|
||||
</g>
|
||||
<!-- api_mascotas -->
|
||||
<g id="node35" class="node">
|
||||
<title>api_mascotas</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1683.93,-388.8C1683.93,-388.8 1602.07,-388.8 1602.07,-388.8 1596.07,-388.8 1590.07,-382.8 1590.07,-376.8 1590.07,-376.8 1590.07,-364.8 1590.07,-364.8 1590.07,-358.8 1596.07,-352.8 1602.07,-352.8 1602.07,-352.8 1683.93,-352.8 1683.93,-352.8 1689.93,-352.8 1695.93,-358.8 1695.93,-364.8 1695.93,-364.8 1695.93,-376.8 1695.93,-376.8 1695.93,-382.8 1689.93,-388.8 1683.93,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1643" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/mascotas/api/v1/</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1643" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Pets, Vets, Visits)</text>
|
||||
</g>
|
||||
<!-- visits_api->api_mascotas -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>visits_api->api_mascotas</title>
|
||||
<path fill="none" stroke="#455a64" d="M1755.07,-460.59C1755.07,-444.98 1755.07,-425 1755.07,-425 1755.07,-425 1675.46,-425 1675.46,-425 1675.46,-425 1675.46,-400.66 1675.46,-400.66"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1678.96,-400.66 1675.46,-390.66 1671.96,-400.66 1678.96,-400.66"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1814.25" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /vet-visits/</text>
|
||||
</g>
|
||||
<!-- api_solicitudes -->
|
||||
<g id="node37" class="node">
|
||||
<title>api_solicitudes</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1439.74,-388.8C1439.74,-388.8 1390.26,-388.8 1390.26,-388.8 1384.26,-388.8 1378.26,-382.8 1378.26,-376.8 1378.26,-376.8 1378.26,-364.8 1378.26,-364.8 1378.26,-358.8 1384.26,-352.8 1390.26,-352.8 1390.26,-352.8 1439.74,-352.8 1439.74,-352.8 1445.74,-352.8 1451.74,-358.8 1451.74,-364.8 1451.74,-364.8 1451.74,-376.8 1451.74,-376.8 1451.74,-382.8 1445.74,-388.8 1439.74,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1415" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/solicitudes/</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1415" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Requests)</text>
|
||||
</g>
|
||||
<!-- orders_api->api_solicitudes -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>orders_api->api_solicitudes</title>
|
||||
<path fill="none" stroke="#455a64" d="M1270,-460.51C1270,-429.76 1270,-371 1270,-371 1270,-371 1366.52,-371 1366.52,-371"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1366.52,-374.5 1376.52,-371 1366.52,-367.5 1366.52,-374.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1320.76" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /service-requests/</text>
|
||||
</g>
|
||||
<!-- petowners_api->api_mascotas -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>petowners_api->api_mascotas</title>
|
||||
<path fill="none" stroke="#455a64" d="M1655,-460.48C1655,-460.48 1655,-400.72 1655,-400.72"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1658.5,-400.72 1655,-390.72 1651.5,-400.72 1658.5,-400.72"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1715.01" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /pet-owners/</text>
|
||||
</g>
|
||||
<!-- vets_api->api_mascotas -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>vets_api->api_mascotas</title>
|
||||
<path fill="none" stroke="#455a64" d="M1603.17,-460.48C1603.17,-460.48 1603.17,-400.72 1603.17,-400.72"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1606.67,-400.72 1603.17,-390.72 1599.67,-400.72 1606.67,-400.72"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1609.76" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">GET /veterinarians/</text>
|
||||
</g>
|
||||
<!-- api_productos -->
|
||||
<g id="node36" class="node">
|
||||
<title>api_productos</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1560.09,-388.8C1560.09,-388.8 1481.91,-388.8 1481.91,-388.8 1475.91,-388.8 1469.91,-382.8 1469.91,-376.8 1469.91,-376.8 1469.91,-364.8 1469.91,-364.8 1469.91,-358.8 1475.91,-352.8 1481.91,-352.8 1481.91,-352.8 1560.09,-352.8 1560.09,-352.8 1566.09,-352.8 1572.09,-358.8 1572.09,-364.8 1572.09,-364.8 1572.09,-376.8 1572.09,-376.8 1572.09,-382.8 1566.09,-388.8 1560.09,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1521" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/productos/</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1521" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Services, Prices)</text>
|
||||
</g>
|
||||
<!-- services_api->api_productos -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>services_api->api_productos</title>
|
||||
<path fill="none" stroke="#455a64" d="M1489.99,-460.48C1489.99,-460.48 1489.99,-400.72 1489.99,-400.72"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1493.49,-400.72 1489.99,-390.72 1486.49,-400.72 1493.49,-400.72"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1517.5" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">GET /services/</text>
|
||||
</g>
|
||||
<!-- cart_api->api_productos -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>cart_api->api_productos</title>
|
||||
<path fill="none" stroke="#455a64" d="M1393.13,-460.48C1393.13,-451.67 1393.13,-443 1393.13,-443 1393.13,-443 1479.95,-443 1479.95,-443 1479.95,-443 1479.95,-400.53 1479.95,-400.53"/>
|
||||
<polygon fill="#455a64" stroke="#455a64" points="1483.45,-400.53 1479.95,-390.53 1476.45,-400.53 1483.45,-400.53"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1433.25" y="-434.7" font-family="Helvetica,sans-Serif" font-size="9.00">CRUD /cart/</text>
|
||||
</g>
|
||||
<!-- redux_store -->
|
||||
<g id="node30" class="node">
|
||||
<title>redux_store</title>
|
||||
<path fill="#b2ebf2" stroke="black" d="M2228.93,-496.8C2228.93,-496.8 2169.07,-496.8 2169.07,-496.8 2163.07,-496.8 2157.07,-490.8 2157.07,-484.8 2157.07,-484.8 2157.07,-472.8 2157.07,-472.8 2157.07,-466.8 2163.07,-460.8 2169.07,-460.8 2169.07,-460.8 2228.93,-460.8 2228.93,-460.8 2234.93,-460.8 2240.93,-466.8 2240.93,-472.8 2240.93,-472.8 2240.93,-484.8 2240.93,-484.8 2240.93,-490.8 2234.93,-496.8 2228.93,-496.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2199" y="-482.1" font-family="Helvetica,sans-Serif" font-size="11.00">Redux Store</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2199" y="-468.9" font-family="Helvetica,sans-Serif" font-size="11.00">(Global State)</text>
|
||||
</g>
|
||||
<!-- auth_slice -->
|
||||
<g id="node31" class="node">
|
||||
<title>auth_slice</title>
|
||||
<path fill="#b2ebf2" stroke="black" d="M2092.87,-611.12C2092.87,-611.12 2039.13,-611.12 2039.13,-611.12 2033.13,-611.12 2027.13,-605.12 2027.13,-599.12 2027.13,-599.12 2027.13,-587.12 2027.13,-587.12 2027.13,-581.12 2033.13,-575.12 2039.13,-575.12 2039.13,-575.12 2092.87,-575.12 2092.87,-575.12 2098.87,-575.12 2104.87,-581.12 2104.87,-587.12 2104.87,-587.12 2104.87,-599.12 2104.87,-599.12 2104.87,-605.12 2098.87,-611.12 2092.87,-611.12"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2066" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Auth Slice</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2066" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(user, token)</text>
|
||||
</g>
|
||||
<!-- auth_slice->redux_store -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>auth_slice->redux_store</title>
|
||||
<path fill="none" stroke="#00838f" d="M2066,-574.79C2066,-542.53 2066,-479 2066,-479 2066,-479 2145.4,-479 2145.4,-479"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="2145.4,-482.5 2155.4,-479 2145.4,-475.5 2145.4,-482.5"/>
|
||||
</g>
|
||||
<!-- visits_slice -->
|
||||
<g id="node32" class="node">
|
||||
<title>visits_slice</title>
|
||||
<path fill="#b2ebf2" stroke="black" d="M2179.59,-611.12C2179.59,-611.12 2134.41,-611.12 2134.41,-611.12 2128.41,-611.12 2122.41,-605.12 2122.41,-599.12 2122.41,-599.12 2122.41,-587.12 2122.41,-587.12 2122.41,-581.12 2128.41,-575.12 2134.41,-575.12 2134.41,-575.12 2179.59,-575.12 2179.59,-575.12 2185.59,-575.12 2191.59,-581.12 2191.59,-587.12 2191.59,-587.12 2191.59,-599.12 2191.59,-599.12 2191.59,-605.12 2185.59,-611.12 2179.59,-611.12"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2157" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Visits Slice</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2157" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(visit data)</text>
|
||||
</g>
|
||||
<!-- visits_slice->redux_store -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>visits_slice->redux_store</title>
|
||||
<path fill="none" stroke="#00838f" d="M2174.33,-574.76C2174.33,-574.76 2174.33,-508.64 2174.33,-508.64"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="2177.83,-508.64 2174.33,-498.64 2170.83,-508.64 2177.83,-508.64"/>
|
||||
</g>
|
||||
<!-- cart_slice -->
|
||||
<g id="node33" class="node">
|
||||
<title>cart_slice</title>
|
||||
<path fill="#b2ebf2" stroke="black" d="M2260.84,-611.12C2260.84,-611.12 2221.16,-611.12 2221.16,-611.12 2215.16,-611.12 2209.16,-605.12 2209.16,-599.12 2209.16,-599.12 2209.16,-587.12 2209.16,-587.12 2209.16,-581.12 2215.16,-575.12 2221.16,-575.12 2221.16,-575.12 2260.84,-575.12 2260.84,-575.12 2266.84,-575.12 2272.84,-581.12 2272.84,-587.12 2272.84,-587.12 2272.84,-599.12 2272.84,-599.12 2272.84,-605.12 2266.84,-611.12 2260.84,-611.12"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2241" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Cart Slice</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2241" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(items)</text>
|
||||
</g>
|
||||
<!-- cart_slice->redux_store -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>cart_slice->redux_store</title>
|
||||
<path fill="none" stroke="#00838f" d="M2225.05,-574.76C2225.05,-574.76 2225.05,-508.64 2225.05,-508.64"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="2228.55,-508.64 2225.05,-498.64 2221.55,-508.64 2228.55,-508.64"/>
|
||||
</g>
|
||||
<!-- auth_context -->
|
||||
<g id="node34" class="node">
|
||||
<title>auth_context</title>
|
||||
<path fill="#b2ebf2" stroke="black" d="M2355.27,-611.12C2355.27,-611.12 2302.73,-611.12 2302.73,-611.12 2296.73,-611.12 2290.73,-605.12 2290.73,-599.12 2290.73,-599.12 2290.73,-587.12 2290.73,-587.12 2290.73,-581.12 2296.73,-575.12 2302.73,-575.12 2302.73,-575.12 2355.27,-575.12 2355.27,-575.12 2361.27,-575.12 2367.27,-581.12 2367.27,-587.12 2367.27,-587.12 2367.27,-599.12 2367.27,-599.12 2367.27,-605.12 2361.27,-611.12 2355.27,-611.12"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2329" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">AuthContext</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2329" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Provider)</text>
|
||||
</g>
|
||||
<!-- auth_context->redux_store -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>auth_context->redux_store</title>
|
||||
<path fill="none" stroke="#00838f" stroke-dasharray="5,2" d="M2329,-574.79C2329,-542.53 2329,-479 2329,-479 2329,-479 2252.72,-479 2252.72,-479"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="2252.72,-475.5 2242.72,-479 2252.72,-482.5 2252.72,-475.5"/>
|
||||
</g>
|
||||
<!-- api_payments -->
|
||||
<g id="node39" class="node">
|
||||
<title>api_payments</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1883.91,-388.8C1883.91,-388.8 1816.09,-388.8 1816.09,-388.8 1810.09,-388.8 1804.09,-382.8 1804.09,-376.8 1804.09,-376.8 1804.09,-364.8 1804.09,-364.8 1804.09,-358.8 1810.09,-352.8 1816.09,-352.8 1816.09,-352.8 1883.91,-352.8 1883.91,-352.8 1889.91,-352.8 1895.91,-358.8 1895.91,-364.8 1895.91,-364.8 1895.91,-376.8 1895.91,-376.8 1895.91,-382.8 1889.91,-388.8 1883.91,-388.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1850" y="-374.1" font-family="Helvetica,sans-Serif" font-size="11.00">/payments/</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1850" y="-360.9" font-family="Helvetica,sans-Serif" font-size="11.00">(MercadoPago)</text>
|
||||
</g>
|
||||
<!-- petowner_user -->
|
||||
<g id="node40" class="node">
|
||||
<title>petowner_user</title>
|
||||
<ellipse fill="#f8bbd9" stroke="black" cx="866" cy="-593.12" rx="45.9" ry="24.32"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="866" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">PetOwner</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="866" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Cliente)</text>
|
||||
</g>
|
||||
<!-- petowner_user->home -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>petowner_user->home</title>
|
||||
<path fill="none" stroke="#ad1457" d="M827.6,-579.22C827.6,-551.07 827.6,-490 827.6,-490 827.6,-490 466.04,-490 466.04,-490"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="466.04,-486.5 456.04,-490 466.04,-493.5 466.04,-486.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="830.01" y="-542.7" font-family="Helvetica,sans-Serif" font-size="9.00">public access</text>
|
||||
</g>
|
||||
<!-- petowner_user->profile_page -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>petowner_user->profile_page</title>
|
||||
<path fill="none" stroke="#ad1457" stroke-dasharray="5,2" d="M819.7,-593C733.92,-593 558.94,-593 558.94,-593 558.94,-593 558.94,-107 558.94,-107 558.94,-107 462.46,-107 462.46,-107"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="462.46,-103.5 452.46,-107 462.46,-110.5 462.46,-103.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1154.02" y="-326.7" font-family="Helvetica,sans-Serif" font-size="9.00">authenticated</text>
|
||||
</g>
|
||||
<!-- vet_user -->
|
||||
<g id="node41" class="node">
|
||||
<title>vet_user</title>
|
||||
<ellipse fill="#f8bbd9" stroke="black" cx="985" cy="-593.12" rx="55.41" ry="24.32"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="985" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Veterinarian</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="985" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Profesional)</text>
|
||||
</g>
|
||||
<!-- vet_user->admin_dash -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>vet_user->admin_dash</title>
|
||||
<path fill="none" stroke="#ad1457" d="M954.15,-572.61C954.15,-542.01 954.15,-488 954.15,-488 954.15,-488 916.71,-488 916.71,-488"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="916.71,-484.5 906.71,-488 916.71,-491.5 916.71,-484.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="921.26" y="-542.7" font-family="Helvetica,sans-Serif" font-size="9.00">backoffice</text>
|
||||
</g>
|
||||
<!-- admin_user -->
|
||||
<g id="node42" class="node">
|
||||
<title>admin_user</title>
|
||||
<ellipse fill="#f8bbd9" stroke="black" cx="1110" cy="-593.12" rx="51.52" ry="24.32"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1110" y="-596.42" font-family="Helvetica,sans-Serif" font-size="11.00">Admin/Staff</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1110" y="-583.22" font-family="Helvetica,sans-Serif" font-size="11.00">(Interno)</text>
|
||||
</g>
|
||||
<!-- admin_user->admin_dash -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>admin_user->admin_dash</title>
|
||||
<path fill="none" stroke="#ad1457" d="M1078.95,-573.25C1078.95,-540.55 1078.95,-479 1078.95,-479 1078.95,-479 916.49,-479 916.49,-479"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="916.49,-475.5 906.49,-479 916.49,-482.5 916.49,-475.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1181.01" y="-542.7" font-family="Helvetica,sans-Serif" font-size="9.00">full access</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 51 KiB |
234
atlas/book/arch-model/03-data-model.dot
Normal file
234
atlas/book/arch-model/03-data-model.dot
Normal file
@@ -0,0 +1,234 @@
|
||||
digraph DataModel {
|
||||
// Graph settings
|
||||
rankdir=TB
|
||||
compound=true
|
||||
splines=ortho
|
||||
node [shape=record, style="filled", fontname="Helvetica", fontsize=10]
|
||||
edge [fontname="Helvetica", fontsize=8]
|
||||
|
||||
label="AMAR Mascotas - Data Model (Entity Relationships)\n\n"
|
||||
labelloc="t"
|
||||
fontsize=16
|
||||
fontname="Helvetica-Bold"
|
||||
|
||||
// === USERS & AUTHENTICATION ===
|
||||
subgraph cluster_auth {
|
||||
label="Users & Authentication"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E8F5E9"
|
||||
color="#2E7D32"
|
||||
|
||||
auth_user [label="{auth.User|id: PK\luser name: str\lemail: str\lis_staff: bool\lis_superuser: bool\l}", fillcolor="#C8E6C9"]
|
||||
}
|
||||
|
||||
// === PET OWNERS & PETS ===
|
||||
subgraph cluster_mascotas_owners {
|
||||
label="Pet Owners & Pets"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E3F2FD"
|
||||
color="#1565C0"
|
||||
|
||||
petowner [label="{PetOwner|id: PK\lemail: str (unique)\lphone: str\lfirst_name: str\llast_name: str\lneighborhood_id: FK\lcampaign_id: FK (opt)\lgeo_latitude: float\lgeo_longitude: float\laddress: str\l}", fillcolor="#BBDEFB"]
|
||||
|
||||
pet [label="{Pet|id: PK\lowner_id: FK\lname: str\lpet_type: DOG/CAT\lbreed_id: FK (opt)\lgender: M/F\lage: int\lweight: decimal\lheight: decimal\lbirth_date: date\lallergies: text\lneutered: bool\lis_deceased: bool\lstate: puppy/adult/...\lprofile_picture: file\l}", fillcolor="#BBDEFB"]
|
||||
|
||||
petvaccine [label="{PetVaccine|id: PK\lpet_id: FK\lvaccine_id: FK\lapplication_date: date\lnext_application: date\l}", fillcolor="#90CAF9"]
|
||||
|
||||
petstudy [label="{PetStudy|id: PK\lpet_id: FK\lstudy_id: FK\ldate: date\lresult: text\limages: files\l}", fillcolor="#90CAF9"]
|
||||
}
|
||||
|
||||
// === VETERINARIANS ===
|
||||
subgraph cluster_veterinarians {
|
||||
label="Veterinarians"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFF3E0"
|
||||
color="#E65100"
|
||||
|
||||
veterinarian [label="{Veterinarian|id: PK\luser_id: FK\llicense: str\lemail: str\lphone: str\l}", fillcolor="#FFE0B2"]
|
||||
|
||||
availability [label="{Availability|id: PK\lveterinarian_id: FK\lstart_day: 0-6\lend_day: 0-6\lstart_time: time\lend_time: time\l}", fillcolor="#FFCC80"]
|
||||
|
||||
unavailability [label="{Unavailability|id: PK\lveterinarian_id: FK\lstart_date: date\lend_date: date\lstart_time: time\lend_time: time\lreason: str\l}", fillcolor="#FFCC80"]
|
||||
|
||||
vet_specialty [label="{M2M: Vet-Specialty|veterinarian_id: FK\lspecialty_id: FK\l}", shape=diamond, fillcolor="#FFB74D"]
|
||||
|
||||
vet_neighborhood [label="{M2M: Vet-Neighborhood|veterinarian_id: FK\lneighborhood_id: FK\l}", shape=diamond, fillcolor="#FFB74D"]
|
||||
}
|
||||
|
||||
// === SERVICES & PRICING ===
|
||||
subgraph cluster_productos {
|
||||
label="Services & Pricing"
|
||||
style="rounded,filled"
|
||||
fillcolor="#F3E5F5"
|
||||
color="#7B1FA2"
|
||||
|
||||
grupo [label="{Group|id: PK\lname: str\ldescription: text\l}", fillcolor="#E1BEE7"]
|
||||
|
||||
category [label="{Category|id: PK\lgroup_id: FK\lname: str\ldescription: text\lvalue: int (order)\l}", fillcolor="#E1BEE7"]
|
||||
|
||||
service [label="{Service|id: PK\lname: str\ldescription: text\lspecialty_id: FK\lcategory_id: FK\lduration: int (min)\lmodality: onsite/online\lpayment_sign_req: bool\lpet_type_filter: str\lage_filter: str\lweight_range: str\l}", fillcolor="#CE93D8"]
|
||||
|
||||
prices [label="{Prices|id: PK\lservice_id: FK\lveterinarian_id: FK (opt)\lprice: decimal\lprofessional_fee: decimal\lpayment_sign: decimal\lfrom_date: date\lto_date: date\lactive: bool\l}", fillcolor="#BA68C8"]
|
||||
|
||||
discounts [label="{Discounts|id: PK\lservice_id: FK\ldiscount: decimal (%)\lfrom_date: date\lto_date: date\lactive: bool\l}", fillcolor="#BA68C8"]
|
||||
|
||||
servicecombo [label="{ServiceCombo|id: PK\lname: str\ldescription: text\ldiscount_percent: decimal\ldiscount_fixed: decimal\l}", fillcolor="#E1BEE7"]
|
||||
}
|
||||
|
||||
// === CART & CHECKOUT ===
|
||||
subgraph cluster_cart {
|
||||
label="Cart & Checkout"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E0F7FA"
|
||||
color="#00838F"
|
||||
|
||||
cart [label="{Cart|id: PK\lpetowner_id: FK\lveterinarian_id: FK (opt)\luse_vet_prices: bool\lapply_turn_fee: bool\l}", fillcolor="#B2EBF2"]
|
||||
|
||||
cartitem [label="{CartItem|id: PK\lcart_id: FK\lpet_id: FK (opt)\lservice_id: FK\lprice: decimal\lquantity: int\ltotal: decimal (calc)\l}", fillcolor="#80DEEA"]
|
||||
|
||||
cartresumeitem [label="{CartResumeItem|id: PK\lcart_id: FK\lconcept: SUBTOTAL/\l DESCUENTO/\l ADELANTO/\l TOTAL/\l COSTO_SERVICIO\lamount: decimal\lorder: int\l}", fillcolor="#80DEEA"]
|
||||
|
||||
cartpetreason [label="{CartPetReason|id: PK\lcart_id: FK\lpet_id: FK\lreason: text\l}", fillcolor="#80DEEA"]
|
||||
}
|
||||
|
||||
// === SERVICE REQUESTS ===
|
||||
subgraph cluster_solicitudes {
|
||||
label="Service Requests (Workflow)"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFEBEE"
|
||||
color="#C62828"
|
||||
|
||||
servicerequest [label="{ServiceRequest|id: PK\lpetowner_id: FK\lcart_id: FK\lveterinarian_id: FK (opt)\lstate: pending/vet_asked/\l vet_accepted/coordinated/\l payed/Confirmado/...\lreason: text\ldays_requested: JSON\ldate_coordinated: datetime\lhour_coordinated: time\lpay_number: str\lcampaign_id: FK (opt)\lattended_by_id: FK (opt)\l}", fillcolor="#FFCDD2"]
|
||||
|
||||
statehistory [label="{StateHistory|id: PK\lservice_request_id: FK\lstate: str\ladditional_data: JSON\lcreated_at: datetime\luser_id: FK\l}", fillcolor="#EF9A9A"]
|
||||
|
||||
vetasked [label="{VeterinarianAsked|id: PK\lservice_request_id: FK\lveterinarian_id: FK\ldate_asked: datetime\ldate_answered: datetime\laccepted: bool\l}", fillcolor="#EF9A9A"]
|
||||
|
||||
vetreminder [label="{ScheduledVetReminder|id: PK\lvet_asked_id: FK\lscheduled_for: datetime\lprocessed_at: datetime\lstatus: pending/sent/...\lcelery_task_id: str\l}", fillcolor="#E57373"]
|
||||
|
||||
payreminder [label="{PaymentReminder|id: PK\lservice_request_id: FK\lscheduled_for: datetime\lprocessed_at: datetime\lstatus: pending/sent/...\l}", fillcolor="#E57373"]
|
||||
}
|
||||
|
||||
// === VET VISITS ===
|
||||
subgraph cluster_vetvisits {
|
||||
label="Veterinary Visits"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFFDE7"
|
||||
color="#F9A825"
|
||||
|
||||
vetvisit [label="{VetVisit|id: PK\lservice_request_id: FK (opt)\lowner_id: FK\lveterinarian_id: FK\ldate: date\lhour: time\lvisit_type: clinical/\l vaccination/\l telemedicina\lvisit_state: PENDING/\l IN_PROGRESS/\l COMPLETED/\l NO_REPORT/\l CANCELLED\lreason: text\lobservations: text\lprice: decimal\ldeposit: decimal\lvet_fee: decimal\lpay_transaction: str\lgoogle_event_id: str\lafip_receipt_id: FK (opt)\l}", fillcolor="#FFF9C4"]
|
||||
|
||||
vetvisitreport [label="{VetVisitReport|id: PK\lvisit_id: FK\lpet_id: FK\lreason: text\lphysical_exam: text\ldiagnosis: text\ltreatment: text\lpdf_file: file\l}", fillcolor="#FFF59D"]
|
||||
|
||||
vetvisitfollowup [label="{VetVisitFollowUp|id: PK\lreport_id: FK\ldate: date\ldescription: text\l}", fillcolor="#FFF176"]
|
||||
|
||||
vetvisitpetreason [label="{VetVisitPetReason|id: PK\lvisit_id: FK\lpet_id: FK\lreason: text\l}", fillcolor="#FFF176"]
|
||||
|
||||
visit_pets [label="{M2M: Visit-Pets|vetvisit_id: FK\lpet_id: FK\l}", shape=diamond, fillcolor="#FFEE58"]
|
||||
}
|
||||
|
||||
// === REFERENCE DATA ===
|
||||
subgraph cluster_reference {
|
||||
label="Reference Data"
|
||||
style="rounded,filled"
|
||||
fillcolor="#ECEFF1"
|
||||
color="#455A64"
|
||||
|
||||
specialty [label="{Specialty|id: PK\lname: str\l}", fillcolor="#CFD8DC"]
|
||||
neighborhood [label="{Neighborhood|id: PK\lname: str\ldistance_coefficient: decimal\lcoverage_area: GIS Polygon\l}", fillcolor="#CFD8DC"]
|
||||
province [label="{Province|id: PK\lname: str\l}", fillcolor="#CFD8DC"]
|
||||
locality [label="{Locality|id: PK\lprovince_id: FK\lname: str\l}", fillcolor="#CFD8DC"]
|
||||
petbreed [label="{PetBreed|id: PK\lname: str\lpet_type: DOG/CAT\l}", fillcolor="#CFD8DC"]
|
||||
vaccine [label="{Vaccine|id: PK\lname: str\lpet_type: DOG/CAT\lperiodicity: int (months)\l}", fillcolor="#CFD8DC"]
|
||||
study [label="{Study|id: PK\lname: str\lpet_type: DOG/CAT\lgroup_id: FK (opt)\l}", fillcolor="#CFD8DC"]
|
||||
campaign [label="{Campaign|id: PK\lname: str\lutm_source: str\lutm_medium: str\lis_active: bool\l}", fillcolor="#CFD8DC"]
|
||||
tag [label="{Tag|id: PK\lname: str (unique)\l}", fillcolor="#CFD8DC"]
|
||||
medication [label="{Medication|id: PK\lname: str\lgeneral_name_id: FK\ltype_id: FK\lpresentation_id: FK\lpet_type: str\l}", fillcolor="#CFD8DC"]
|
||||
turnfeegroup [label="{IndividualTurnFeeGroup|id: PK\lname: str\lfee_percentage: decimal\l}", fillcolor="#CFD8DC"]
|
||||
}
|
||||
|
||||
// === AFIP/INVOICING ===
|
||||
subgraph cluster_afip {
|
||||
label="AFIP Invoicing"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FCE4EC"
|
||||
color="#AD1457"
|
||||
|
||||
receipt [label="{Receipt|id: PK\ldocument_number: bigint\lreceipt_number: int\lissued_date: date\ltotal_amount: decimal\lnet_taxed: decimal\lcae: str\lcae_expiration: date\l}", fillcolor="#F8BBD9"]
|
||||
}
|
||||
|
||||
// === RELATIONSHIPS ===
|
||||
|
||||
// Auth -> PetOwner/Vet
|
||||
auth_user -> petowner [label="1:1 opt", style=dashed, color="#2E7D32"]
|
||||
auth_user -> veterinarian [label="1:1", color="#2E7D32"]
|
||||
|
||||
// PetOwner relationships
|
||||
petowner -> pet [label="1:N owns", color="#1565C0"]
|
||||
petowner -> neighborhood [label="N:1 lives in", color="#1565C0"]
|
||||
petowner -> campaign [label="N:1 opt", style=dashed, color="#666"]
|
||||
petowner -> cart [label="1:N", color="#00838F"]
|
||||
petowner -> servicerequest [label="1:N requests", color="#C62828"]
|
||||
petowner -> vetvisit [label="1:N as owner", color="#F9A825"]
|
||||
|
||||
// Pet relationships
|
||||
pet -> petbreed [label="N:1 opt", style=dashed, color="#666"]
|
||||
pet -> petvaccine [label="1:N", color="#1565C0"]
|
||||
pet -> petstudy [label="1:N", color="#1565C0"]
|
||||
petvaccine -> vaccine [label="N:1", color="#666"]
|
||||
petstudy -> study [label="N:1", color="#666"]
|
||||
|
||||
// Veterinarian relationships
|
||||
veterinarian -> vet_specialty [label="1:N", color="#E65100"]
|
||||
vet_specialty -> specialty [label="N:1", color="#E65100"]
|
||||
veterinarian -> vet_neighborhood [label="1:N coverage", color="#E65100"]
|
||||
vet_neighborhood -> neighborhood [label="N:1", color="#E65100"]
|
||||
veterinarian -> availability [label="1:N", color="#E65100"]
|
||||
veterinarian -> unavailability [label="1:N", color="#E65100"]
|
||||
veterinarian -> turnfeegroup [label="N:M", color="#E65100", style=dashed]
|
||||
|
||||
// Service/Pricing relationships
|
||||
grupo -> category [label="1:N", color="#7B1FA2"]
|
||||
category -> service [label="1:N", color="#7B1FA2"]
|
||||
service -> specialty [label="N:1 opt", style=dashed, color="#7B1FA2"]
|
||||
service -> prices [label="1:N", color="#7B1FA2"]
|
||||
service -> discounts [label="1:N", color="#7B1FA2"]
|
||||
prices -> veterinarian [label="N:1 opt\n(vet-specific)", style=dashed, color="#7B1FA2"]
|
||||
|
||||
// Cart relationships
|
||||
cart -> veterinarian [label="N:1 opt\n(assigned vet)", style=dashed, color="#00838F"]
|
||||
cart -> cartitem [label="1:N", color="#00838F"]
|
||||
cart -> cartresumeitem [label="1:N", color="#00838F"]
|
||||
cart -> cartpetreason [label="1:N", color="#00838F"]
|
||||
cartitem -> service [label="N:1", color="#00838F"]
|
||||
cartitem -> pet [label="N:1 opt", style=dashed, color="#00838F"]
|
||||
cartpetreason -> pet [label="N:1", color="#00838F"]
|
||||
|
||||
// ServiceRequest relationships
|
||||
servicerequest -> cart [label="1:1", color="#C62828"]
|
||||
servicerequest -> veterinarian [label="N:1 opt\n(assigned)", style=dashed, color="#C62828"]
|
||||
servicerequest -> statehistory [label="1:N audit", color="#C62828"]
|
||||
servicerequest -> vetasked [label="1:N", color="#C62828"]
|
||||
servicerequest -> payreminder [label="1:N", color="#C62828"]
|
||||
servicerequest -> campaign [label="N:1 opt", style=dashed, color="#666"]
|
||||
servicerequest -> tag [label="N:M", color="#666"]
|
||||
vetasked -> veterinarian [label="N:1", color="#C62828"]
|
||||
vetasked -> vetreminder [label="1:N", color="#C62828"]
|
||||
|
||||
// VetVisit relationships
|
||||
vetvisit -> servicerequest [label="1:1 opt\n(from request)", style=dashed, color="#F9A825"]
|
||||
vetvisit -> veterinarian [label="N:1", color="#F9A825"]
|
||||
vetvisit -> visit_pets [label="1:N", color="#F9A825"]
|
||||
visit_pets -> pet [label="N:1", color="#F9A825"]
|
||||
vetvisit -> vetvisitreport [label="1:N", color="#F9A825"]
|
||||
vetvisit -> vetvisitpetreason [label="1:N", color="#F9A825"]
|
||||
vetvisitreport -> pet [label="N:1", color="#F9A825"]
|
||||
vetvisitreport -> vetvisitfollowup [label="1:N", color="#F9A825"]
|
||||
vetvisitpetreason -> pet [label="N:1", color="#F9A825"]
|
||||
|
||||
// AFIP
|
||||
vetvisit -> receipt [label="1:1 opt\n(invoice)", style=dashed, color="#AD1457"]
|
||||
|
||||
// Geography
|
||||
province -> locality [label="1:N", color="#666"]
|
||||
}
|
||||
976
atlas/book/arch-model/03-data-model.svg
Normal file
976
atlas/book/arch-model/03-data-model.svg
Normal file
@@ -0,0 +1,976 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 14.0.5 (0)
|
||||
-->
|
||||
<!-- Title: DataModel Pages: 1 -->
|
||||
<svg width="2547pt" height="1691pt"
|
||||
viewBox="0.00 0.00 2547.00 1691.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1687.2)">
|
||||
<title>DataModel</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-1687.2 2543,-1687.2 2543,4 -4,4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1269.5" y="-1664" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Data Model (Entity Relationships)</text>
|
||||
<g id="clust1" class="cluster">
|
||||
<title>cluster_auth</title>
|
||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M550,-1367.6C550,-1367.6 703,-1367.6 703,-1367.6 709,-1367.6 715,-1373.6 715,-1379.6 715,-1379.6 715,-1515 715,-1515 715,-1521 709,-1527 703,-1527 703,-1527 550,-1527 550,-1527 544,-1527 538,-1521 538,-1515 538,-1515 538,-1379.6 538,-1379.6 538,-1373.6 544,-1367.6 550,-1367.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="626.5" y="-1507.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Users & Authentication</text>
|
||||
</g>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_mascotas_owners</title>
|
||||
<path fill="#e3f2fd" stroke="#1565c0" d="M482,-494.2C482,-494.2 678,-494.2 678,-494.2 684,-494.2 690,-500.2 690,-506.2 690,-506.2 690,-1192.4 690,-1192.4 690,-1198.4 684,-1204.4 678,-1204.4 678,-1204.4 482,-1204.4 482,-1204.4 476,-1204.4 470,-1198.4 470,-1192.4 470,-1192.4 470,-506.2 470,-506.2 470,-500.2 476,-494.2 482,-494.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="580" y="-1185.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Pet Owners & Pets</text>
|
||||
</g>
|
||||
<g id="clust3" class="cluster">
|
||||
<title>cluster_veterinarians</title>
|
||||
<path fill="#fff3e0" stroke="#e65100" d="M806,-317.6C806,-317.6 1867,-317.6 1867,-317.6 1873,-317.6 1879,-323.6 1879,-329.6 1879,-329.6 1879,-647.6 1879,-647.6 1879,-653.6 1873,-659.6 1867,-659.6 1867,-659.6 806,-659.6 806,-659.6 800,-659.6 794,-653.6 794,-647.6 794,-647.6 794,-329.6 794,-329.6 794,-323.6 800,-317.6 806,-317.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1336.5" y="-640.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinarians</text>
|
||||
</g>
|
||||
<g id="clust4" class="cluster">
|
||||
<title>cluster_productos</title>
|
||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M67,-476.2C67,-476.2 318,-476.2 318,-476.2 324,-476.2 330,-482.2 330,-488.2 330,-488.2 330,-1515 330,-1515 330,-1521 324,-1527 318,-1527 318,-1527 67,-1527 67,-1527 61,-1527 55,-1521 55,-1515 55,-1515 55,-488.2 55,-488.2 55,-482.2 61,-476.2 67,-476.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="192.5" y="-1507.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Services & Pricing</text>
|
||||
</g>
|
||||
<g id="clust5" class="cluster">
|
||||
<title>cluster_cart</title>
|
||||
<path fill="#e0f7fa" stroke="#00838f" d="M740,-991C740,-991 1066,-991 1066,-991 1072,-991 1078,-997 1078,-1003 1078,-1003 1078,-1515 1078,-1515 1078,-1521 1072,-1527 1066,-1527 1066,-1527 740,-1527 740,-1527 734,-1527 728,-1521 728,-1515 728,-1515 728,-1003 728,-1003 728,-997 734,-991 740,-991"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="903" y="-1507.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Cart & Checkout</text>
|
||||
</g>
|
||||
<g id="clust6" class="cluster">
|
||||
<title>cluster_solicitudes</title>
|
||||
<path fill="#ffebee" stroke="#c62828" d="M1899,-494.2C1899,-494.2 2297,-494.2 2297,-494.2 2303,-494.2 2309,-500.2 2309,-506.2 2309,-506.2 2309,-1216.4 2309,-1216.4 2309,-1222.4 2303,-1228.4 2297,-1228.4 2297,-1228.4 1899,-1228.4 1899,-1228.4 1893,-1228.4 1887,-1222.4 1887,-1216.4 1887,-1216.4 1887,-506.2 1887,-506.2 1887,-500.2 1893,-494.2 1899,-494.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2098" y="-1209.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Service Requests (Workflow)</text>
|
||||
</g>
|
||||
<g id="clust7" class="cluster">
|
||||
<title>cluster_vetvisits</title>
|
||||
<path fill="#fffde7" stroke="#f9a825" d="M1186,-762.8C1186,-762.8 1726,-762.8 1726,-762.8 1732,-762.8 1738,-768.8 1738,-774.8 1738,-774.8 1738,-1617 1738,-1617 1738,-1623 1732,-1629 1726,-1629 1726,-1629 1186,-1629 1186,-1629 1180,-1629 1174,-1623 1174,-1617 1174,-1617 1174,-774.8 1174,-774.8 1174,-768.8 1180,-762.8 1186,-762.8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1456" y="-1609.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinary Visits</text>
|
||||
</g>
|
||||
<g id="clust8" class="cluster">
|
||||
<title>cluster_reference</title>
|
||||
<path fill="#eceff1" stroke="#455a64" d="M20,-8C20,-8 1189,-8 1189,-8 1195,-8 1201,-14 1201,-20 1201,-20 1201,-278 1201,-278 1201,-284 1195,-290 1189,-290 1189,-290 20,-290 20,-290 14,-290 8,-284 8,-278 8,-278 8,-20 8,-20 8,-14 14,-8 20,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="604.5" y="-270.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Reference Data</text>
|
||||
</g>
|
||||
<g id="clust9" class="cluster">
|
||||
<title>cluster_afip</title>
|
||||
<path fill="#fce4ec" stroke="#ad1457" d="M2399,-997C2399,-997 2519,-997 2519,-997 2525,-997 2531,-1003 2531,-1009 2531,-1009 2531,-1180.4 2531,-1180.4 2531,-1186.4 2525,-1192.4 2519,-1192.4 2519,-1192.4 2399,-1192.4 2399,-1192.4 2393,-1192.4 2387,-1186.4 2387,-1180.4 2387,-1180.4 2387,-1009 2387,-1009 2387,-1003 2393,-997 2399,-997"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2459" y="-1173.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AFIP Invoicing</text>
|
||||
</g>
|
||||
<!-- auth_user -->
|
||||
<g id="node1" class="node">
|
||||
<title>auth_user</title>
|
||||
<polygon fill="#c8e6c9" stroke="black" points="604.14,-1376.1 604.14,-1464.1 701.86,-1464.1 701.86,-1376.1 604.14,-1376.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="653" y="-1451.1" font-family="Helvetica,sans-Serif" font-size="10.00">auth.User</text>
|
||||
<polyline fill="none" stroke="black" points="604.14,-1444.1 701.86,-1444.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1431.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">user name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">email: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">is_staff: bool</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="612.14" y="-1383.1" font-family="Helvetica,sans-Serif" font-size="10.00">is_superuser: bool</text>
|
||||
</g>
|
||||
<!-- petowner -->
|
||||
<g id="node2" class="node">
|
||||
<title>petowner</title>
|
||||
<polygon fill="#bbdefb" stroke="black" points="565.81,-993.5 565.81,-1141.5 680.19,-1141.5 680.19,-993.5 565.81,-993.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="623" y="-1128.5" font-family="Helvetica,sans-Serif" font-size="10.00">PetOwner</text>
|
||||
<polyline fill="none" stroke="black" points="565.81,-1121.5 680.19,-1121.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1108.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">email: str (unique)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">phone: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">first_name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">last_name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">neighborhood_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">campaign_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">geo_latitude: float</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">geo_longitude: float</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="573.81" y="-1000.5" font-family="Helvetica,sans-Serif" font-size="10.00">address: str</text>
|
||||
</g>
|
||||
<!-- auth_user->petowner -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>auth_user->petowner</title>
|
||||
<path fill="none" stroke="#2e7d32" stroke-dasharray="5,2" d="M642.17,-1375.8C642.17,-1375.8 642.17,-1153.26 642.17,-1153.26"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="645.67,-1153.26 642.17,-1143.26 638.67,-1153.26 645.67,-1153.26"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="645.23" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
||||
</g>
|
||||
<!-- veterinarian -->
|
||||
<g id="node6" class="node">
|
||||
<title>veterinarian</title>
|
||||
<polygon fill="#ffe0b2" stroke="black" points="1538.04,-508.7 1538.04,-596.7 1607.96,-596.7 1607.96,-508.7 1538.04,-508.7"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1573" y="-583.7" font-family="Helvetica,sans-Serif" font-size="10.00">Veterinarian</text>
|
||||
<polyline fill="none" stroke="black" points="1538.04,-576.7 1607.96,-576.7"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-563.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-551.7" font-family="Helvetica,sans-Serif" font-size="10.00">user_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-539.7" font-family="Helvetica,sans-Serif" font-size="10.00">license: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-527.7" font-family="Helvetica,sans-Serif" font-size="10.00">email: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1546.04" y="-515.7" font-family="Helvetica,sans-Serif" font-size="10.00">phone: str</text>
|
||||
</g>
|
||||
<!-- auth_user->veterinarian -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>auth_user->veterinarian</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M695.21,-1375.77C695.21,-1200.06 695.21,-562 695.21,-562 695.21,-562 1526.26,-562 1526.26,-562"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1526.26,-565.5 1536.26,-562 1526.26,-558.5 1526.26,-565.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="787.56" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
||||
</g>
|
||||
<!-- pet -->
|
||||
<g id="node3" class="node">
|
||||
<title>pet</title>
|
||||
<polygon fill="#bbdefb" stroke="black" points="574.97,-705.3 574.97,-913.3 681.03,-913.3 681.03,-705.3 574.97,-705.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="628" y="-900.3" font-family="Helvetica,sans-Serif" font-size="10.00">Pet</text>
|
||||
<polyline fill="none" stroke="black" points="574.97,-893.3 681.03,-893.3"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-880.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-868.3" font-family="Helvetica,sans-Serif" font-size="10.00">owner_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-856.3" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-844.3" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-832.3" font-family="Helvetica,sans-Serif" font-size="10.00">breed_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-820.3" font-family="Helvetica,sans-Serif" font-size="10.00">gender: M/F</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-808.3" font-family="Helvetica,sans-Serif" font-size="10.00">age: int</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-796.3" font-family="Helvetica,sans-Serif" font-size="10.00">weight: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-784.3" font-family="Helvetica,sans-Serif" font-size="10.00">height: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-772.3" font-family="Helvetica,sans-Serif" font-size="10.00">birth_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-760.3" font-family="Helvetica,sans-Serif" font-size="10.00">allergies: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-748.3" font-family="Helvetica,sans-Serif" font-size="10.00">neutered: bool</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-736.3" font-family="Helvetica,sans-Serif" font-size="10.00">is_deceased: bool</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-724.3" font-family="Helvetica,sans-Serif" font-size="10.00">state: puppy/adult/...</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="582.97" y="-712.3" font-family="Helvetica,sans-Serif" font-size="10.00">profile_picture: file</text>
|
||||
</g>
|
||||
<!-- petowner->pet -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>petowner->pet</title>
|
||||
<path fill="none" stroke="#1565c0" d="M627.58,-993.16C627.58,-993.16 627.58,-924.87 627.58,-924.87"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="631.08,-924.87 627.58,-914.87 624.08,-924.87 631.08,-924.87"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="588.67" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N owns</text>
|
||||
</g>
|
||||
<!-- cart -->
|
||||
<g id="node17" class="node">
|
||||
<title>cart</title>
|
||||
<polygon fill="#b2ebf2" stroke="black" points="906.36,-1376.1 906.36,-1464.1 1029.64,-1464.1 1029.64,-1376.1 906.36,-1376.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="968" y="-1451.1" font-family="Helvetica,sans-Serif" font-size="10.00">Cart</text>
|
||||
<polyline fill="none" stroke="black" points="906.36,-1444.1 1029.64,-1444.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1431.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">petowner_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">use_vet_prices: bool</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="914.36" y="-1383.1" font-family="Helvetica,sans-Serif" font-size="10.00">apply_turn_fee: bool</text>
|
||||
</g>
|
||||
<!-- petowner->cart -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>petowner->cart</title>
|
||||
<path fill="none" stroke="#00838f" d="M680.65,-1130C765.7,-1130 914.82,-1130 914.82,-1130 914.82,-1130 914.82,-1364.21 914.82,-1364.21"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="911.32,-1364.21 914.82,-1374.21 918.32,-1364.21 911.32,-1364.21"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="790.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- servicerequest -->
|
||||
<g id="node21" class="node">
|
||||
<title>servicerequest</title>
|
||||
<polygon fill="#ffcdd2" stroke="black" points="1895.4,-969.5 1895.4,-1165.5 2032.6,-1165.5 2032.6,-969.5 1895.4,-969.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1964" y="-1152.5" font-family="Helvetica,sans-Serif" font-size="10.00">ServiceRequest</text>
|
||||
<polyline fill="none" stroke="black" points="1895.4,-1145.5 2032.6,-1145.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1132.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1120.5" font-family="Helvetica,sans-Serif" font-size="10.00">petowner_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1108.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">state: pending/vet_asked/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00"> vet_accepted/coordinated/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00"> payed/Confirmado/...</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">days_requested: JSON</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">date_coordinated: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">hour_coordinated: time</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-1000.5" font-family="Helvetica,sans-Serif" font-size="10.00">pay_number: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-988.5" font-family="Helvetica,sans-Serif" font-size="10.00">campaign_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1903.4" y="-976.5" font-family="Helvetica,sans-Serif" font-size="10.00">attended_by_id: FK (opt)</text>
|
||||
</g>
|
||||
<!-- petowner->servicerequest -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>petowner->servicerequest</title>
|
||||
<path fill="none" stroke="#c62828" d="M623.15,-1141.95C623.15,-1145.73 623.15,-1148 623.15,-1148 623.15,-1148 1883.69,-1148 1883.69,-1148"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1883.69,-1151.5 1893.69,-1148 1883.69,-1144.5 1883.69,-1151.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1109" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N requests</text>
|
||||
</g>
|
||||
<!-- vetvisit -->
|
||||
<g id="node26" class="node">
|
||||
<title>vetvisit</title>
|
||||
<polygon fill="#fff9c4" stroke="black" points="1588.19,-1274.1 1588.19,-1566.1 1729.81,-1566.1 1729.81,-1274.1 1588.19,-1274.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1659" y="-1553.1" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisit</text>
|
||||
<polyline fill="none" stroke="black" points="1588.19,-1546.1 1729.81,-1546.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1533.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1521.1" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1509.1" font-family="Helvetica,sans-Serif" font-size="10.00">owner_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1497.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1485.1" font-family="Helvetica,sans-Serif" font-size="10.00">date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1473.1" font-family="Helvetica,sans-Serif" font-size="10.00">hour: time</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1461.1" font-family="Helvetica,sans-Serif" font-size="10.00">visit_type: clinical/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1449.1" font-family="Helvetica,sans-Serif" font-size="10.00"> vaccination/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1437.1" font-family="Helvetica,sans-Serif" font-size="10.00"> telemedicina</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1425.1" font-family="Helvetica,sans-Serif" font-size="10.00">visit_state: PENDING/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1413.1" font-family="Helvetica,sans-Serif" font-size="10.00"> IN_PROGRESS/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1401.1" font-family="Helvetica,sans-Serif" font-size="10.00"> COMPLETED/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1389.1" font-family="Helvetica,sans-Serif" font-size="10.00"> NO_REPORT/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1377.1" font-family="Helvetica,sans-Serif" font-size="10.00"> CANCELLED</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1365.1" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1353.1" font-family="Helvetica,sans-Serif" font-size="10.00">observations: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1341.1" font-family="Helvetica,sans-Serif" font-size="10.00">price: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1329.1" font-family="Helvetica,sans-Serif" font-size="10.00">deposit: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1317.1" font-family="Helvetica,sans-Serif" font-size="10.00">vet_fee: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1305.1" font-family="Helvetica,sans-Serif" font-size="10.00">pay_transaction: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1293.1" font-family="Helvetica,sans-Serif" font-size="10.00">google_event_id: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1596.19" y="-1281.1" font-family="Helvetica,sans-Serif" font-size="10.00">afip_receipt_id: FK (opt)</text>
|
||||
</g>
|
||||
<!-- petowner->vetvisit -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>petowner->vetvisit</title>
|
||||
<path fill="none" stroke="#f9a825" d="M661.18,-1141.78C661.18,-1223.08 661.18,-1342 661.18,-1342 661.18,-1342 1576.53,-1342 1576.53,-1342"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1576.53,-1345.5 1586.53,-1342 1576.53,-1338.5 1576.53,-1345.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1249.57" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N as owner</text>
|
||||
</g>
|
||||
<!-- neighborhood -->
|
||||
<g id="node32" class="node">
|
||||
<title>neighborhood</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="139.79,-139.1 139.79,-215.1 284.21,-215.1 284.21,-139.1 139.79,-139.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="212" y="-202.1" font-family="Helvetica,sans-Serif" font-size="10.00">Neighborhood</text>
|
||||
<polyline fill="none" stroke="black" points="139.79,-195.1 284.21,-195.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">distance_coefficient: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="147.79" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">coverage_area: GIS Polygon</text>
|
||||
</g>
|
||||
<!-- petowner->neighborhood -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>petowner->neighborhood</title>
|
||||
<path fill="none" stroke="#1565c0" d="M565.5,-1008C489.96,-1008 366.79,-1008 366.79,-1008 366.79,-1008 366.79,-210 366.79,-210 366.79,-210 295.92,-210 295.92,-210"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="295.92,-206.5 285.92,-210 295.92,-213.5 295.92,-206.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="25.56" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 lives in</text>
|
||||
</g>
|
||||
<!-- campaign -->
|
||||
<g id="node38" class="node">
|
||||
<title>campaign</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="828.77,-133.1 828.77,-221.1 919.23,-221.1 919.23,-133.1 828.77,-133.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="874" y="-208.1" font-family="Helvetica,sans-Serif" font-size="10.00">Campaign</text>
|
||||
<polyline fill="none" stroke="black" points="828.77,-201.1 919.23,-201.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-188.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-176.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-164.1" font-family="Helvetica,sans-Serif" font-size="10.00">utm_source: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-152.1" font-family="Helvetica,sans-Serif" font-size="10.00">utm_medium: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="836.77" y="-140.1" font-family="Helvetica,sans-Serif" font-size="10.00">is_active: bool</text>
|
||||
</g>
|
||||
<!-- petowner->campaign -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>petowner->campaign</title>
|
||||
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M680.53,-1005C685.43,-1005 688.57,-1005 688.57,-1005 688.57,-1005 688.57,-210 688.57,-210 688.57,-210 817.13,-210 817.13,-210"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="817.13,-213.5 827.13,-210 817.13,-206.5 817.13,-213.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="408.9" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
</g>
|
||||
<!-- petvaccine -->
|
||||
<g id="node4" class="node">
|
||||
<title>petvaccine</title>
|
||||
<polygon fill="#90caf9" stroke="black" points="568.07,-508.7 568.07,-596.7 681.93,-596.7 681.93,-508.7 568.07,-508.7"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="625" y="-583.7" font-family="Helvetica,sans-Serif" font-size="10.00">PetVaccine</text>
|
||||
<polyline fill="none" stroke="black" points="568.07,-576.7 681.93,-576.7"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-563.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-551.7" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-539.7" font-family="Helvetica,sans-Serif" font-size="10.00">vaccine_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-527.7" font-family="Helvetica,sans-Serif" font-size="10.00">application_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="576.07" y="-515.7" font-family="Helvetica,sans-Serif" font-size="10.00">next_application: date</text>
|
||||
</g>
|
||||
<!-- pet->petvaccine -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>pet->petvaccine</title>
|
||||
<path fill="none" stroke="#1565c0" d="M628,-704.93C628,-704.93 628,-608.65 628,-608.65"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="631.5,-608.65 628,-598.65 624.5,-608.65 631.5,-608.65"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="633.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- petstudy -->
|
||||
<g id="node5" class="node">
|
||||
<title>petstudy</title>
|
||||
<polygon fill="#90caf9" stroke="black" points="478.21,-502.7 478.21,-602.7 549.79,-602.7 549.79,-502.7 478.21,-502.7"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="514" y="-589.7" font-family="Helvetica,sans-Serif" font-size="10.00">PetStudy</text>
|
||||
<polyline fill="none" stroke="black" points="478.21,-582.7 549.79,-582.7"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-569.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-557.7" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-545.7" font-family="Helvetica,sans-Serif" font-size="10.00">study_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-533.7" font-family="Helvetica,sans-Serif" font-size="10.00">date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-521.7" font-family="Helvetica,sans-Serif" font-size="10.00">result: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="486.21" y="-509.7" font-family="Helvetica,sans-Serif" font-size="10.00">images: files</text>
|
||||
</g>
|
||||
<!-- pet->petstudy -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>pet->petstudy</title>
|
||||
<path fill="none" stroke="#1565c0" d="M574.67,-713C545,-713 514,-713 514,-713 514,-713 514,-614.49 514,-614.49"/>
|
||||
<polygon fill="#1565c0" stroke="#1565c0" points="517.5,-614.49 514,-604.49 510.5,-614.49 517.5,-614.49"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="558.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- petbreed -->
|
||||
<g id="node35" class="node">
|
||||
<title>petbreed</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="16.26,-145.1 16.26,-209.1 121.74,-209.1 121.74,-145.1 16.26,-145.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="69" y="-196.1" font-family="Helvetica,sans-Serif" font-size="10.00">PetBreed</text>
|
||||
<polyline fill="none" stroke="black" points="16.26,-189.1 121.74,-189.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="24.26" y="-176.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="24.26" y="-164.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="24.26" y="-152.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
||||
</g>
|
||||
<!-- pet->petbreed -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>pet->petbreed</title>
|
||||
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M574.5,-721C429.5,-721 39.78,-721 39.78,-721 39.78,-721 39.78,-221.01 39.78,-221.01"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="43.28,-221.01 39.78,-211.01 36.28,-221.01 43.28,-221.01"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="457.9" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
</g>
|
||||
<!-- vaccine -->
|
||||
<g id="node36" class="node">
|
||||
<title>vaccine</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="501.32,-139.1 501.32,-215.1 620.68,-215.1 620.68,-139.1 501.32,-139.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="561" y="-202.1" font-family="Helvetica,sans-Serif" font-size="10.00">Vaccine</text>
|
||||
<polyline fill="none" stroke="black" points="501.32,-195.1 620.68,-195.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="509.32" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">periodicity: int (months)</text>
|
||||
</g>
|
||||
<!-- petvaccine->vaccine -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>petvaccine->vaccine</title>
|
||||
<path fill="none" stroke="#666666" d="M594.38,-508.51C594.38,-508.51 594.38,-226.98 594.38,-226.98"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="597.88,-226.98 594.38,-216.98 590.88,-226.98 597.88,-226.98"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="581.22" y="-379.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- study -->
|
||||
<g id="node37" class="node">
|
||||
<title>study</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="378.26,-139.1 378.26,-215.1 483.74,-215.1 483.74,-139.1 378.26,-139.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="431" y="-202.1" font-family="Helvetica,sans-Serif" font-size="10.00">Study</text>
|
||||
<polyline fill="none" stroke="black" points="378.26,-195.1 483.74,-195.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: DOG/CAT</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="386.26" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">group_id: FK (opt)</text>
|
||||
</g>
|
||||
<!-- petstudy->study -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>petstudy->study</title>
|
||||
<path fill="none" stroke="#666666" d="M492.53,-502.42C492.53,-400.05 492.53,-177 492.53,-177 492.53,-177 491.7,-177 491.7,-177"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="495.74,-173.5 485.74,-177 495.74,-180.5 495.74,-173.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="495.22" y="-379.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- availability -->
|
||||
<g id="node7" class="node">
|
||||
<title>availability</title>
|
||||
<polygon fill="#ffcc80" stroke="black" points="1653.03,-332.1 1653.03,-432.1 1752.97,-432.1 1752.97,-332.1 1653.03,-332.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1703" y="-419.1" font-family="Helvetica,sans-Serif" font-size="10.00">Availability</text>
|
||||
<polyline fill="none" stroke="black" points="1653.03,-412.1 1752.97,-412.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-399.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-387.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-375.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_day: 0-6</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-363.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_day: 0-6</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-351.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_time: time</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1661.03" y="-339.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_time: time</text>
|
||||
</g>
|
||||
<!-- veterinarian->availability -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>veterinarian->availability</title>
|
||||
<path fill="none" stroke="#e65100" d="M1608.25,-530C1642.2,-530 1688.41,-530 1688.41,-530 1688.41,-530 1688.41,-444.1 1688.41,-444.1"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1691.91,-444.1 1688.41,-434.1 1684.91,-444.1 1691.91,-444.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1653.22" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- unavailability -->
|
||||
<g id="node8" class="node">
|
||||
<title>unavailability</title>
|
||||
<polygon fill="#ffcc80" stroke="black" points="1771.03,-326.1 1771.03,-438.1 1870.97,-438.1 1870.97,-326.1 1771.03,-326.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1821" y="-425.1" font-family="Helvetica,sans-Serif" font-size="10.00">Unavailability</text>
|
||||
<polyline fill="none" stroke="black" points="1771.03,-418.1 1870.97,-418.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-405.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-393.1" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-381.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-369.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-357.1" font-family="Helvetica,sans-Serif" font-size="10.00">start_time: time</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-345.1" font-family="Helvetica,sans-Serif" font-size="10.00">end_time: time</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1779.03" y="-333.1" font-family="Helvetica,sans-Serif" font-size="10.00">reason: str</text>
|
||||
</g>
|
||||
<!-- veterinarian->unavailability -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>veterinarian->unavailability</title>
|
||||
<path fill="none" stroke="#e65100" d="M1608.27,-553C1676.74,-553 1821,-553 1821,-553 1821,-553 1821,-450.05 1821,-450.05"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1824.5,-450.05 1821,-440.05 1817.5,-450.05 1824.5,-450.05"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1729.22" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vet_specialty -->
|
||||
<g id="node9" class="node">
|
||||
<title>vet_specialty</title>
|
||||
<polygon fill="#ffb74d" stroke="black" points="1442,-426.1 1249.44,-382.1 1442,-338.1 1634.56,-382.1 1442,-426.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1283.24" y="-391.1" font-family="Helvetica,sans-Serif" font-size="10.00">{M2M: Vet-Specialty|veterinarian_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1283.24" y="-379.1" font-family="Helvetica,sans-Serif" font-size="10.00">specialty_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1442" y="-367.1" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
|
||||
</g>
|
||||
<!-- veterinarian->vet_specialty -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>veterinarian->vet_specialty</title>
|
||||
<path fill="none" stroke="#e65100" d="M1584.65,-508.32C1584.65,-508.32 1584.65,-405.5 1584.65,-405.5"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1588.15,-405.5 1584.65,-395.5 1581.15,-405.5 1588.15,-405.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1505.22" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vet_neighborhood -->
|
||||
<g id="node10" class="node">
|
||||
<title>vet_neighborhood</title>
|
||||
<polygon fill="#ffb74d" stroke="black" points="1017,-426.1 802.19,-382.1 1017,-338.1 1231.81,-382.1 1017,-426.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="838.97" y="-391.1" font-family="Helvetica,sans-Serif" font-size="10.00">{M2M: Vet-Neighborhood|veterinarian_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="838.97" y="-379.1" font-family="Helvetica,sans-Serif" font-size="10.00">neighborhood_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1017" y="-367.1" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
|
||||
</g>
|
||||
<!-- veterinarian->vet_neighborhood -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>veterinarian->vet_neighborhood</title>
|
||||
<path fill="none" stroke="#e65100" d="M1537.71,-544C1447.43,-544 1215.15,-544 1215.15,-544 1215.15,-544 1215.15,-397.3 1215.15,-397.3"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1218.65,-397.3 1215.15,-387.3 1211.65,-397.3 1218.65,-397.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1273.79" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">1:N coverage</text>
|
||||
</g>
|
||||
<!-- turnfeegroup -->
|
||||
<g id="node41" class="node">
|
||||
<title>turnfeegroup</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="1067.24,-145.1 1067.24,-209.1 1192.76,-209.1 1192.76,-145.1 1067.24,-145.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1130" y="-196.1" font-family="Helvetica,sans-Serif" font-size="10.00">IndividualTurnFeeGroup</text>
|
||||
<polyline fill="none" stroke="black" points="1067.24,-189.1 1192.76,-189.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1075.24" y="-176.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1075.24" y="-164.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1075.24" y="-152.1" font-family="Helvetica,sans-Serif" font-size="10.00">fee_percentage: decimal</text>
|
||||
</g>
|
||||
<!-- veterinarian->turnfeegroup -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>veterinarian->turnfeegroup</title>
|
||||
<path fill="none" stroke="#e65100" stroke-dasharray="5,2" d="M1537.74,-526C1452.2,-526 1240.63,-526 1240.63,-526 1240.63,-526 1240.63,-177 1240.63,-177 1240.63,-177 1204.58,-177 1204.58,-177"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="1204.58,-173.5 1194.58,-177 1204.58,-180.5 1204.58,-173.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="777.33" y="-379.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:M</text>
|
||||
</g>
|
||||
<!-- specialty -->
|
||||
<g id="node31" class="node">
|
||||
<title>specialty</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="753.16,-151.1 753.16,-203.1 810.84,-203.1 810.84,-151.1 753.16,-151.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="782" y="-190.1" font-family="Helvetica,sans-Serif" font-size="10.00">Specialty</text>
|
||||
<polyline fill="none" stroke="black" points="753.16,-183.1 810.84,-183.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="761.16" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="761.16" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
</g>
|
||||
<!-- vet_specialty->specialty -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>vet_specialty->specialty</title>
|
||||
<path fill="none" stroke="#e65100" d="M1442,-337.81C1442,-315.45 1442,-293 1442,-293 1442,-293 806.51,-293 806.51,-293 806.51,-293 806.51,-214.91 806.51,-214.91"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="810.01,-214.91 806.51,-204.91 803.01,-214.91 810.01,-214.91"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="751.22" y="-300.4" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- vet_neighborhood->neighborhood -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>vet_neighborhood->neighborhood</title>
|
||||
<path fill="none" stroke="#e65100" d="M800.26,-382C572.59,-382 247.21,-382 247.21,-382 247.21,-382 247.21,-226.67 247.21,-226.67"/>
|
||||
<polygon fill="#e65100" stroke="#e65100" points="250.71,-226.67 247.21,-216.67 243.71,-226.67 250.71,-226.67"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="657.22" y="-300.4" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- grupo -->
|
||||
<g id="node11" class="node">
|
||||
<title>grupo</title>
|
||||
<polygon fill="#e1bee7" stroke="black" points="85.98,-1388.1 85.98,-1452.1 172.02,-1452.1 172.02,-1388.1 85.98,-1388.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="129" y="-1439.1" font-family="Helvetica,sans-Serif" font-size="10.00">Group</text>
|
||||
<polyline fill="none" stroke="black" points="85.98,-1432.1 172.02,-1432.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="93.98" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="93.98" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="93.98" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
||||
</g>
|
||||
<!-- category -->
|
||||
<g id="node12" class="node">
|
||||
<title>category</title>
|
||||
<polygon fill="#e1bee7" stroke="black" points="166.6,-1023.5 166.6,-1111.5 255.4,-1111.5 255.4,-1023.5 166.6,-1023.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="211" y="-1098.5" font-family="Helvetica,sans-Serif" font-size="10.00">Category</text>
|
||||
<polyline fill="none" stroke="black" points="166.6,-1091.5 255.4,-1091.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1078.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1066.5" font-family="Helvetica,sans-Serif" font-size="10.00">group_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1054.5" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1042.5" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="174.6" y="-1030.5" font-family="Helvetica,sans-Serif" font-size="10.00">value: int (order)</text>
|
||||
</g>
|
||||
<!-- grupo->category -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>grupo->category</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M172.38,-1420C177.51,-1420 180.96,-1420 180.96,-1420 180.96,-1420 180.96,-1123.3 180.96,-1123.3"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="184.46,-1123.3 180.96,-1113.3 177.46,-1123.3 184.46,-1123.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="151.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- service -->
|
||||
<g id="node13" class="node">
|
||||
<title>service</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="199.63,-729.3 199.63,-889.3 322.37,-889.3 322.37,-729.3 199.63,-729.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="261" y="-876.3" font-family="Helvetica,sans-Serif" font-size="10.00">Service</text>
|
||||
<polyline fill="none" stroke="black" points="199.63,-869.3 322.37,-869.3"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-856.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-844.3" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-832.3" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-820.3" font-family="Helvetica,sans-Serif" font-size="10.00">specialty_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-808.3" font-family="Helvetica,sans-Serif" font-size="10.00">category_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-796.3" font-family="Helvetica,sans-Serif" font-size="10.00">duration: int (min)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-784.3" font-family="Helvetica,sans-Serif" font-size="10.00">modality: onsite/online</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-772.3" font-family="Helvetica,sans-Serif" font-size="10.00">payment_sign_req: bool</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-760.3" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type_filter: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-748.3" font-family="Helvetica,sans-Serif" font-size="10.00">age_filter: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="207.63" y="-736.3" font-family="Helvetica,sans-Serif" font-size="10.00">weight_range: str</text>
|
||||
</g>
|
||||
<!-- category->service -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>category->service</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M227.52,-1023.11C227.52,-1023.11 227.52,-901.07 227.52,-901.07"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="231.02,-901.07 227.52,-891.07 224.02,-901.08 231.02,-901.07"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="250.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- prices -->
|
||||
<g id="node14" class="node">
|
||||
<title>prices</title>
|
||||
<polygon fill="#ba68c8" stroke="black" points="63.3,-484.7 63.3,-620.7 192.7,-620.7 192.7,-484.7 63.3,-484.7"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="128" y="-607.7" font-family="Helvetica,sans-Serif" font-size="10.00">Prices</text>
|
||||
<polyline fill="none" stroke="black" points="63.3,-600.7 192.7,-600.7"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-587.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-575.7" font-family="Helvetica,sans-Serif" font-size="10.00">service_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-563.7" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-551.7" font-family="Helvetica,sans-Serif" font-size="10.00">price: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-539.7" font-family="Helvetica,sans-Serif" font-size="10.00">professional_fee: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-527.7" font-family="Helvetica,sans-Serif" font-size="10.00">payment_sign: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-515.7" font-family="Helvetica,sans-Serif" font-size="10.00">from_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-503.7" font-family="Helvetica,sans-Serif" font-size="10.00">to_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="71.3" y="-491.7" font-family="Helvetica,sans-Serif" font-size="10.00">active: bool</text>
|
||||
</g>
|
||||
<!-- service->prices -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>service->prices</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M204.92,-728.98C204.92,-673.26 204.92,-609 204.92,-609 204.92,-609 203.72,-609 203.72,-609"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="204.41,-605.5 194.41,-609 204.41,-612.5 204.41,-605.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="184.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- discounts -->
|
||||
<g id="node15" class="node">
|
||||
<title>discounts</title>
|
||||
<polygon fill="#ba68c8" stroke="black" points="210.2,-502.7 210.2,-602.7 321.8,-602.7 321.8,-502.7 210.2,-502.7"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="266" y="-589.7" font-family="Helvetica,sans-Serif" font-size="10.00">Discounts</text>
|
||||
<polyline fill="none" stroke="black" points="210.2,-582.7 321.8,-582.7"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-569.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-557.7" font-family="Helvetica,sans-Serif" font-size="10.00">service_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-545.7" font-family="Helvetica,sans-Serif" font-size="10.00">discount: decimal (%)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-533.7" font-family="Helvetica,sans-Serif" font-size="10.00">from_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-521.7" font-family="Helvetica,sans-Serif" font-size="10.00">to_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="218.2" y="-509.7" font-family="Helvetica,sans-Serif" font-size="10.00">active: bool</text>
|
||||
</g>
|
||||
<!-- service->discounts -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>service->discounts</title>
|
||||
<path fill="none" stroke="#7b1fa2" d="M247.4,-728.81C247.4,-728.81 247.4,-614.57 247.4,-614.57"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="250.9,-614.57 247.4,-604.57 243.9,-614.57 250.9,-614.57"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="270.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- service->specialty -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>service->specialty</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M284.6,-728.87C284.6,-675.42 284.6,-615 284.6,-615 284.6,-615 777.67,-615 777.67,-615 777.67,-615 777.67,-215.02 777.67,-215.02"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="781.17,-215.02 777.67,-205.02 774.17,-215.02 781.17,-215.02"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="538.9" y="-459" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
</g>
|
||||
<!-- prices->veterinarian -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>prices->veterinarian</title>
|
||||
<path fill="none" stroke="#7b1fa2" stroke-dasharray="5,2" d="M192.84,-493C472.5,-493 1561.35,-493 1561.35,-493 1561.35,-493 1561.35,-496.86 1561.35,-496.86"/>
|
||||
<polygon fill="#7b1fa2" stroke="#7b1fa2" points="1557.85,-496.86 1561.35,-506.86 1564.85,-496.86 1557.85,-496.86"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="434" y="-679.6" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="434" y="-670" font-family="Helvetica,sans-Serif" font-size="8.00">(vet-specific)</text>
|
||||
</g>
|
||||
<!-- servicecombo -->
|
||||
<g id="node16" class="node">
|
||||
<title>servicecombo</title>
|
||||
<polygon fill="#e1bee7" stroke="black" points="189.91,-1376.1 189.91,-1464.1 322.09,-1464.1 322.09,-1376.1 189.91,-1376.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="256" y="-1451.1" font-family="Helvetica,sans-Serif" font-size="10.00">ServiceCombo</text>
|
||||
<polyline fill="none" stroke="black" points="189.91,-1444.1 322.09,-1444.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1431.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1419.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1407.1" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1395.1" font-family="Helvetica,sans-Serif" font-size="10.00">discount_percent: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="197.91" y="-1383.1" font-family="Helvetica,sans-Serif" font-size="10.00">discount_fixed: decimal</text>
|
||||
</g>
|
||||
<!-- cart->veterinarian -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>cart->veterinarian</title>
|
||||
<path fill="none" stroke="#00838f" stroke-dasharray="5,2" d="M949.06,-1375.77C949.06,-1202.29 949.06,-579 949.06,-579 949.06,-579 1526.15,-579 1526.15,-579"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="1526.15,-582.5 1536.15,-579 1526.15,-575.5 1526.15,-582.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1789.12" y="-943.8" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1789.12" y="-934.2" font-family="Helvetica,sans-Serif" font-size="8.00">(assigned vet)</text>
|
||||
</g>
|
||||
<!-- cartitem -->
|
||||
<g id="node18" class="node">
|
||||
<title>cartitem</title>
|
||||
<polygon fill="#80deea" stroke="black" points="735.93,-1011.5 735.93,-1123.5 838.07,-1123.5 838.07,-1011.5 735.93,-1011.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="787" y="-1110.5" font-family="Helvetica,sans-Serif" font-size="10.00">CartItem</text>
|
||||
<polyline fill="none" stroke="black" points="735.93,-1103.5 838.07,-1103.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1090.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1078.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1066.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK (opt)</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1054.5" font-family="Helvetica,sans-Serif" font-size="10.00">service_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1042.5" font-family="Helvetica,sans-Serif" font-size="10.00">price: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1030.5" font-family="Helvetica,sans-Serif" font-size="10.00">quantity: int</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="743.93" y="-1018.5" font-family="Helvetica,sans-Serif" font-size="10.00">total: decimal (calc)</text>
|
||||
</g>
|
||||
<!-- cart->cartitem -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>cart->cartitem</title>
|
||||
<path fill="none" stroke="#00838f" d="M923.27,-1375.74C923.27,-1291.12 923.27,-1115 923.27,-1115 923.27,-1115 850.03,-1115 850.03,-1115"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="850.03,-1111.5 840.03,-1115 850.03,-1118.5 850.03,-1111.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="862.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- cartresumeitem -->
|
||||
<g id="node19" class="node">
|
||||
<title>cartresumeitem</title>
|
||||
<polygon fill="#80deea" stroke="black" points="957.93,-999.5 957.93,-1135.5 1070.07,-1135.5 1070.07,-999.5 957.93,-999.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1014" y="-1122.5" font-family="Helvetica,sans-Serif" font-size="10.00">CartResumeItem</text>
|
||||
<polyline fill="none" stroke="black" points="957.93,-1115.5 1070.07,-1115.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1102.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1090.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1078.5" font-family="Helvetica,sans-Serif" font-size="10.00">concept: SUBTOTAL/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1066.5" font-family="Helvetica,sans-Serif" font-size="10.00"> DESCUENTO/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1054.5" font-family="Helvetica,sans-Serif" font-size="10.00"> ADELANTO/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1042.5" font-family="Helvetica,sans-Serif" font-size="10.00"> TOTAL/</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1030.5" font-family="Helvetica,sans-Serif" font-size="10.00"> COSTO_SERVICIO</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1018.5" font-family="Helvetica,sans-Serif" font-size="10.00">amount: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="965.93" y="-1006.5" font-family="Helvetica,sans-Serif" font-size="10.00">order: int</text>
|
||||
</g>
|
||||
<!-- cart->cartresumeitem -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>cart->cartresumeitem</title>
|
||||
<path fill="none" stroke="#00838f" d="M981.83,-1375.8C981.83,-1375.8 981.83,-1147.31 981.83,-1147.31"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="985.33,-1147.31 981.83,-1137.31 978.33,-1147.31 985.33,-1147.31"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="990.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- cartpetreason -->
|
||||
<g id="node20" class="node">
|
||||
<title>cartpetreason</title>
|
||||
<polygon fill="#80deea" stroke="black" points="855.82,-1029.5 855.82,-1105.5 940.18,-1105.5 940.18,-1029.5 855.82,-1029.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="898" y="-1092.5" font-family="Helvetica,sans-Serif" font-size="10.00">CartPetReason</text>
|
||||
<polyline fill="none" stroke="black" points="855.82,-1085.5 940.18,-1085.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">cart_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="863.82" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
||||
</g>
|
||||
<!-- cart->cartpetreason -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>cart->cartpetreason</title>
|
||||
<path fill="none" stroke="#00838f" d="M931.73,-1375.8C931.73,-1375.8 931.73,-1117.47 931.73,-1117.47"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="935.23,-1117.47 931.73,-1107.47 928.23,-1117.47 935.23,-1117.47"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="919.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- cartitem->pet -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>cartitem->pet</title>
|
||||
<path fill="none" stroke="#00838f" stroke-dasharray="5,2" d="M747.42,-1011.12C747.42,-964.19 747.42,-905 747.42,-905 747.42,-905 692.94,-905 692.94,-905"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="692.94,-901.5 682.94,-905 692.94,-908.5 692.94,-901.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="640.9" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
</g>
|
||||
<!-- cartitem->service -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>cartitem->service</title>
|
||||
<path fill="none" stroke="#00838f" d="M741.67,-1011.15C741.67,-997.62 741.67,-987 741.67,-987 741.67,-987 288.75,-987 288.75,-987 288.75,-987 288.75,-901.09 288.75,-901.09"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="292.25,-901.09 288.75,-891.09 285.25,-901.09 292.25,-901.09"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="446.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- cartpetreason->pet -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>cartpetreason->pet</title>
|
||||
<path fill="none" stroke="#00838f" d="M898,-1029.01C898,-978.45 898,-896 898,-896 898,-896 692.86,-896 692.86,-896"/>
|
||||
<polygon fill="#00838f" stroke="#00838f" points="692.86,-892.5 682.86,-896 692.86,-899.5 692.86,-892.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="683.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- servicerequest->veterinarian -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>servicerequest->veterinarian</title>
|
||||
<path fill="none" stroke="#c62828" stroke-dasharray="5,2" d="M1895.22,-975C1780.2,-975 1561.26,-975 1561.26,-975 1561.26,-975 1561.26,-608.61 1561.26,-608.61"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1564.76,-608.61 1561.26,-598.61 1557.76,-608.61 1564.76,-608.61"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1858.68" y="-811.7" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1858.68" y="-802.1" font-family="Helvetica,sans-Serif" font-size="8.00">(assigned)</text>
|
||||
</g>
|
||||
<!-- servicerequest->cart -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>servicerequest->cart</title>
|
||||
<path fill="none" stroke="#c62828" d="M1895.08,-1154C1675.36,-1154 1005.73,-1154 1005.73,-1154 1005.73,-1154 1005.73,-1364.35 1005.73,-1364.35"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1002.23,-1364.35 1005.73,-1374.35 1009.23,-1364.35 1002.23,-1364.35"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1160.56" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
||||
</g>
|
||||
<!-- statehistory -->
|
||||
<g id="node22" class="node">
|
||||
<title>statehistory</title>
|
||||
<polygon fill="#ef9a9a" stroke="black" points="2182.86,-759.3 2182.86,-859.3 2301.14,-859.3 2301.14,-759.3 2182.86,-759.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2242" y="-846.3" font-family="Helvetica,sans-Serif" font-size="10.00">StateHistory</text>
|
||||
<polyline fill="none" stroke="black" points="2182.86,-839.3 2301.14,-839.3"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-826.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-814.3" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-802.3" font-family="Helvetica,sans-Serif" font-size="10.00">state: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-790.3" font-family="Helvetica,sans-Serif" font-size="10.00">additional_data: JSON</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-778.3" font-family="Helvetica,sans-Serif" font-size="10.00">created_at: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2190.86" y="-766.3" font-family="Helvetica,sans-Serif" font-size="10.00">user_id: FK</text>
|
||||
</g>
|
||||
<!-- servicerequest->statehistory -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>servicerequest->statehistory</title>
|
||||
<path fill="none" stroke="#c62828" d="M2033.08,-987C2115.41,-987 2242,-987 2242,-987 2242,-987 2242,-871.26 2242,-871.26"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="2245.5,-871.26 2242,-861.26 2238.5,-871.26 2245.5,-871.26"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2179.01" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N audit</text>
|
||||
</g>
|
||||
<!-- vetasked -->
|
||||
<g id="node23" class="node">
|
||||
<title>vetasked</title>
|
||||
<polygon fill="#ef9a9a" stroke="black" points="1894.85,-759.3 1894.85,-859.3 2023.15,-859.3 2023.15,-759.3 1894.85,-759.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1959" y="-846.3" font-family="Helvetica,sans-Serif" font-size="10.00">VeterinarianAsked</text>
|
||||
<polyline fill="none" stroke="black" points="1894.85,-839.3 2023.15,-839.3"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-826.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-814.3" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-802.3" font-family="Helvetica,sans-Serif" font-size="10.00">veterinarian_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-790.3" font-family="Helvetica,sans-Serif" font-size="10.00">date_asked: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-778.3" font-family="Helvetica,sans-Serif" font-size="10.00">date_answered: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1902.85" y="-766.3" font-family="Helvetica,sans-Serif" font-size="10.00">accepted: bool</text>
|
||||
</g>
|
||||
<!-- servicerequest->vetasked -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>servicerequest->vetasked</title>
|
||||
<path fill="none" stroke="#c62828" d="M1959.27,-969.15C1959.27,-969.15 1959.27,-871.22 1959.27,-871.22"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1962.77,-871.22 1959.27,-861.22 1955.77,-871.22 1962.77,-871.22"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1967.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- payreminder -->
|
||||
<g id="node25" class="node">
|
||||
<title>payreminder</title>
|
||||
<polygon fill="#e57373" stroke="black" points="2041.63,-765.3 2041.63,-853.3 2164.37,-853.3 2164.37,-765.3 2041.63,-765.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2103" y="-840.3" font-family="Helvetica,sans-Serif" font-size="10.00">PaymentReminder</text>
|
||||
<polyline fill="none" stroke="black" points="2041.63,-833.3 2164.37,-833.3"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-820.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-808.3" font-family="Helvetica,sans-Serif" font-size="10.00">service_request_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-796.3" font-family="Helvetica,sans-Serif" font-size="10.00">scheduled_for: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-784.3" font-family="Helvetica,sans-Serif" font-size="10.00">processed_at: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2049.63" y="-772.3" font-family="Helvetica,sans-Serif" font-size="10.00">status: pending/sent/...</text>
|
||||
</g>
|
||||
<!-- servicerequest->payreminder -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>servicerequest->payreminder</title>
|
||||
<path fill="none" stroke="#c62828" d="M2029.45,-969.03C2029.45,-895.98 2029.45,-809 2029.45,-809 2029.45,-809 2030.63,-809 2030.63,-809"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="2029.75,-812.5 2039.75,-809 2029.75,-805.5 2029.75,-812.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2055.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- servicerequest->campaign -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>servicerequest->campaign</title>
|
||||
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M2026.3,-969.29C2026.3,-754.09 2026.3,-260 2026.3,-260 2026.3,-260 874,-260 874,-260 874,-260 874,-232.98 874,-232.98"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="877.5,-232.98 874,-222.98 870.5,-232.98 877.5,-232.98"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2341.9" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:1 opt</text>
|
||||
</g>
|
||||
<!-- tag -->
|
||||
<g id="node39" class="node">
|
||||
<title>tag</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="638.43,-151.1 638.43,-203.1 735.57,-203.1 735.57,-151.1 638.43,-151.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="687" y="-190.1" font-family="Helvetica,sans-Serif" font-size="10.00">Tag</text>
|
||||
<polyline fill="none" stroke="black" points="638.43,-183.1 735.57,-183.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="646.43" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="646.43" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str (unique)</text>
|
||||
</g>
|
||||
<!-- servicerequest->tag -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>servicerequest->tag</title>
|
||||
<path fill="none" stroke="#666666" d="M1895.27,-981C1634.21,-981 718.72,-981 718.72,-981 718.72,-981 718.72,-214.9 718.72,-214.9"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="722.22,-214.9 718.72,-204.9 715.22,-214.9 722.22,-214.9"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="756.33" y="-550.3" font-family="Helvetica,sans-Serif" font-size="8.00">N:M</text>
|
||||
</g>
|
||||
<!-- vetasked->veterinarian -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>vetasked->veterinarian</title>
|
||||
<path fill="none" stroke="#c62828" d="M1894.62,-765C1787.53,-765 1584.47,-765 1584.47,-765 1584.47,-765 1584.47,-608.46 1584.47,-608.46"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1587.97,-608.46 1584.47,-598.46 1580.97,-608.46 1587.97,-608.46"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1848.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- vetreminder -->
|
||||
<g id="node24" class="node">
|
||||
<title>vetreminder</title>
|
||||
<polygon fill="#e57373" stroke="black" points="1897.63,-502.7 1897.63,-602.7 2020.37,-602.7 2020.37,-502.7 1897.63,-502.7"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1959" y="-589.7" font-family="Helvetica,sans-Serif" font-size="10.00">ScheduledVetReminder</text>
|
||||
<polyline fill="none" stroke="black" points="1897.63,-582.7 2020.37,-582.7"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-569.7" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-557.7" font-family="Helvetica,sans-Serif" font-size="10.00">vet_asked_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-545.7" font-family="Helvetica,sans-Serif" font-size="10.00">scheduled_for: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-533.7" font-family="Helvetica,sans-Serif" font-size="10.00">processed_at: datetime</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-521.7" font-family="Helvetica,sans-Serif" font-size="10.00">status: pending/sent/...</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1905.63" y="-509.7" font-family="Helvetica,sans-Serif" font-size="10.00">celery_task_id: str</text>
|
||||
</g>
|
||||
<!-- vetasked->vetreminder -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>vetasked->vetreminder</title>
|
||||
<path fill="none" stroke="#c62828" d="M1959,-758.91C1959,-758.91 1959,-614.49 1959,-614.49"/>
|
||||
<polygon fill="#c62828" stroke="#c62828" points="1962.5,-614.49 1959,-604.49 1955.5,-614.49 1962.5,-614.49"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1965.22" y="-674.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetvisit->veterinarian -->
|
||||
<g id="edge44" class="edge">
|
||||
<title>vetvisit->veterinarian</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1617.03,-1273.86C1617.03,-1031.98 1617.03,-575 1617.03,-575 1617.03,-575 1616.13,-575 1616.13,-575"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1619.63,-571.5 1609.63,-575 1619.63,-578.5 1619.63,-571.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2373.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- vetvisit->servicerequest -->
|
||||
<g id="edge43" class="edge">
|
||||
<title>vetvisit->servicerequest</title>
|
||||
<path fill="none" stroke="#f9a825" stroke-dasharray="5,2" d="M1695.23,-1273.71C1695.23,-1214.56 1695.23,-1160 1695.23,-1160 1695.23,-1160 1883.62,-1160 1883.62,-1160"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1883.62,-1163.5 1893.62,-1160 1883.62,-1156.5 1883.62,-1163.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1864.12" y="-1248.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1864.12" y="-1238.8" font-family="Helvetica,sans-Serif" font-size="8.00">(from request)</text>
|
||||
</g>
|
||||
<!-- vetvisitreport -->
|
||||
<g id="node27" class="node">
|
||||
<title>vetvisitreport</title>
|
||||
<polygon fill="#fff59d" stroke="black" points="1626.09,-1005.5 1626.09,-1129.5 1729.91,-1129.5 1729.91,-1005.5 1626.09,-1005.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-1116.5" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitReport</text>
|
||||
<polyline fill="none" stroke="black" points="1626.09,-1109.5 1729.91,-1109.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">visit_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">physical_exam: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">diagnosis: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">treatment: text</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1634.09" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">pdf_file: file</text>
|
||||
</g>
|
||||
<!-- vetvisit->vetvisitreport -->
|
||||
<g id="edge47" class="edge">
|
||||
<title>vetvisit->vetvisitreport</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1660.66,-1273.63C1660.66,-1273.63 1660.66,-1141.19 1660.66,-1141.19"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1664.16,-1141.19 1660.66,-1131.19 1657.16,-1141.19 1664.16,-1141.19"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1675.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetvisitpetreason -->
|
||||
<g id="node29" class="node">
|
||||
<title>vetvisitpetreason</title>
|
||||
<polygon fill="#fff176" stroke="black" points="1508.31,-1029.5 1508.31,-1105.5 1607.69,-1105.5 1607.69,-1029.5 1508.31,-1029.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1558" y="-1092.5" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitPetReason</text>
|
||||
<polyline fill="none" stroke="black" points="1508.31,-1085.5 1607.69,-1085.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">visit_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1516.31" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">reason: text</text>
|
||||
</g>
|
||||
<!-- vetvisit->vetvisitpetreason -->
|
||||
<g id="edge48" class="edge">
|
||||
<title>vetvisit->vetvisitpetreason</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1597.94,-1273.63C1597.94,-1273.63 1597.94,-1117.18 1597.94,-1117.18"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1601.44,-1117.18 1597.94,-1107.18 1594.44,-1117.18 1601.44,-1117.18"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1598.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- visit_pets -->
|
||||
<g id="node30" class="node">
|
||||
<title>visit_pets</title>
|
||||
<polygon fill="#ffee58" stroke="black" points="1336,-1111.5 1181.81,-1067.5 1336,-1023.5 1490.19,-1067.5 1336,-1111.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1210.47" y="-1076.5" font-family="Helvetica,sans-Serif" font-size="10.00">{M2M: Visit-Pets|vetvisit_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1210.47" y="-1064.5" font-family="Helvetica,sans-Serif" font-size="10.00">pet_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1336" y="-1052.5" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
|
||||
</g>
|
||||
<!-- vetvisit->visit_pets -->
|
||||
<g id="edge45" class="edge">
|
||||
<title>vetvisit->visit_pets</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1587.72,-1308C1492.6,-1308 1336,-1308 1336,-1308 1336,-1308 1336,-1123.41 1336,-1123.41"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1339.5,-1123.41 1336,-1113.41 1332.5,-1123.41 1339.5,-1123.41"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1476.22" y="-1243.6" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- receipt -->
|
||||
<g id="node42" class="node">
|
||||
<title>receipt</title>
|
||||
<polygon fill="#f8bbd9" stroke="black" points="2394.58,-1005.5 2394.58,-1129.5 2523.42,-1129.5 2523.42,-1005.5 2394.58,-1005.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2459" y="-1116.5" font-family="Helvetica,sans-Serif" font-size="10.00">Receipt</text>
|
||||
<polyline fill="none" stroke="black" points="2394.58,-1109.5 2523.42,-1109.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1096.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1084.5" font-family="Helvetica,sans-Serif" font-size="10.00">document_number: bigint</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1072.5" font-family="Helvetica,sans-Serif" font-size="10.00">receipt_number: int</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1060.5" font-family="Helvetica,sans-Serif" font-size="10.00">issued_date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1048.5" font-family="Helvetica,sans-Serif" font-size="10.00">total_amount: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1036.5" font-family="Helvetica,sans-Serif" font-size="10.00">net_taxed: decimal</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1024.5" font-family="Helvetica,sans-Serif" font-size="10.00">cae: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="2402.58" y="-1012.5" font-family="Helvetica,sans-Serif" font-size="10.00">cae_expiration: date</text>
|
||||
</g>
|
||||
<!-- vetvisit->receipt -->
|
||||
<g id="edge52" class="edge">
|
||||
<title>vetvisit->receipt</title>
|
||||
<path fill="none" stroke="#ad1457" stroke-dasharray="5,2" d="M1729.99,-1420C1925.78,-1420 2459,-1420 2459,-1420 2459,-1420 2459,-1141.22 2459,-1141.22"/>
|
||||
<polygon fill="#ad1457" stroke="#ad1457" points="2462.5,-1141.22 2459,-1131.22 2455.5,-1141.22 2462.5,-1141.22"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2444.12" y="-1248.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2444.12" y="-1238.8" font-family="Helvetica,sans-Serif" font-size="8.00">(invoice)</text>
|
||||
</g>
|
||||
<!-- vetvisitreport->pet -->
|
||||
<g id="edge49" class="edge">
|
||||
<title>vetvisitreport->pet</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1662.74,-1005.24C1662.74,-947.02 1662.74,-869 1662.74,-869 1662.74,-869 692.97,-869 692.97,-869"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="692.97,-865.5 682.97,-869 692.97,-872.5 692.97,-865.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1237.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- vetvisitfollowup -->
|
||||
<g id="node28" class="node">
|
||||
<title>vetvisitfollowup</title>
|
||||
<polygon fill="#fff176" stroke="black" points="1632.21,-771.3 1632.21,-847.3 1723.79,-847.3 1723.79,-771.3 1632.21,-771.3"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1678" y="-834.3" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitFollowUp</text>
|
||||
<polyline fill="none" stroke="black" points="1632.21,-827.3 1723.79,-827.3"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-814.3" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-802.3" font-family="Helvetica,sans-Serif" font-size="10.00">report_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-790.3" font-family="Helvetica,sans-Serif" font-size="10.00">date: date</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="1640.21" y="-778.3" font-family="Helvetica,sans-Serif" font-size="10.00">description: text</text>
|
||||
</g>
|
||||
<!-- vetvisitreport->vetvisitfollowup -->
|
||||
<g id="edge50" class="edge">
|
||||
<title>vetvisitreport->vetvisitfollowup</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1693.26,-1005.33C1693.26,-1005.33 1693.26,-859.22 1693.26,-859.22"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="1696.76,-859.22 1693.26,-849.22 1689.76,-859.22 1696.76,-859.22"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1684.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetvisitpetreason->pet -->
|
||||
<g id="edge51" class="edge">
|
||||
<title>vetvisitpetreason->pet</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1523.18,-1029.15C1523.18,-973.78 1523.18,-878 1523.18,-878 1523.18,-878 692.83,-878 692.83,-878"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="692.83,-874.5 682.83,-878 692.83,-881.5 692.83,-874.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="753.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- visit_pets->pet -->
|
||||
<g id="edge46" class="edge">
|
||||
<title>visit_pets->pet</title>
|
||||
<path fill="none" stroke="#f9a825" d="M1198.48,-1062.29C1198.48,-1032.7 1198.48,-887 1198.48,-887 1198.48,-887 692.93,-887 692.93,-887"/>
|
||||
<polygon fill="#f9a825" stroke="#f9a825" points="692.93,-883.5 682.93,-887 692.93,-890.5 692.93,-883.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="718.22" y="-939" font-family="Helvetica,sans-Serif" font-size="8.00">N:1</text>
|
||||
</g>
|
||||
<!-- province -->
|
||||
<g id="node33" class="node">
|
||||
<title>province</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="302.16,-151.1 302.16,-203.1 359.84,-203.1 359.84,-151.1 302.16,-151.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="331" y="-190.1" font-family="Helvetica,sans-Serif" font-size="10.00">Province</text>
|
||||
<polyline fill="none" stroke="black" points="302.16,-183.1 359.84,-183.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="310.16" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="310.16" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
</g>
|
||||
<!-- locality -->
|
||||
<g id="node34" class="node">
|
||||
<title>locality</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="288.26,-16.5 288.26,-80.5 373.74,-80.5 373.74,-16.5 288.26,-16.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="331" y="-67.5" font-family="Helvetica,sans-Serif" font-size="10.00">Locality</text>
|
||||
<polyline fill="none" stroke="black" points="288.26,-60.5 373.74,-60.5"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="296.26" y="-47.5" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="296.26" y="-35.5" font-family="Helvetica,sans-Serif" font-size="10.00">province_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="296.26" y="-23.5" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
</g>
|
||||
<!-- province->locality -->
|
||||
<g id="edge53" class="edge">
|
||||
<title>province->locality</title>
|
||||
<path fill="none" stroke="#666666" d="M331,-150.77C331,-150.77 331,-92.24 331,-92.24"/>
|
||||
<polygon fill="#666666" stroke="#666666" points="334.5,-92.24 331,-82.24 327.5,-92.24 334.5,-92.24"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="337.22" y="-101.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- medication -->
|
||||
<g id="node40" class="node">
|
||||
<title>medication</title>
|
||||
<polygon fill="#cfd8dc" stroke="black" points="937.19,-127.1 937.19,-227.1 1048.81,-227.1 1048.81,-127.1 937.19,-127.1"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="993" y="-214.1" font-family="Helvetica,sans-Serif" font-size="10.00">Medication</text>
|
||||
<polyline fill="none" stroke="black" points="937.19,-207.1 1048.81,-207.1"/>
|
||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-194.1" font-family="Helvetica,sans-Serif" font-size="10.00">id: PK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-182.1" font-family="Helvetica,sans-Serif" font-size="10.00">name: str</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-170.1" font-family="Helvetica,sans-Serif" font-size="10.00">general_name_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-158.1" font-family="Helvetica,sans-Serif" font-size="10.00">type_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-146.1" font-family="Helvetica,sans-Serif" font-size="10.00">presentation_id: FK</text>
|
||||
<text xml:space="preserve" text-anchor="start" x="945.19" y="-134.1" font-family="Helvetica,sans-Serif" font-size="10.00">pet_type: str</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 84 KiB |
195
atlas/book/arch-model/04-data-model-simple.dot
Normal file
195
atlas/book/arch-model/04-data-model-simple.dot
Normal file
@@ -0,0 +1,195 @@
|
||||
digraph DataModelSimple {
|
||||
rankdir=TB
|
||||
compound=true
|
||||
splines=ortho
|
||||
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10]
|
||||
edge [fontname="Helvetica", fontsize=8]
|
||||
nodesep=0.3
|
||||
ranksep=1.2
|
||||
newrank=true
|
||||
|
||||
label="AMAR Mascotas - Data Model Overview"
|
||||
labelloc="t"
|
||||
fontsize=16
|
||||
fontname="Helvetica-Bold"
|
||||
|
||||
// === ROW 1: AUTH (center) ===
|
||||
subgraph cluster_auth {
|
||||
label="Users & Auth"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E8F5E9"
|
||||
color="#2E7D32"
|
||||
auth_user [label="auth.User", fillcolor="#C8E6C9"]
|
||||
}
|
||||
|
||||
// === ROW 2: LEFT COLUMN - Pet Owners ===
|
||||
subgraph cluster_mascotas {
|
||||
label="Pet Owners & Pets"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E3F2FD"
|
||||
color="#1565C0"
|
||||
|
||||
petowner [label="PetOwner", fillcolor="#BBDEFB"]
|
||||
pet [label="Pet", fillcolor="#BBDEFB"]
|
||||
petvaccine [label="PetVaccine", fillcolor="#90CAF9"]
|
||||
petstudy [label="PetStudy", fillcolor="#90CAF9"]
|
||||
}
|
||||
|
||||
// === ROW 2: CENTER COLUMN - Veterinarians ===
|
||||
subgraph cluster_vets {
|
||||
label="Veterinarians"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFF3E0"
|
||||
color="#E65100"
|
||||
|
||||
veterinarian [label="Veterinarian", fillcolor="#FFE0B2"]
|
||||
availability [label="Availability", fillcolor="#FFCC80"]
|
||||
unavailability [label="Unavailability", fillcolor="#FFCC80"]
|
||||
vet_specialty [label="Vet-Specialty", shape=diamond, fillcolor="#FFB74D"]
|
||||
vet_neighborhood [label="Vet-Neighborhood", shape=diamond, fillcolor="#FFB74D"]
|
||||
}
|
||||
|
||||
// === ROW 2: RIGHT COLUMN - Services ===
|
||||
subgraph cluster_productos {
|
||||
label="Services & Pricing"
|
||||
style="rounded,filled"
|
||||
fillcolor="#F3E5F5"
|
||||
color="#7B1FA2"
|
||||
|
||||
grupo [label="Group", fillcolor="#E1BEE7"]
|
||||
category [label="Category", fillcolor="#E1BEE7"]
|
||||
service [label="Service", fillcolor="#CE93D8"]
|
||||
prices [label="Prices", fillcolor="#BA68C8"]
|
||||
discounts [label="Discounts", fillcolor="#BA68C8"]
|
||||
}
|
||||
|
||||
// === ROW 3: LEFT - Cart ===
|
||||
subgraph cluster_cart {
|
||||
label="Cart & Checkout"
|
||||
style="rounded,filled"
|
||||
fillcolor="#E0F7FA"
|
||||
color="#00838F"
|
||||
|
||||
cart [label="Cart", fillcolor="#B2EBF2"]
|
||||
cartitem [label="CartItem", fillcolor="#80DEEA"]
|
||||
cartresumeitem [label="CartResumeItem", fillcolor="#80DEEA"]
|
||||
cartpetreason [label="CartPetReason", fillcolor="#80DEEA"]
|
||||
}
|
||||
|
||||
// === ROW 3: CENTER - Requests ===
|
||||
subgraph cluster_solicitudes {
|
||||
label="Service Requests"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFEBEE"
|
||||
color="#C62828"
|
||||
|
||||
servicerequest [label="ServiceRequest", fillcolor="#FFCDD2"]
|
||||
statehistory [label="StateHistory", fillcolor="#EF9A9A"]
|
||||
vetasked [label="VeterinarianAsked", fillcolor="#EF9A9A"]
|
||||
vetreminder [label="VetReminder", fillcolor="#E57373"]
|
||||
payreminder [label="PayReminder", fillcolor="#E57373"]
|
||||
}
|
||||
|
||||
// === ROW 3: RIGHT - Visits ===
|
||||
subgraph cluster_visits {
|
||||
label="Veterinary Visits"
|
||||
style="rounded,filled"
|
||||
fillcolor="#FFFDE7"
|
||||
color="#F9A825"
|
||||
|
||||
vetvisit [label="VetVisit", fillcolor="#FFF9C4"]
|
||||
vetvisitreport [label="VetVisitReport", fillcolor="#FFF59D"]
|
||||
vetvisitfollowup [label="FollowUp", fillcolor="#FFF176"]
|
||||
vetvisitpetreason [label="VisitPetReason", fillcolor="#FFF176"]
|
||||
visit_pets [label="Visit-Pets", shape=diamond, fillcolor="#FFEE58"]
|
||||
receipt [label="Receipt\n(AFIP)", fillcolor="#F8BBD9"]
|
||||
}
|
||||
|
||||
// === ROW 4: REFERENCE DATA (bottom, full width) ===
|
||||
subgraph cluster_reference {
|
||||
label="Reference Data"
|
||||
style="rounded,filled"
|
||||
fillcolor="#ECEFF1"
|
||||
color="#455A64"
|
||||
|
||||
subgraph {
|
||||
rank=same
|
||||
specialty [label="Specialty", fillcolor="#CFD8DC"]
|
||||
neighborhood [label="Neighborhood", fillcolor="#CFD8DC"]
|
||||
province [label="Province", fillcolor="#CFD8DC"]
|
||||
locality [label="Locality", fillcolor="#CFD8DC"]
|
||||
petbreed [label="PetBreed", fillcolor="#CFD8DC"]
|
||||
vaccine [label="Vaccine", fillcolor="#CFD8DC"]
|
||||
}
|
||||
subgraph {
|
||||
rank=same
|
||||
study [label="Study", fillcolor="#CFD8DC"]
|
||||
campaign [label="Campaign", fillcolor="#CFD8DC"]
|
||||
tag [label="Tag", fillcolor="#CFD8DC"]
|
||||
medication [label="Medication", fillcolor="#CFD8DC"]
|
||||
turnfeegroup [label="TurnFeeGroup", fillcolor="#CFD8DC"]
|
||||
}
|
||||
specialty -> study [style=invis]
|
||||
province -> locality
|
||||
}
|
||||
|
||||
// === FORCE COLUMN ALIGNMENT WITH INVISIBLE EDGES ===
|
||||
// Column 1: Pets -> Cart
|
||||
petowner -> cart [style=invis, weight=10]
|
||||
// Column 2: Vets -> Requests
|
||||
veterinarian -> servicerequest [style=invis, weight=10]
|
||||
// Column 3: Services -> Visits
|
||||
service -> vetvisit [style=invis, weight=10]
|
||||
|
||||
// Force Reference Data to bottom
|
||||
cart -> specialty [style=invis, weight=10]
|
||||
servicerequest -> campaign [style=invis, weight=10]
|
||||
vetvisit -> turnfeegroup [style=invis, weight=10]
|
||||
|
||||
// === INTERNAL CLUSTER EDGES ===
|
||||
petowner -> pet [label="1:N"]
|
||||
pet -> petvaccine [label="1:N"]
|
||||
pet -> petstudy [label="1:N"]
|
||||
|
||||
veterinarian -> availability
|
||||
veterinarian -> unavailability
|
||||
veterinarian -> vet_specialty
|
||||
veterinarian -> vet_neighborhood
|
||||
|
||||
grupo -> category [label="1:N"]
|
||||
category -> service [label="1:N"]
|
||||
service -> prices [label="1:N"]
|
||||
service -> discounts [label="1:N"]
|
||||
|
||||
cart -> cartitem [label="1:N"]
|
||||
cart -> cartresumeitem
|
||||
cart -> cartpetreason
|
||||
|
||||
servicerequest -> statehistory [label="1:N"]
|
||||
servicerequest -> vetasked [label="1:N"]
|
||||
servicerequest -> payreminder
|
||||
vetasked -> vetreminder
|
||||
|
||||
vetvisit -> visit_pets
|
||||
vetvisit -> vetvisitreport [label="1:N"]
|
||||
vetvisit -> vetvisitpetreason
|
||||
vetvisitreport -> vetvisitfollowup
|
||||
vetvisit -> receipt [style=dashed]
|
||||
|
||||
// === CROSS-CLUSTER RELATIONSHIPS ===
|
||||
auth_user -> petowner [label="1:1 opt", style=dashed]
|
||||
auth_user -> veterinarian [label="1:1"]
|
||||
|
||||
petowner -> cart [label="1:N"]
|
||||
petowner -> servicerequest [label="1:N"]
|
||||
servicerequest -> cart [label="1:1", constraint=false]
|
||||
vetasked -> veterinarian [constraint=false]
|
||||
vetvisit -> servicerequest [style=dashed, constraint=false]
|
||||
|
||||
// Reference links
|
||||
petvaccine -> vaccine [constraint=false]
|
||||
petstudy -> study [constraint=false]
|
||||
vet_specialty -> specialty [constraint=false]
|
||||
vet_neighborhood -> neighborhood [constraint=false]
|
||||
cartitem -> service [constraint=false]
|
||||
}
|
||||
540
atlas/book/arch-model/04-data-model-simple.svg
Normal file
540
atlas/book/arch-model/04-data-model-simple.svg
Normal file
@@ -0,0 +1,540 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 14.0.5 (0)
|
||||
-->
|
||||
<!-- Title: DataModelSimple Pages: 1 -->
|
||||
<svg width="2433pt" height="780pt"
|
||||
viewBox="0.00 0.00 2433.00 780.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 776.4)">
|
||||
<title>DataModelSimple</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-776.4 2429,-776.4 2429,4 -4,4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1212.5" y="-753.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">AMAR Mascotas - Data Model Overview</text>
|
||||
<g id="clust1" class="cluster">
|
||||
<title>cluster_auth</title>
|
||||
<path fill="#e8f5e9" stroke="#2e7d32" d="M890,-658C890,-658 975,-658 975,-658 981,-658 987,-664 987,-670 987,-670 987,-725.2 987,-725.2 987,-731.2 981,-737.2 975,-737.2 975,-737.2 890,-737.2 890,-737.2 884,-737.2 878,-731.2 878,-725.2 878,-725.2 878,-670 878,-670 878,-664 884,-658 890,-658"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="932.5" y="-718" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Users & Auth</text>
|
||||
</g>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_mascotas</title>
|
||||
<path fill="#e3f2fd" stroke="#1565c0" d="M1662,-262.6C1662,-262.6 1799,-262.6 1799,-262.6 1805,-262.6 1811,-268.6 1811,-274.6 1811,-274.6 1811,-593.4 1811,-593.4 1811,-599.4 1805,-605.4 1799,-605.4 1799,-605.4 1662,-605.4 1662,-605.4 1656,-605.4 1650,-599.4 1650,-593.4 1650,-593.4 1650,-274.6 1650,-274.6 1650,-268.6 1656,-262.6 1662,-262.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1730.5" y="-586.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Pet Owners & Pets</text>
|
||||
</g>
|
||||
<g id="clust3" class="cluster">
|
||||
<title>cluster_vets</title>
|
||||
<path fill="#fff3e0" stroke="#e65100" d="M20,-262.6C20,-262.6 509,-262.6 509,-262.6 515,-262.6 521,-268.6 521,-274.6 521,-274.6 521,-461.6 521,-461.6 521,-467.6 515,-473.6 509,-473.6 509,-473.6 20,-473.6 20,-473.6 14,-473.6 8,-467.6 8,-461.6 8,-461.6 8,-274.6 8,-274.6 8,-268.6 14,-262.6 20,-262.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="264.5" y="-454.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinarians</text>
|
||||
</g>
|
||||
<g id="clust4" class="cluster">
|
||||
<title>cluster_productos</title>
|
||||
<path fill="#f3e5f5" stroke="#7b1fa2" d="M2277,-262.6C2277,-262.6 2405,-262.6 2405,-262.6 2411,-262.6 2417,-268.6 2417,-274.6 2417,-274.6 2417,-725.2 2417,-725.2 2417,-731.2 2411,-737.2 2405,-737.2 2405,-737.2 2277,-737.2 2277,-737.2 2271,-737.2 2265,-731.2 2265,-725.2 2265,-725.2 2265,-274.6 2265,-274.6 2265,-268.6 2271,-262.6 2277,-262.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2341" y="-718" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Services & Pricing</text>
|
||||
</g>
|
||||
<g id="clust5" class="cluster">
|
||||
<title>cluster_cart</title>
|
||||
<path fill="#e0f7fa" stroke="#00838f" d="M853,-262.6C853,-262.6 1119,-262.6 1119,-262.6 1125,-262.6 1131,-268.6 1131,-274.6 1131,-274.6 1131,-461.6 1131,-461.6 1131,-467.6 1125,-473.6 1119,-473.6 1119,-473.6 853,-473.6 853,-473.6 847,-473.6 841,-467.6 841,-461.6 841,-461.6 841,-274.6 841,-274.6 841,-268.6 847,-262.6 853,-262.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="986" y="-454.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Cart & Checkout</text>
|
||||
</g>
|
||||
<g id="clust6" class="cluster">
|
||||
<title>cluster_solicitudes</title>
|
||||
<path fill="#ffebee" stroke="#c62828" d="M541,-8C541,-8 821,-8 821,-8 827,-8 833,-14 833,-20 833,-20 833,-329.8 833,-329.8 833,-335.8 827,-341.8 821,-341.8 821,-341.8 541,-341.8 541,-341.8 535,-341.8 529,-335.8 529,-329.8 529,-329.8 529,-20 529,-20 529,-14 535,-8 541,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="681" y="-322.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Service Requests</text>
|
||||
</g>
|
||||
<g id="clust7" class="cluster">
|
||||
<title>cluster_visits</title>
|
||||
<path fill="#fffde7" stroke="#f9a825" d="M1869,-8C1869,-8 2245,-8 2245,-8 2251,-8 2257,-14 2257,-20 2257,-20 2257,-329.8 2257,-329.8 2257,-335.8 2251,-341.8 2245,-341.8 2245,-341.8 1869,-341.8 1869,-341.8 1863,-341.8 1857,-335.8 1857,-329.8 1857,-329.8 1857,-20 1857,-20 1857,-14 1863,-8 1869,-8"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2057" y="-322.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Veterinary Visits</text>
|
||||
</g>
|
||||
<g id="clust8" class="cluster">
|
||||
<title>cluster_reference</title>
|
||||
<path fill="#eceff1" stroke="#455a64" d="M1151,-131C1151,-131 1630,-131 1630,-131 1636,-131 1642,-137 1642,-143 1642,-143 1642,-329.8 1642,-329.8 1642,-335.8 1636,-341.8 1630,-341.8 1630,-341.8 1151,-341.8 1151,-341.8 1145,-341.8 1139,-335.8 1139,-329.8 1139,-329.8 1139,-143 1139,-143 1139,-137 1145,-131 1151,-131"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1390.5" y="-322.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="16.00">Reference Data</text>
|
||||
</g>
|
||||
<!-- auth_user -->
|
||||
<g id="node1" class="node">
|
||||
<title>auth_user</title>
|
||||
<path fill="#c8e6c9" stroke="black" d="M949.68,-702C949.68,-702 914.32,-702 914.32,-702 908.32,-702 902.32,-696 902.32,-690 902.32,-690 902.32,-678 902.32,-678 902.32,-672 908.32,-666 914.32,-666 914.32,-666 949.68,-666 949.68,-666 955.68,-666 961.68,-672 961.68,-678 961.68,-678 961.68,-690 961.68,-690 961.68,-696 955.68,-702 949.68,-702"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="932" y="-681" font-family="Helvetica,sans-Serif" font-size="10.00">auth.User</text>
|
||||
</g>
|
||||
<!-- petowner -->
|
||||
<g id="node2" class="node">
|
||||
<title>petowner</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M1706.23,-570.2C1706.23,-570.2 1669.77,-570.2 1669.77,-570.2 1663.77,-570.2 1657.77,-564.2 1657.77,-558.2 1657.77,-558.2 1657.77,-546.2 1657.77,-546.2 1657.77,-540.2 1663.77,-534.2 1669.77,-534.2 1669.77,-534.2 1706.23,-534.2 1706.23,-534.2 1712.23,-534.2 1718.23,-540.2 1718.23,-546.2 1718.23,-546.2 1718.23,-558.2 1718.23,-558.2 1718.23,-564.2 1712.23,-570.2 1706.23,-570.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1688" y="-549.2" font-family="Helvetica,sans-Serif" font-size="10.00">PetOwner</text>
|
||||
</g>
|
||||
<!-- auth_user->petowner -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>auth_user->petowner</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M943.44,-665.62C943.44,-631.77 943.44,-563 943.44,-563 943.44,-563 1645.91,-563 1645.91,-563"/>
|
||||
<polygon fill="black" stroke="black" points="1645.91,-566.5 1655.91,-563 1645.91,-559.5 1645.91,-566.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1213.23" y="-615.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:1 opt</text>
|
||||
</g>
|
||||
<!-- veterinarian -->
|
||||
<g id="node6" class="node">
|
||||
<title>veterinarian</title>
|
||||
<path fill="#ffe0b2" stroke="black" d="M500.96,-438.4C500.96,-438.4 455.04,-438.4 455.04,-438.4 449.04,-438.4 443.04,-432.4 443.04,-426.4 443.04,-426.4 443.04,-414.4 443.04,-414.4 443.04,-408.4 449.04,-402.4 455.04,-402.4 455.04,-402.4 500.96,-402.4 500.96,-402.4 506.96,-402.4 512.96,-408.4 512.96,-414.4 512.96,-414.4 512.96,-426.4 512.96,-426.4 512.96,-432.4 506.96,-438.4 500.96,-438.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="478" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Veterinarian</text>
|
||||
</g>
|
||||
<!-- auth_user->veterinarian -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>auth_user->veterinarian</title>
|
||||
<path fill="none" stroke="black" d="M914.19,-665.54C914.19,-607.58 914.19,-433 914.19,-433 914.19,-433 524.84,-433 524.84,-433"/>
|
||||
<polygon fill="black" stroke="black" points="524.84,-429.5 514.84,-433 524.84,-436.5 524.84,-429.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="667.56" y="-549.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
||||
</g>
|
||||
<!-- pet -->
|
||||
<g id="node3" class="node">
|
||||
<title>pet</title>
|
||||
<path fill="#bbdefb" stroke="black" d="M1706,-438.4C1706,-438.4 1676,-438.4 1676,-438.4 1670,-438.4 1664,-432.4 1664,-426.4 1664,-426.4 1664,-414.4 1664,-414.4 1664,-408.4 1670,-402.4 1676,-402.4 1676,-402.4 1706,-402.4 1706,-402.4 1712,-402.4 1718,-408.4 1718,-414.4 1718,-414.4 1718,-426.4 1718,-426.4 1718,-432.4 1712,-438.4 1706,-438.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1691" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Pet</text>
|
||||
</g>
|
||||
<!-- petowner->pet -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>petowner->pet</title>
|
||||
<path fill="none" stroke="black" d="M1691,-533.87C1691,-533.87 1691,-450.08 1691,-450.08"/>
|
||||
<polygon fill="black" stroke="black" points="1694.5,-450.08 1691,-440.08 1687.5,-450.08 1694.5,-450.08"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1696.22" y="-484" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- cart -->
|
||||
<g id="node16" class="node">
|
||||
<title>cart</title>
|
||||
<path fill="#b2ebf2" stroke="black" d="M1111,-438.4C1111,-438.4 1081,-438.4 1081,-438.4 1075,-438.4 1069,-432.4 1069,-426.4 1069,-426.4 1069,-414.4 1069,-414.4 1069,-408.4 1075,-402.4 1081,-402.4 1081,-402.4 1111,-402.4 1111,-402.4 1117,-402.4 1123,-408.4 1123,-414.4 1123,-414.4 1123,-426.4 1123,-426.4 1123,-432.4 1117,-438.4 1111,-438.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1096" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Cart</text>
|
||||
</g>
|
||||
<!-- petowner->cart -->
|
||||
<!-- petowner->cart -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>petowner->cart</title>
|
||||
<path fill="none" stroke="black" d="M1657.48,-541C1537.52,-541 1105,-541 1105,-541 1105,-541 1105,-450.23 1105,-450.23"/>
|
||||
<polygon fill="black" stroke="black" points="1108.5,-450.23 1105,-440.23 1101.5,-450.23 1108.5,-450.23"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1233.22" y="-484" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- servicerequest -->
|
||||
<g id="node20" class="node">
|
||||
<title>servicerequest</title>
|
||||
<path fill="#ffcdd2" stroke="black" d="M763.3,-306.6C763.3,-306.6 700.7,-306.6 700.7,-306.6 694.7,-306.6 688.7,-300.6 688.7,-294.6 688.7,-294.6 688.7,-282.6 688.7,-282.6 688.7,-276.6 694.7,-270.6 700.7,-270.6 700.7,-270.6 763.3,-270.6 763.3,-270.6 769.3,-270.6 775.3,-276.6 775.3,-282.6 775.3,-282.6 775.3,-294.6 775.3,-294.6 775.3,-300.6 769.3,-306.6 763.3,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="732" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">ServiceRequest</text>
|
||||
</g>
|
||||
<!-- petowner->servicerequest -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>petowner->servicerequest</title>
|
||||
<path fill="none" stroke="black" d="M1657.27,-556C1493.64,-556 732,-556 732,-556 732,-556 732,-318.49 732,-318.49"/>
|
||||
<polygon fill="black" stroke="black" points="735.5,-318.49 732,-308.49 728.5,-318.49 735.5,-318.49"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="782.22" y="-418" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- petvaccine -->
|
||||
<g id="node4" class="node">
|
||||
<title>petvaccine</title>
|
||||
<path fill="#90caf9" stroke="black" d="M1712.29,-306.6C1712.29,-306.6 1669.71,-306.6 1669.71,-306.6 1663.71,-306.6 1657.71,-300.6 1657.71,-294.6 1657.71,-294.6 1657.71,-282.6 1657.71,-282.6 1657.71,-276.6 1663.71,-270.6 1669.71,-270.6 1669.71,-270.6 1712.29,-270.6 1712.29,-270.6 1718.29,-270.6 1724.29,-276.6 1724.29,-282.6 1724.29,-282.6 1724.29,-294.6 1724.29,-294.6 1724.29,-300.6 1718.29,-306.6 1712.29,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1691" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">PetVaccine</text>
|
||||
</g>
|
||||
<!-- pet->petvaccine -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>pet->petvaccine</title>
|
||||
<path fill="none" stroke="black" d="M1691,-402.07C1691,-402.07 1691,-318.28 1691,-318.28"/>
|
||||
<polygon fill="black" stroke="black" points="1694.5,-318.28 1691,-308.28 1687.5,-318.28 1694.5,-318.28"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1697.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- petstudy -->
|
||||
<g id="node5" class="node">
|
||||
<title>petstudy</title>
|
||||
<path fill="#90caf9" stroke="black" d="M1791.29,-306.6C1791.29,-306.6 1758.71,-306.6 1758.71,-306.6 1752.71,-306.6 1746.71,-300.6 1746.71,-294.6 1746.71,-294.6 1746.71,-282.6 1746.71,-282.6 1746.71,-276.6 1752.71,-270.6 1758.71,-270.6 1758.71,-270.6 1791.29,-270.6 1791.29,-270.6 1797.29,-270.6 1803.29,-276.6 1803.29,-282.6 1803.29,-282.6 1803.29,-294.6 1803.29,-294.6 1803.29,-300.6 1797.29,-306.6 1791.29,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1775" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">PetStudy</text>
|
||||
</g>
|
||||
<!-- pet->petstudy -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>pet->petstudy</title>
|
||||
<path fill="none" stroke="black" d="M1718.04,-414C1742.6,-414 1775,-414 1775,-414 1775,-414 1775,-318.35 1775,-318.35"/>
|
||||
<polygon fill="black" stroke="black" points="1778.5,-318.35 1775,-308.35 1771.5,-318.35 1778.5,-318.35"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1758.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vaccine -->
|
||||
<g id="node36" class="node">
|
||||
<title>vaccine</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1189,-306.6C1189,-306.6 1159,-306.6 1159,-306.6 1153,-306.6 1147,-300.6 1147,-294.6 1147,-294.6 1147,-282.6 1147,-282.6 1147,-276.6 1153,-270.6 1159,-270.6 1159,-270.6 1189,-270.6 1189,-270.6 1195,-270.6 1201,-276.6 1201,-282.6 1201,-282.6 1201,-294.6 1201,-294.6 1201,-300.6 1195,-306.6 1189,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1174" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Vaccine</text>
|
||||
</g>
|
||||
<!-- petvaccine->vaccine -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>petvaccine->vaccine</title>
|
||||
<path fill="none" stroke="black" d="M1691,-270.32C1691,-257.63 1691,-243 1691,-243 1691,-243 1174.1,-243 1174.1,-243 1174.1,-243 1174.1,-258.8 1174.1,-258.8"/>
|
||||
<polygon fill="black" stroke="black" points="1170.6,-258.8 1174.1,-268.8 1177.6,-258.8 1170.6,-258.8"/>
|
||||
</g>
|
||||
<!-- study -->
|
||||
<g id="node37" class="node">
|
||||
<title>study</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1356,-175C1356,-175 1326,-175 1326,-175 1320,-175 1314,-169 1314,-163 1314,-163 1314,-151 1314,-151 1314,-145 1320,-139 1326,-139 1326,-139 1356,-139 1356,-139 1362,-139 1368,-145 1368,-151 1368,-151 1368,-163 1368,-163 1368,-169 1362,-175 1356,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1341" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Study</text>
|
||||
</g>
|
||||
<!-- petstudy->study -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>petstudy->study</title>
|
||||
<path fill="none" stroke="black" d="M1775,-270.23C1775,-244.81 1775,-202 1775,-202 1775,-202 1353.18,-202 1353.18,-202 1353.18,-202 1353.18,-186.82 1353.18,-186.82"/>
|
||||
<polygon fill="black" stroke="black" points="1356.68,-186.82 1353.18,-176.82 1349.68,-186.82 1356.68,-186.82"/>
|
||||
</g>
|
||||
<!-- availability -->
|
||||
<g id="node7" class="node">
|
||||
<title>availability</title>
|
||||
<path fill="#ffcc80" stroke="black" d="M67.62,-306.6C67.62,-306.6 28.38,-306.6 28.38,-306.6 22.38,-306.6 16.38,-300.6 16.38,-294.6 16.38,-294.6 16.38,-282.6 16.38,-282.6 16.38,-276.6 22.38,-270.6 28.38,-270.6 28.38,-270.6 67.62,-270.6 67.62,-270.6 73.62,-270.6 79.62,-276.6 79.62,-282.6 79.62,-282.6 79.62,-294.6 79.62,-294.6 79.62,-300.6 73.62,-306.6 67.62,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="48" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Availability</text>
|
||||
</g>
|
||||
<!-- veterinarian->availability -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>veterinarian->availability</title>
|
||||
<path fill="none" stroke="black" d="M442.81,-429C340.11,-429 48,-429 48,-429 48,-429 48,-318.47 48,-318.47"/>
|
||||
<polygon fill="black" stroke="black" points="51.5,-318.47 48,-308.47 44.5,-318.47 51.5,-318.47"/>
|
||||
</g>
|
||||
<!-- unavailability -->
|
||||
<g id="node8" class="node">
|
||||
<title>unavailability</title>
|
||||
<path fill="#ffcc80" stroke="black" d="M164.46,-306.6C164.46,-306.6 113.54,-306.6 113.54,-306.6 107.54,-306.6 101.54,-300.6 101.54,-294.6 101.54,-294.6 101.54,-282.6 101.54,-282.6 101.54,-276.6 107.54,-270.6 113.54,-270.6 113.54,-270.6 164.46,-270.6 164.46,-270.6 170.46,-270.6 176.46,-276.6 176.46,-282.6 176.46,-282.6 176.46,-294.6 176.46,-294.6 176.46,-300.6 170.46,-306.6 164.46,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="139" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Unavailability</text>
|
||||
</g>
|
||||
<!-- veterinarian->unavailability -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>veterinarian->unavailability</title>
|
||||
<path fill="none" stroke="black" d="M442.68,-420C355.85,-420 139,-420 139,-420 139,-420 139,-318.38 139,-318.38"/>
|
||||
<polygon fill="black" stroke="black" points="142.5,-318.38 139,-308.38 135.5,-318.38 142.5,-318.38"/>
|
||||
</g>
|
||||
<!-- vet_specialty -->
|
||||
<g id="node9" class="node">
|
||||
<title>vet_specialty</title>
|
||||
<path fill="#ffb74d" stroke="black" d="M250.45,-303.34C250.45,-303.34 209.84,-291.86 209.84,-291.86 204.06,-290.23 204.06,-286.97 209.84,-285.34 209.84,-285.34 250.45,-273.86 250.45,-273.86 256.23,-272.23 267.77,-272.23 273.55,-273.86 273.55,-273.86 314.16,-285.34 314.16,-285.34 319.94,-286.97 319.94,-290.23 314.16,-291.86 314.16,-291.86 273.55,-303.34 273.55,-303.34 267.77,-304.97 256.23,-304.97 250.45,-303.34"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="262" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Vet-Specialty</text>
|
||||
</g>
|
||||
<!-- veterinarian->vet_specialty -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>veterinarian->vet_specialty</title>
|
||||
<path fill="none" stroke="black" d="M442.82,-411C381.61,-411 262,-411 262,-411 262,-411 262,-318.44 262,-318.44"/>
|
||||
<polygon fill="black" stroke="black" points="265.5,-318.44 262,-308.44 258.5,-318.44 265.5,-318.44"/>
|
||||
</g>
|
||||
<!-- vet_neighborhood -->
|
||||
<g id="node10" class="node">
|
||||
<title>vet_neighborhood</title>
|
||||
<path fill="#ffb74d" stroke="black" d="M418.27,-304.05C418.27,-304.05 359.09,-291.15 359.09,-291.15 353.23,-289.88 353.23,-287.32 359.09,-286.05 359.09,-286.05 418.27,-273.15 418.27,-273.15 424.14,-271.88 435.86,-271.88 441.73,-273.15 441.73,-273.15 500.91,-286.05 500.91,-286.05 506.77,-287.32 506.77,-289.88 500.91,-291.15 500.91,-291.15 441.73,-304.05 441.73,-304.05 435.86,-305.32 424.14,-305.32 418.27,-304.05"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="430" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Vet-Neighborhood</text>
|
||||
</g>
|
||||
<!-- veterinarian->vet_neighborhood -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>veterinarian->vet_neighborhood</title>
|
||||
<path fill="none" stroke="black" d="M466.24,-402.07C466.24,-402.07 466.24,-310.63 466.24,-310.63"/>
|
||||
<polygon fill="black" stroke="black" points="469.74,-310.63 466.24,-300.63 462.74,-310.63 469.74,-310.63"/>
|
||||
</g>
|
||||
<!-- veterinarian->servicerequest -->
|
||||
<!-- specialty -->
|
||||
<g id="node31" class="node">
|
||||
<title>specialty</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1622.29,-306.6C1622.29,-306.6 1589.71,-306.6 1589.71,-306.6 1583.71,-306.6 1577.71,-300.6 1577.71,-294.6 1577.71,-294.6 1577.71,-282.6 1577.71,-282.6 1577.71,-276.6 1583.71,-270.6 1589.71,-270.6 1589.71,-270.6 1622.29,-270.6 1622.29,-270.6 1628.29,-270.6 1634.29,-276.6 1634.29,-282.6 1634.29,-282.6 1634.29,-294.6 1634.29,-294.6 1634.29,-300.6 1628.29,-306.6 1622.29,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1606" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Specialty</text>
|
||||
</g>
|
||||
<!-- vet_specialty->specialty -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>vet_specialty->specialty</title>
|
||||
<path fill="none" stroke="black" d="M262,-270.22C262,-263.27 262,-257 262,-257 262,-257 1591.73,-257 1591.73,-257 1591.73,-257 1591.73,-258.7 1591.73,-258.7"/>
|
||||
<polygon fill="black" stroke="black" points="1588.23,-258.7 1591.73,-268.7 1595.23,-258.7 1588.23,-258.7"/>
|
||||
</g>
|
||||
<!-- neighborhood -->
|
||||
<g id="node32" class="node">
|
||||
<title>neighborhood</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1289.41,-306.6C1289.41,-306.6 1234.59,-306.6 1234.59,-306.6 1228.59,-306.6 1222.59,-300.6 1222.59,-294.6 1222.59,-294.6 1222.59,-282.6 1222.59,-282.6 1222.59,-276.6 1228.59,-270.6 1234.59,-270.6 1234.59,-270.6 1289.41,-270.6 1289.41,-270.6 1295.41,-270.6 1301.41,-276.6 1301.41,-282.6 1301.41,-282.6 1301.41,-294.6 1301.41,-294.6 1301.41,-300.6 1295.41,-306.6 1289.41,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1262" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Neighborhood</text>
|
||||
</g>
|
||||
<!-- vet_neighborhood->neighborhood -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>vet_neighborhood->neighborhood</title>
|
||||
<path fill="none" stroke="black" d="M489.44,-293.99C489.44,-307 489.44,-339 489.44,-339 489.44,-339 1262,-339 1262,-339 1262,-339 1262,-318.59 1262,-318.59"/>
|
||||
<polygon fill="black" stroke="black" points="1265.5,-318.59 1262,-308.59 1258.5,-318.59 1265.5,-318.59"/>
|
||||
</g>
|
||||
<!-- grupo -->
|
||||
<g id="node11" class="node">
|
||||
<title>grupo</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M2316,-702C2316,-702 2286,-702 2286,-702 2280,-702 2274,-696 2274,-690 2274,-690 2274,-678 2274,-678 2274,-672 2280,-666 2286,-666 2286,-666 2316,-666 2316,-666 2322,-666 2328,-672 2328,-678 2328,-678 2328,-690 2328,-690 2328,-696 2322,-702 2316,-702"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2301" y="-681" font-family="Helvetica,sans-Serif" font-size="10.00">Group</text>
|
||||
</g>
|
||||
<!-- category -->
|
||||
<g id="node12" class="node">
|
||||
<title>category</title>
|
||||
<path fill="#e1bee7" stroke="black" d="M2317.29,-570.2C2317.29,-570.2 2284.71,-570.2 2284.71,-570.2 2278.71,-570.2 2272.71,-564.2 2272.71,-558.2 2272.71,-558.2 2272.71,-546.2 2272.71,-546.2 2272.71,-540.2 2278.71,-534.2 2284.71,-534.2 2284.71,-534.2 2317.29,-534.2 2317.29,-534.2 2323.29,-534.2 2329.29,-540.2 2329.29,-546.2 2329.29,-546.2 2329.29,-558.2 2329.29,-558.2 2329.29,-564.2 2323.29,-570.2 2317.29,-570.2"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2301" y="-549.2" font-family="Helvetica,sans-Serif" font-size="10.00">Category</text>
|
||||
</g>
|
||||
<!-- grupo->category -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>grupo->category</title>
|
||||
<path fill="none" stroke="black" d="M2301,-665.67C2301,-665.67 2301,-581.88 2301,-581.88"/>
|
||||
<polygon fill="black" stroke="black" points="2304.5,-581.88 2301,-571.88 2297.5,-581.88 2304.5,-581.88"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2307.22" y="-615.8" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- service -->
|
||||
<g id="node13" class="node">
|
||||
<title>service</title>
|
||||
<path fill="#ce93d8" stroke="black" d="M2315,-438.4C2315,-438.4 2285,-438.4 2285,-438.4 2279,-438.4 2273,-432.4 2273,-426.4 2273,-426.4 2273,-414.4 2273,-414.4 2273,-408.4 2279,-402.4 2285,-402.4 2285,-402.4 2315,-402.4 2315,-402.4 2321,-402.4 2327,-408.4 2327,-414.4 2327,-414.4 2327,-426.4 2327,-426.4 2327,-432.4 2321,-438.4 2315,-438.4"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2300" y="-417.4" font-family="Helvetica,sans-Serif" font-size="10.00">Service</text>
|
||||
</g>
|
||||
<!-- category->service -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>category->service</title>
|
||||
<path fill="none" stroke="black" d="M2300,-533.87C2300,-533.87 2300,-450.08 2300,-450.08"/>
|
||||
<polygon fill="black" stroke="black" points="2303.5,-450.08 2300,-440.08 2296.5,-450.08 2303.5,-450.08"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2306.22" y="-484" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- prices -->
|
||||
<g id="node14" class="node">
|
||||
<title>prices</title>
|
||||
<path fill="#ba68c8" stroke="black" d="M2315,-306.6C2315,-306.6 2285,-306.6 2285,-306.6 2279,-306.6 2273,-300.6 2273,-294.6 2273,-294.6 2273,-282.6 2273,-282.6 2273,-276.6 2279,-270.6 2285,-270.6 2285,-270.6 2315,-270.6 2315,-270.6 2321,-270.6 2327,-276.6 2327,-282.6 2327,-282.6 2327,-294.6 2327,-294.6 2327,-300.6 2321,-306.6 2315,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2300" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Prices</text>
|
||||
</g>
|
||||
<!-- service->prices -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>service->prices</title>
|
||||
<path fill="none" stroke="black" d="M2309,-402.07C2309,-402.07 2309,-318.28 2309,-318.28"/>
|
||||
<polygon fill="black" stroke="black" points="2312.5,-318.28 2309,-308.28 2305.5,-318.28 2312.5,-318.28"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2306.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- discounts -->
|
||||
<g id="node15" class="node">
|
||||
<title>discounts</title>
|
||||
<path fill="#ba68c8" stroke="black" d="M2396.95,-306.6C2396.95,-306.6 2361.05,-306.6 2361.05,-306.6 2355.05,-306.6 2349.05,-300.6 2349.05,-294.6 2349.05,-294.6 2349.05,-282.6 2349.05,-282.6 2349.05,-276.6 2355.05,-270.6 2361.05,-270.6 2361.05,-270.6 2396.95,-270.6 2396.95,-270.6 2402.95,-270.6 2408.95,-276.6 2408.95,-282.6 2408.95,-282.6 2408.95,-294.6 2408.95,-294.6 2408.95,-300.6 2402.95,-306.6 2396.95,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2379" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Discounts</text>
|
||||
</g>
|
||||
<!-- service->discounts -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>service->discounts</title>
|
||||
<path fill="none" stroke="black" d="M2327.19,-420C2350.04,-420 2379,-420 2379,-420 2379,-420 2379,-318.38 2379,-318.38"/>
|
||||
<polygon fill="black" stroke="black" points="2382.5,-318.38 2379,-308.38 2375.5,-318.38 2382.5,-318.38"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2365.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetvisit -->
|
||||
<g id="node25" class="node">
|
||||
<title>vetvisit</title>
|
||||
<path fill="#fff9c4" stroke="black" d="M2081,-306.6C2081,-306.6 2051,-306.6 2051,-306.6 2045,-306.6 2039,-300.6 2039,-294.6 2039,-294.6 2039,-282.6 2039,-282.6 2039,-276.6 2045,-270.6 2051,-270.6 2051,-270.6 2081,-270.6 2081,-270.6 2087,-270.6 2093,-276.6 2093,-282.6 2093,-282.6 2093,-294.6 2093,-294.6 2093,-300.6 2087,-306.6 2081,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2066" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisit</text>
|
||||
</g>
|
||||
<!-- service->vetvisit -->
|
||||
<!-- cartitem -->
|
||||
<g id="node17" class="node">
|
||||
<title>cartitem</title>
|
||||
<path fill="#80deea" stroke="black" d="M891.17,-306.6C891.17,-306.6 860.83,-306.6 860.83,-306.6 854.83,-306.6 848.83,-300.6 848.83,-294.6 848.83,-294.6 848.83,-282.6 848.83,-282.6 848.83,-276.6 854.83,-270.6 860.83,-270.6 860.83,-270.6 891.17,-270.6 891.17,-270.6 897.17,-270.6 903.17,-276.6 903.17,-282.6 903.17,-282.6 903.17,-294.6 903.17,-294.6 903.17,-300.6 897.17,-306.6 891.17,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="876" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">CartItem</text>
|
||||
</g>
|
||||
<!-- cart->cartitem -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>cart->cartitem</title>
|
||||
<path fill="none" stroke="black" d="M1068.78,-413C1011.66,-413 884.49,-413 884.49,-413 884.49,-413 884.49,-318.47 884.49,-318.47"/>
|
||||
<polygon fill="black" stroke="black" points="887.99,-318.47 884.49,-308.47 880.99,-318.47 887.99,-318.47"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="924.22" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- cartresumeitem -->
|
||||
<g id="node18" class="node">
|
||||
<title>cartresumeitem</title>
|
||||
<path fill="#80deea" stroke="black" d="M1004.79,-306.6C1004.79,-306.6 937.21,-306.6 937.21,-306.6 931.21,-306.6 925.21,-300.6 925.21,-294.6 925.21,-294.6 925.21,-282.6 925.21,-282.6 925.21,-276.6 931.21,-270.6 937.21,-270.6 937.21,-270.6 1004.79,-270.6 1004.79,-270.6 1010.79,-270.6 1016.79,-276.6 1016.79,-282.6 1016.79,-282.6 1016.79,-294.6 1016.79,-294.6 1016.79,-300.6 1010.79,-306.6 1004.79,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="971" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">CartResumeItem</text>
|
||||
</g>
|
||||
<!-- cart->cartresumeitem -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>cart->cartresumeitem</title>
|
||||
<path fill="none" stroke="black" d="M1068.5,-408C1036.87,-408 989.23,-408 989.23,-408 989.23,-408 989.23,-318.51 989.23,-318.51"/>
|
||||
<polygon fill="black" stroke="black" points="992.73,-318.51 989.23,-308.51 985.73,-318.51 992.73,-318.51"/>
|
||||
</g>
|
||||
<!-- cartpetreason -->
|
||||
<g id="node19" class="node">
|
||||
<title>cartpetreason</title>
|
||||
<path fill="#80deea" stroke="black" d="M1111.18,-306.6C1111.18,-306.6 1050.82,-306.6 1050.82,-306.6 1044.82,-306.6 1038.82,-300.6 1038.82,-294.6 1038.82,-294.6 1038.82,-282.6 1038.82,-282.6 1038.82,-276.6 1044.82,-270.6 1050.82,-270.6 1050.82,-270.6 1111.18,-270.6 1111.18,-270.6 1117.18,-270.6 1123.18,-276.6 1123.18,-282.6 1123.18,-282.6 1123.18,-294.6 1123.18,-294.6 1123.18,-300.6 1117.18,-306.6 1111.18,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1081" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">CartPetReason</text>
|
||||
</g>
|
||||
<!-- cart->cartpetreason -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>cart->cartpetreason</title>
|
||||
<path fill="none" stroke="black" d="M1096,-402.07C1096,-402.07 1096,-318.28 1096,-318.28"/>
|
||||
<polygon fill="black" stroke="black" points="1099.5,-318.28 1096,-308.28 1092.5,-318.28 1099.5,-318.28"/>
|
||||
</g>
|
||||
<!-- cart->specialty -->
|
||||
<!-- cartitem->service -->
|
||||
<g id="edge43" class="edge">
|
||||
<title>cartitem->service</title>
|
||||
<path fill="none" stroke="black" d="M866.66,-307.07C866.66,-331.09 866.66,-370 866.66,-370 866.66,-370 2291,-370 2291,-370 2291,-370 2291,-390.41 2291,-390.41"/>
|
||||
<polygon fill="black" stroke="black" points="2287.5,-390.41 2291,-400.41 2294.5,-390.41 2287.5,-390.41"/>
|
||||
</g>
|
||||
<!-- servicerequest->cart -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>servicerequest->cart</title>
|
||||
<path fill="none" stroke="black" d="M753.65,-306.82C753.65,-344.61 753.65,-428 753.65,-428 753.65,-428 1057.05,-428 1057.05,-428"/>
|
||||
<polygon fill="black" stroke="black" points="1057.05,-431.5 1067.05,-428 1057.05,-424.5 1057.05,-431.5"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1349.56" y="-352.2" font-family="Helvetica,sans-Serif" font-size="8.00">1:1</text>
|
||||
</g>
|
||||
<!-- statehistory -->
|
||||
<g id="node21" class="node">
|
||||
<title>statehistory</title>
|
||||
<path fill="#ef9a9a" stroke="black" d="M595.23,-175C595.23,-175 548.77,-175 548.77,-175 542.77,-175 536.77,-169 536.77,-163 536.77,-163 536.77,-151 536.77,-151 536.77,-145 542.77,-139 548.77,-139 548.77,-139 595.23,-139 595.23,-139 601.23,-139 607.23,-145 607.23,-151 607.23,-151 607.23,-163 607.23,-163 607.23,-169 601.23,-175 595.23,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="572" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">StateHistory</text>
|
||||
</g>
|
||||
<!-- servicerequest->statehistory -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>servicerequest->statehistory</title>
|
||||
<path fill="none" stroke="black" d="M688.28,-289C640.91,-289 572,-289 572,-289 572,-289 572,-186.87 572,-186.87"/>
|
||||
<polygon fill="black" stroke="black" points="575.5,-186.87 572,-176.87 568.5,-186.87 575.5,-186.87"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="635.22" y="-220.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetasked -->
|
||||
<g id="node22" class="node">
|
||||
<title>vetasked</title>
|
||||
<path fill="#ef9a9a" stroke="black" d="M714.86,-175C714.86,-175 641.14,-175 641.14,-175 635.14,-175 629.14,-169 629.14,-163 629.14,-163 629.14,-151 629.14,-151 629.14,-145 635.14,-139 641.14,-139 641.14,-139 714.86,-139 714.86,-139 720.86,-139 726.86,-145 726.86,-151 726.86,-151 726.86,-163 726.86,-163 726.86,-169 720.86,-175 714.86,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="678" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">VeterinarianAsked</text>
|
||||
</g>
|
||||
<!-- servicerequest->vetasked -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>servicerequest->vetasked</title>
|
||||
<path fill="none" stroke="black" d="M707.78,-270.3C707.78,-270.3 707.78,-186.66 707.78,-186.66"/>
|
||||
<polygon fill="black" stroke="black" points="711.28,-186.66 707.78,-176.66 704.28,-186.66 711.28,-186.66"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="707.22" y="-220.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- payreminder -->
|
||||
<g id="node24" class="node">
|
||||
<title>payreminder</title>
|
||||
<path fill="#e57373" stroke="black" d="M813.29,-175C813.29,-175 760.71,-175 760.71,-175 754.71,-175 748.71,-169 748.71,-163 748.71,-163 748.71,-151 748.71,-151 748.71,-145 754.71,-139 760.71,-139 760.71,-139 813.29,-139 813.29,-139 819.29,-139 825.29,-145 825.29,-151 825.29,-151 825.29,-163 825.29,-163 825.29,-169 819.29,-175 813.29,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="787" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">PayReminder</text>
|
||||
</g>
|
||||
<!-- servicerequest->payreminder -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>servicerequest->payreminder</title>
|
||||
<path fill="none" stroke="black" d="M757.57,-270.3C757.57,-270.3 757.57,-186.66 757.57,-186.66"/>
|
||||
<polygon fill="black" stroke="black" points="761.07,-186.66 757.57,-176.66 754.07,-186.66 761.07,-186.66"/>
|
||||
</g>
|
||||
<!-- campaign -->
|
||||
<g id="node38" class="node">
|
||||
<title>campaign</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1196.79,-175C1196.79,-175 1159.21,-175 1159.21,-175 1153.21,-175 1147.21,-169 1147.21,-163 1147.21,-163 1147.21,-151 1147.21,-151 1147.21,-145 1153.21,-139 1159.21,-139 1159.21,-139 1196.79,-139 1196.79,-139 1202.79,-139 1208.79,-145 1208.79,-151 1208.79,-151 1208.79,-163 1208.79,-163 1208.79,-169 1202.79,-175 1196.79,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1178" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Campaign</text>
|
||||
</g>
|
||||
<!-- servicerequest->campaign -->
|
||||
<!-- vetasked->veterinarian -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>vetasked->veterinarian</title>
|
||||
<path fill="none" stroke="black" d="M658.92,-175.35C658.92,-234.76 658.92,-418 658.92,-418 658.92,-418 524.83,-418 524.83,-418"/>
|
||||
<polygon fill="black" stroke="black" points="524.83,-414.5 514.83,-418 524.83,-421.5 524.83,-414.5"/>
|
||||
</g>
|
||||
<!-- vetreminder -->
|
||||
<g id="node23" class="node">
|
||||
<title>vetreminder</title>
|
||||
<path fill="#e57373" stroke="black" d="M703.18,-52C703.18,-52 652.82,-52 652.82,-52 646.82,-52 640.82,-46 640.82,-40 640.82,-40 640.82,-28 640.82,-28 640.82,-22 646.82,-16 652.82,-16 652.82,-16 703.18,-16 703.18,-16 709.18,-16 715.18,-22 715.18,-28 715.18,-28 715.18,-40 715.18,-40 715.18,-46 709.18,-52 703.18,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="678" y="-31" font-family="Helvetica,sans-Serif" font-size="10.00">VetReminder</text>
|
||||
</g>
|
||||
<!-- vetasked->vetreminder -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>vetasked->vetreminder</title>
|
||||
<path fill="none" stroke="black" d="M678,-138.59C678,-138.59 678,-63.65 678,-63.65"/>
|
||||
<polygon fill="black" stroke="black" points="681.5,-63.65 678,-53.65 674.5,-63.65 681.5,-63.65"/>
|
||||
</g>
|
||||
<!-- vetvisit->servicerequest -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>vetvisit->servicerequest</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M2042.05,-270.38C2042.05,-253.18 2042.05,-230 2042.05,-230 2042.05,-230 766.43,-230 766.43,-230 766.43,-230 766.43,-258.87 766.43,-258.87"/>
|
||||
<polygon fill="black" stroke="black" points="762.93,-258.87 766.43,-268.87 769.93,-258.87 762.93,-258.87"/>
|
||||
</g>
|
||||
<!-- vetvisitreport -->
|
||||
<g id="node26" class="node">
|
||||
<title>vetvisitreport</title>
|
||||
<path fill="#fff59d" stroke="black" d="M1932.96,-175C1932.96,-175 1877.04,-175 1877.04,-175 1871.04,-175 1865.04,-169 1865.04,-163 1865.04,-163 1865.04,-151 1865.04,-151 1865.04,-145 1871.04,-139 1877.04,-139 1877.04,-139 1932.96,-139 1932.96,-139 1938.96,-139 1944.96,-145 1944.96,-151 1944.96,-151 1944.96,-163 1944.96,-163 1944.96,-169 1938.96,-175 1932.96,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1905" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">VetVisitReport</text>
|
||||
</g>
|
||||
<!-- vetvisit->vetvisitreport -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>vetvisit->vetvisitreport</title>
|
||||
<path fill="none" stroke="black" d="M2038.69,-289C1992.75,-289 1905,-289 1905,-289 1905,-289 1905,-186.87 1905,-186.87"/>
|
||||
<polygon fill="black" stroke="black" points="1908.5,-186.87 1905,-176.87 1901.5,-186.87 1908.5,-186.87"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1981.22" y="-220.4" font-family="Helvetica,sans-Serif" font-size="8.00">1:N</text>
|
||||
</g>
|
||||
<!-- vetvisitpetreason -->
|
||||
<g id="node28" class="node">
|
||||
<title>vetvisitpetreason</title>
|
||||
<path fill="#fff176" stroke="black" d="M2039.18,-175C2039.18,-175 1978.82,-175 1978.82,-175 1972.82,-175 1966.82,-169 1966.82,-163 1966.82,-163 1966.82,-151 1966.82,-151 1966.82,-145 1972.82,-139 1978.82,-139 1978.82,-139 2039.18,-139 2039.18,-139 2045.18,-139 2051.18,-145 2051.18,-151 2051.18,-151 2051.18,-163 2051.18,-163 2051.18,-169 2045.18,-175 2039.18,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2009" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">VisitPetReason</text>
|
||||
</g>
|
||||
<!-- vetvisit->vetvisitpetreason -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>vetvisit->vetvisitpetreason</title>
|
||||
<path fill="none" stroke="black" d="M2048.14,-270.3C2048.14,-270.3 2048.14,-186.66 2048.14,-186.66"/>
|
||||
<polygon fill="black" stroke="black" points="2051.64,-186.66 2048.14,-176.66 2044.64,-186.66 2051.64,-186.66"/>
|
||||
</g>
|
||||
<!-- visit_pets -->
|
||||
<g id="node29" class="node">
|
||||
<title>visit_pets</title>
|
||||
<path fill="#ffee58" stroke="black" d="M2111.72,-170.9C2111.72,-170.9 2084.76,-161.1 2084.76,-161.1 2079.12,-159.05 2079.12,-154.95 2084.76,-152.9 2084.76,-152.9 2111.72,-143.1 2111.72,-143.1 2117.36,-141.05 2128.64,-141.05 2134.28,-143.1 2134.28,-143.1 2161.24,-152.9 2161.24,-152.9 2166.88,-154.95 2166.88,-159.05 2161.24,-161.1 2161.24,-161.1 2134.28,-170.9 2134.28,-170.9 2128.64,-172.95 2117.36,-172.95 2111.72,-170.9"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2123" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Visit-Pets</text>
|
||||
</g>
|
||||
<!-- vetvisit->visit_pets -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>vetvisit->visit_pets</title>
|
||||
<path fill="none" stroke="black" d="M2083.24,-270.3C2083.24,-270.3 2083.24,-172.32 2083.24,-172.32"/>
|
||||
<polygon fill="black" stroke="black" points="2086.74,-172.32 2083.24,-162.32 2079.74,-172.32 2086.74,-172.32"/>
|
||||
</g>
|
||||
<!-- receipt -->
|
||||
<g id="node30" class="node">
|
||||
<title>receipt</title>
|
||||
<path fill="#f8bbd9" stroke="black" d="M2237,-175C2237,-175 2207,-175 2207,-175 2201,-175 2195,-169 2195,-163 2195,-163 2195,-151 2195,-151 2195,-145 2201,-139 2207,-139 2207,-139 2237,-139 2237,-139 2243,-139 2249,-145 2249,-151 2249,-151 2249,-163 2249,-163 2249,-169 2243,-175 2237,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2222" y="-160" font-family="Helvetica,sans-Serif" font-size="10.00">Receipt</text>
|
||||
<text xml:space="preserve" text-anchor="middle" x="2222" y="-148" font-family="Helvetica,sans-Serif" font-size="10.00">(AFIP)</text>
|
||||
</g>
|
||||
<!-- vetvisit->receipt -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>vetvisit->receipt</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M2093.17,-289C2137.87,-289 2222,-289 2222,-289 2222,-289 2222,-186.87 2222,-186.87"/>
|
||||
<polygon fill="black" stroke="black" points="2225.5,-186.87 2222,-176.87 2218.5,-186.87 2225.5,-186.87"/>
|
||||
</g>
|
||||
<!-- turnfeegroup -->
|
||||
<g id="node41" class="node">
|
||||
<title>turnfeegroup</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1621.79,-175C1621.79,-175 1564.21,-175 1564.21,-175 1558.21,-175 1552.21,-169 1552.21,-163 1552.21,-163 1552.21,-151 1552.21,-151 1552.21,-145 1558.21,-139 1564.21,-139 1564.21,-139 1621.79,-139 1621.79,-139 1627.79,-139 1633.79,-145 1633.79,-151 1633.79,-151 1633.79,-163 1633.79,-163 1633.79,-169 1627.79,-175 1621.79,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1593" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">TurnFeeGroup</text>
|
||||
</g>
|
||||
<!-- vetvisit->turnfeegroup -->
|
||||
<!-- vetvisitfollowup -->
|
||||
<g id="node27" class="node">
|
||||
<title>vetvisitfollowup</title>
|
||||
<path fill="#fff176" stroke="black" d="M1921.84,-52C1921.84,-52 1888.16,-52 1888.16,-52 1882.16,-52 1876.16,-46 1876.16,-40 1876.16,-40 1876.16,-28 1876.16,-28 1876.16,-22 1882.16,-16 1888.16,-16 1888.16,-16 1921.84,-16 1921.84,-16 1927.84,-16 1933.84,-22 1933.84,-28 1933.84,-28 1933.84,-40 1933.84,-40 1933.84,-46 1927.84,-52 1921.84,-52"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1905" y="-31" font-family="Helvetica,sans-Serif" font-size="10.00">FollowUp</text>
|
||||
</g>
|
||||
<!-- vetvisitreport->vetvisitfollowup -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>vetvisitreport->vetvisitfollowup</title>
|
||||
<path fill="none" stroke="black" d="M1905,-138.59C1905,-138.59 1905,-63.65 1905,-63.65"/>
|
||||
<polygon fill="black" stroke="black" points="1908.5,-63.65 1905,-53.65 1901.5,-63.65 1908.5,-63.65"/>
|
||||
</g>
|
||||
<!-- specialty->study -->
|
||||
<!-- province -->
|
||||
<g id="node33" class="node">
|
||||
<title>province</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1366.45,-306.6C1366.45,-306.6 1335.55,-306.6 1335.55,-306.6 1329.55,-306.6 1323.55,-300.6 1323.55,-294.6 1323.55,-294.6 1323.55,-282.6 1323.55,-282.6 1323.55,-276.6 1329.55,-270.6 1335.55,-270.6 1335.55,-270.6 1366.45,-270.6 1366.45,-270.6 1372.45,-270.6 1378.45,-276.6 1378.45,-282.6 1378.45,-282.6 1378.45,-294.6 1378.45,-294.6 1378.45,-300.6 1372.45,-306.6 1366.45,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1351" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Province</text>
|
||||
</g>
|
||||
<!-- locality -->
|
||||
<g id="node34" class="node">
|
||||
<title>locality</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1464,-306.6C1464,-306.6 1434,-306.6 1434,-306.6 1428,-306.6 1422,-300.6 1422,-294.6 1422,-294.6 1422,-282.6 1422,-282.6 1422,-276.6 1428,-270.6 1434,-270.6 1434,-270.6 1464,-270.6 1464,-270.6 1470,-270.6 1476,-276.6 1476,-282.6 1476,-282.6 1476,-294.6 1476,-294.6 1476,-300.6 1470,-306.6 1464,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1449" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">Locality</text>
|
||||
</g>
|
||||
<!-- province->locality -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>province->locality</title>
|
||||
<path fill="none" stroke="black" d="M1378.82,-289C1378.82,-289 1410.27,-289 1410.27,-289"/>
|
||||
<polygon fill="black" stroke="black" points="1410.27,-292.5 1420.27,-289 1410.27,-285.5 1410.27,-292.5"/>
|
||||
</g>
|
||||
<!-- petbreed -->
|
||||
<g id="node35" class="node">
|
||||
<title>petbreed</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1543.85,-306.6C1543.85,-306.6 1510.15,-306.6 1510.15,-306.6 1504.15,-306.6 1498.15,-300.6 1498.15,-294.6 1498.15,-294.6 1498.15,-282.6 1498.15,-282.6 1498.15,-276.6 1504.15,-270.6 1510.15,-270.6 1510.15,-270.6 1543.85,-270.6 1543.85,-270.6 1549.85,-270.6 1555.85,-276.6 1555.85,-282.6 1555.85,-282.6 1555.85,-294.6 1555.85,-294.6 1555.85,-300.6 1549.85,-306.6 1543.85,-306.6"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1527" y="-285.6" font-family="Helvetica,sans-Serif" font-size="10.00">PetBreed</text>
|
||||
</g>
|
||||
<!-- tag -->
|
||||
<g id="node39" class="node">
|
||||
<title>tag</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1432,-175C1432,-175 1402,-175 1402,-175 1396,-175 1390,-169 1390,-163 1390,-163 1390,-151 1390,-151 1390,-145 1396,-139 1402,-139 1402,-139 1432,-139 1432,-139 1438,-139 1444,-145 1444,-151 1444,-151 1444,-163 1444,-163 1444,-169 1438,-175 1432,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1417" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Tag</text>
|
||||
</g>
|
||||
<!-- medication -->
|
||||
<g id="node40" class="node">
|
||||
<title>medication</title>
|
||||
<path fill="#cfd8dc" stroke="black" d="M1518.18,-175C1518.18,-175 1477.82,-175 1477.82,-175 1471.82,-175 1465.82,-169 1465.82,-163 1465.82,-163 1465.82,-151 1465.82,-151 1465.82,-145 1471.82,-139 1477.82,-139 1477.82,-139 1518.18,-139 1518.18,-139 1524.18,-139 1530.18,-145 1530.18,-151 1530.18,-151 1530.18,-163 1530.18,-163 1530.18,-169 1524.18,-175 1518.18,-175"/>
|
||||
<text xml:space="preserve" text-anchor="middle" x="1498" y="-154" font-family="Helvetica,sans-Serif" font-size="10.00">Medication</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 43 KiB |
120
atlas/book/arch-model/graph.html
Normal file
120
atlas/book/arch-model/graph.html
Normal file
@@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Graph Viewer - AMAR Mascotas</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="graph-viewer">
|
||||
<header class="graph-header">
|
||||
<a href="index.html" class="back-link">← Index</a>
|
||||
<div class="nav-controls">
|
||||
<button onclick="navigate(-1)" id="btn-prev" title="Previous (←)">◀</button>
|
||||
<span id="nav-position">1 / 4</span>
|
||||
<button onclick="navigate(1)" id="btn-next" title="Next (→)">▶</button>
|
||||
</div>
|
||||
<h1 id="graph-title">Loading...</h1>
|
||||
<div class="graph-controls">
|
||||
<button onclick="setMode('fit')">Fit</button>
|
||||
<button onclick="setMode('fit-width')">Width</button>
|
||||
<button onclick="setMode('fit-height')">Height</button>
|
||||
<button onclick="setMode('actual-size')">100%</button>
|
||||
<button onclick="downloadSvg()">↓ SVG</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="graph-container" id="graph-container">
|
||||
<img id="graph-img" src="" alt="Graph">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const graphOrder = [
|
||||
'04-data-model-simple',
|
||||
'01-backend-architecture',
|
||||
'02-frontend-architecture',
|
||||
'03-data-model'
|
||||
];
|
||||
|
||||
const graphs = {
|
||||
'04-data-model-simple': {
|
||||
title: 'Data Model Overview',
|
||||
file: '04-data-model-simple.svg'
|
||||
},
|
||||
'01-backend-architecture': {
|
||||
title: 'Backend Architecture',
|
||||
file: '01-backend-architecture.svg'
|
||||
},
|
||||
'02-frontend-architecture': {
|
||||
title: 'Frontend Architecture',
|
||||
file: '02-frontend-architecture.svg'
|
||||
},
|
||||
'03-data-model': {
|
||||
title: 'Detailed Data Model',
|
||||
file: '03-data-model.svg'
|
||||
}
|
||||
};
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
let graphKey = params.get('g') || '04-data-model-simple';
|
||||
let currentIndex = graphOrder.indexOf(graphKey);
|
||||
if (currentIndex === -1) currentIndex = 0;
|
||||
|
||||
function loadGraph(key) {
|
||||
const graph = graphs[key];
|
||||
document.getElementById('graph-title').textContent = graph.title;
|
||||
document.getElementById('graph-img').src = graph.file;
|
||||
document.title = graph.title + ' - AMAR Mascotas';
|
||||
history.replaceState(null, '', '?g=' + key);
|
||||
graphKey = key;
|
||||
updateNavHints();
|
||||
}
|
||||
|
||||
function updateNavHints() {
|
||||
const idx = graphOrder.indexOf(graphKey);
|
||||
const prevBtn = document.getElementById('btn-prev');
|
||||
const nextBtn = document.getElementById('btn-next');
|
||||
prevBtn.disabled = idx === 0;
|
||||
nextBtn.disabled = idx === graphOrder.length - 1;
|
||||
document.getElementById('nav-position').textContent = (idx + 1) + ' / ' + graphOrder.length;
|
||||
}
|
||||
|
||||
function navigate(direction) {
|
||||
const idx = graphOrder.indexOf(graphKey);
|
||||
const newIdx = idx + direction;
|
||||
if (newIdx >= 0 && newIdx < graphOrder.length) {
|
||||
currentIndex = newIdx;
|
||||
loadGraph(graphOrder[newIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
function setMode(mode) {
|
||||
const container = document.getElementById('graph-container');
|
||||
container.className = 'graph-container ' + mode;
|
||||
}
|
||||
|
||||
function downloadSvg() {
|
||||
const graph = graphs[graphKey];
|
||||
const link = document.createElement('a');
|
||||
link.href = graph.file;
|
||||
link.download = graph.file;
|
||||
link.click();
|
||||
}
|
||||
|
||||
// Keyboard navigation
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
navigate(-1);
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
navigate(1);
|
||||
} else if (e.key === 'Escape') {
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
loadGraph(graphOrder[currentIndex]);
|
||||
setMode('fit');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
245
atlas/book/arch-model/index.html
Normal file
245
atlas/book/arch-model/index.html
Normal file
@@ -0,0 +1,245 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AMAR Mascotas - Architecture & Data Models</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>AMAR Mascotas</h1>
|
||||
<p class="subtitle">Architecture & Data Model Documentation</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Graph Sections -->
|
||||
<section class="graph-section" id="data-model-simple">
|
||||
<div class="graph-header-row">
|
||||
<h2>Data Model Overview</h2>
|
||||
<a href="graph.html?g=04-data-model-simple" class="view-btn">View Full</a>
|
||||
</div>
|
||||
<a href="graph.html?g=04-data-model-simple" class="graph-preview">
|
||||
<img src="04-data-model-simple.svg" alt="Data Model Overview">
|
||||
</a>
|
||||
<div class="graph-details">
|
||||
<p>High-level entity relationships without field details. Shows the main actors, workflow, and data flow.</p>
|
||||
<h4>Clusters</h4>
|
||||
<ul>
|
||||
<li><strong>Users & Auth</strong>: Django auth.User as central identity</li>
|
||||
<li><strong>Pet Owners & Pets</strong>: Clients, their pets, vaccines, studies</li>
|
||||
<li><strong>Veterinarians</strong>: Vets with availability, specialties, coverage areas</li>
|
||||
<li><strong>Services & Pricing</strong>: Service catalog with dynamic pricing</li>
|
||||
<li><strong>Cart & Checkout</strong>: Shopping cart workflow</li>
|
||||
<li><strong>Service Requests</strong>: Order lifecycle with state machine</li>
|
||||
<li><strong>Veterinary Visits</strong>: Scheduled visits, reports, AFIP invoicing</li>
|
||||
<li><strong>Reference Data</strong>: Lookups (specialties, neighborhoods, vaccines, etc.)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="graph-section" id="backend">
|
||||
<div class="graph-header-row">
|
||||
<h2>Backend Architecture</h2>
|
||||
<a href="graph.html?g=01-backend-architecture" class="view-btn">View Full</a>
|
||||
</div>
|
||||
<a href="graph.html?g=01-backend-architecture" class="graph-preview">
|
||||
<img src="01-backend-architecture.svg" alt="Backend Architecture">
|
||||
</a>
|
||||
<div class="graph-details">
|
||||
<p>Django apps structure: mascotas, productos, solicitudes, common, payments, and external integrations.</p>
|
||||
|
||||
<h4>Celery Tasks</h4>
|
||||
<table class="details-table">
|
||||
<thead>
|
||||
<tr><th>Task</th><th>App</th><th>Purpose</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>send_veterinarian_followup</code></td>
|
||||
<td>solicitudes</td>
|
||||
<td>Re-sends availability request to vet if still pending</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>run_payment_reminder_cron</code></td>
|
||||
<td>solicitudes</td>
|
||||
<td>Cron job to send payment reminders</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>create_vetvisit_in_sheet</code></td>
|
||||
<td>mascotas</td>
|
||||
<td>Creates row in Google Sheets for visit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>update_vetvisit_in_sheet</code></td>
|
||||
<td>mascotas</td>
|
||||
<td>Updates Google Sheets row</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>create_event_calendar_vetvisit</code></td>
|
||||
<td>mascotas</td>
|
||||
<td>Creates Google Calendar event</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>update_event_calendar_vetvisit</code></td>
|
||||
<td>mascotas</td>
|
||||
<td>Updates Google Calendar event</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>create_user_owner</code></td>
|
||||
<td>mascotas</td>
|
||||
<td>Creates Django user for PetOwner + welcome email</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>generate_vetvisit_invoice</code></td>
|
||||
<td>mascotas</td>
|
||||
<td>Generates AFIP invoice and PDF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>fetch_mp_notification_details</code></td>
|
||||
<td>payments</td>
|
||||
<td>Fetches MercadoPago webhook details</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="note">Celery handles async operations: external APIs (Google, MercadoPago, AFIP), scheduled reminders, and heavy processing (invoices, emails).</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="graph-section" id="frontend">
|
||||
<div class="graph-header-row">
|
||||
<h2>Frontend Architecture</h2>
|
||||
<a href="graph.html?g=02-frontend-architecture" class="view-btn">View Full</a>
|
||||
</div>
|
||||
<a href="graph.html?g=02-frontend-architecture" class="graph-preview">
|
||||
<img src="02-frontend-architecture.svg" alt="Frontend Architecture">
|
||||
</a>
|
||||
<div class="graph-details">
|
||||
<p>Next.js 13+ App Router structure with public pages, backoffice, shared components, and services layer.</p>
|
||||
<h4>Key Areas</h4>
|
||||
<ul>
|
||||
<li><strong>Public Pages</strong>: Landing, service catalog, booking flow</li>
|
||||
<li><strong>Backoffice</strong>: Role-based dashboards (admin, vet, petowner)</li>
|
||||
<li><strong>Services Layer</strong>: API clients for backend communication</li>
|
||||
<li><strong>State Management</strong>: Redux store for cart, auth, UI state</li>
|
||||
<li><strong>Shared Components</strong>: Forms, tables, modals, navigation</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="graph-section" id="data-model-detailed">
|
||||
<div class="graph-header-row">
|
||||
<h2>Detailed Data Model</h2>
|
||||
<a href="graph.html?g=03-data-model" class="view-btn">View Full</a>
|
||||
</div>
|
||||
<a href="graph.html?g=03-data-model" class="graph-preview">
|
||||
<img src="03-data-model.svg" alt="Detailed Data Model">
|
||||
</a>
|
||||
<div class="graph-details">
|
||||
<p>Complete entity-relationship diagram with all fields, types, and relationships.</p>
|
||||
<h4>Data Patterns</h4>
|
||||
<ul>
|
||||
<li><strong>Soft delete</strong>: <code>deleted</code> flag on most models</li>
|
||||
<li><strong>Audit trail</strong>: <code>StateHistory</code> for service requests</li>
|
||||
<li><strong>Geographic</strong>: PostGIS polygons for coverage areas</li>
|
||||
<li><strong>Versioned pricing</strong>: Date ranges on <code>Prices</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Findings Section -->
|
||||
<section class="findings-section">
|
||||
<h2>Key Findings</h2>
|
||||
|
||||
<div class="findings-grid">
|
||||
<article class="finding-card">
|
||||
<h3>User Types</h3>
|
||||
<p>All users connect to <code>auth.User</code>:</p>
|
||||
<ul>
|
||||
<li><strong>PetOwner</strong>: Optional 1:1 link (created lazily)</li>
|
||||
<li><strong>Veterinarian</strong>: Required 1:1 link to User</li>
|
||||
<li><strong>Staff</strong>: Direct Django users with <code>is_staff=True</code></li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="finding-card">
|
||||
<h3>Core Workflow</h3>
|
||||
<div class="workflow-diagram">
|
||||
<code>PetOwner → Cart → ServiceRequest → VeterinarianAsked → VetVisit → Report</code>
|
||||
</div>
|
||||
<p><strong>State Machine:</strong></p>
|
||||
<div class="state-flow">
|
||||
pending → vet_asked → vet_accepted → coordinated → payed → Confirmado
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="finding-card">
|
||||
<h3>Pricing Logic</h3>
|
||||
<ul>
|
||||
<li>Base price × <code>neighborhood.distance_coefficient</code></li>
|
||||
<li>Optional vet-specific pricing via <code>Prices.veterinarian_id</code></li>
|
||||
<li>Turn fee surcharge via <code>IndividualTurnFeeGroup</code></li>
|
||||
<li>Time-based discounts via <code>Discounts</code> model</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="finding-card">
|
||||
<h3>External Integrations</h3>
|
||||
<ul>
|
||||
<li><strong>MercadoPago</strong>: Payment processing</li>
|
||||
<li><strong>Google Calendar</strong>: Visit synchronization</li>
|
||||
<li><strong>Google Sheets</strong>: Visit tracking spreadsheet</li>
|
||||
<li><strong>Mercately</strong>: WhatsApp notifications</li>
|
||||
<li><strong>AFIP</strong>: Argentine tax invoicing</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tech Stack Section -->
|
||||
<section class="tech-section">
|
||||
<h2>Technology Stack</h2>
|
||||
<div class="tech-grid">
|
||||
<div class="tech-column">
|
||||
<h3>Backend</h3>
|
||||
<ul>
|
||||
<li>Django 4.x</li>
|
||||
<li>Django REST Framework</li>
|
||||
<li>PostgreSQL + PostGIS</li>
|
||||
<li>Celery (Redis)</li>
|
||||
<li>JWT Authentication</li>
|
||||
<li>django-afip</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tech-column">
|
||||
<h3>Frontend</h3>
|
||||
<ul>
|
||||
<li>Next.js 13+ (App Router)</li>
|
||||
<li>React 18+</li>
|
||||
<li>TypeScript</li>
|
||||
<li>Redux</li>
|
||||
<li>Axios</li>
|
||||
<li>Tailwind CSS</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tech-column">
|
||||
<h3>Infrastructure</h3>
|
||||
<ul>
|
||||
<li>Docker</li>
|
||||
<li>Nginx</li>
|
||||
<li>AWS S3 (storage)</li>
|
||||
<li>MercadoPago API</li>
|
||||
<li>Google APIs</li>
|
||||
<li>WhatsApp Business</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>AMAR Mascotas Architecture Documentation</p>
|
||||
<p class="date">Generated: <time datetime="2024-12-14">December 2024</time></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
565
atlas/book/arch-model/styles.css
Normal file
565
atlas/book/arch-model/styles.css
Normal file
@@ -0,0 +1,565 @@
|
||||
/* Reset and base */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-bg: #0f0f0f;
|
||||
--color-surface: #1a1a1a;
|
||||
--color-surface-hover: #252525;
|
||||
--color-border: #333;
|
||||
--color-text: #e0e0e0;
|
||||
--color-text-muted: #888;
|
||||
--color-accent: #4A90D9;
|
||||
--color-accent-light: #6BA3E0;
|
||||
--radius: 8px;
|
||||
--shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
background: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
header .subtitle {
|
||||
margin: 0.5rem 0 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
main {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
/* Card Grid - max 3 columns */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 700px) and (max-width: 999px) {
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Graph Cards */
|
||||
.card {
|
||||
display: block;
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow);
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.card-preview {
|
||||
aspect-ratio: 4/3;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-content h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.card-content p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
/* Findings Grid */
|
||||
.findings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.findings-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.finding-card {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.finding-card h3 {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.finding-card p,
|
||||
.finding-card ul {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.finding-card ul {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.finding-card li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.finding-card code {
|
||||
background: #2a2a2a;
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.85em;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.workflow-diagram {
|
||||
background: #2a2a2a;
|
||||
padding: 0.75rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.75rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.workflow-diagram code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.state-flow {
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text-muted);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.stats-table {
|
||||
width: 100%;
|
||||
font-size: 0.85rem;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.stats-table td {
|
||||
padding: 0.35rem 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.stats-table td:first-child {
|
||||
font-family: monospace;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.stats-table td:last-child {
|
||||
color: var(--color-text-muted);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Tech Stack Grid */
|
||||
.tech-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 700px) {
|
||||
.tech-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.tech-column {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.tech-column h3 {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.tech-column ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tech-column li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
background: var(--color-surface);
|
||||
border-top: 1px solid var(--color-border);
|
||||
padding: 1.5rem 2rem;
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
/* Graph Viewer Page */
|
||||
.graph-viewer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.graph-header {
|
||||
background: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 1rem 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.graph-header .back-link {
|
||||
color: var(--color-accent);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.graph-header .back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.nav-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-controls button {
|
||||
background: var(--color-surface-hover);
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text);
|
||||
padding: 0.4rem 0.7rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nav-controls button:hover:not(:disabled) {
|
||||
background: var(--color-border);
|
||||
}
|
||||
|
||||
.nav-controls button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.nav-controls span {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-muted);
|
||||
min-width: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.graph-header h1 {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.graph-controls {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.graph-controls button {
|
||||
background: var(--color-surface-hover);
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.graph-controls button:hover {
|
||||
background: var(--color-border);
|
||||
}
|
||||
|
||||
.graph-container {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.graph-container img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.graph-container.fit-width img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.graph-container.fit-height img {
|
||||
height: calc(100vh - 80px);
|
||||
width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.graph-container.actual-size img {
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
/* Graph Sections (index page) */
|
||||
.graph-section {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: 2rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.graph-header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.graph-header-row h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
background: var(--color-accent);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.view-btn:hover {
|
||||
background: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.graph-section .graph-preview {
|
||||
display: block;
|
||||
background: #fff;
|
||||
max-height: 400px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.graph-section .graph-preview img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
object-position: top left;
|
||||
}
|
||||
|
||||
.graph-details {
|
||||
padding: 1.5rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.graph-details p {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.graph-details h4 {
|
||||
margin: 1.5rem 0 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent-light);
|
||||
}
|
||||
|
||||
.graph-details h4:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.graph-details ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.graph-details li {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.graph-details code {
|
||||
background: #2a2a2a;
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* Details Table */
|
||||
.details-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.85rem;
|
||||
margin: 0.5rem 0 1rem;
|
||||
}
|
||||
|
||||
.details-table th {
|
||||
text-align: left;
|
||||
padding: 0.5rem;
|
||||
background: #2a2a2a;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.details-table td {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.details-table code {
|
||||
background: #2a2a2a;
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.85em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-muted);
|
||||
font-style: italic;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 600px) {
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card-grid,
|
||||
.findings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.graph-header {
|
||||
flex-wrap: wrap;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.graph-header h1 {
|
||||
order: -1;
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.graph-controls {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
300
atlas/book/drive-index/index.html
Normal file
300
atlas/book/drive-index/index.html
Normal file
@@ -0,0 +1,300 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Drive Index - Amar Mascotas</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
padding: 2rem;
|
||||
}
|
||||
.container { max-width: 900px; margin: 0 auto; }
|
||||
.back { font-size: 0.85rem; margin-bottom: 1rem; }
|
||||
.back a { color: #6366f1; text-decoration: none; }
|
||||
.back a:hover { text-decoration: underline; }
|
||||
header {
|
||||
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
||||
color: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
header h1 { font-size: 1.5rem; margin-bottom: 0.25rem; }
|
||||
header p { opacity: 0.9; font-size: 0.85rem; }
|
||||
.stats { display: flex; gap: 0.75rem; margin-top: 0.75rem; flex-wrap: wrap; }
|
||||
.stat { background: rgba(255,255,255,0.2); padding: 0.35rem 0.75rem; border-radius: 6px; font-size: 0.8rem; }
|
||||
.notice {
|
||||
background: #fef3c7;
|
||||
border: 1px solid #fcd34d;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: #92400e;
|
||||
}
|
||||
.notice strong { display: block; margin-bottom: 0.5rem; }
|
||||
.notice ul { margin: 0.5rem 0 0 1.25rem; }
|
||||
.notice li { margin-bottom: 0.25rem; }
|
||||
|
||||
details {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 0.5rem;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||
}
|
||||
summary {
|
||||
padding: 0.75rem 1rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
list-style: none;
|
||||
}
|
||||
summary::-webkit-details-marker { display: none; }
|
||||
summary:hover { background: #f9fafb; border-radius: 8px; }
|
||||
summary .icon {
|
||||
font-size: 0.7rem;
|
||||
color: #6366f1;
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
details[open] summary .icon { transform: rotate(90deg); }
|
||||
summary .name { font-weight: 500; font-size: 0.9rem; flex: 1; }
|
||||
summary .count {
|
||||
background: #e0e7ff;
|
||||
color: #4f46e5;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 10px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.folder-content {
|
||||
padding: 0.5rem 1rem 0.75rem 2rem;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
.folder-desc {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.file-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
color: #555;
|
||||
}
|
||||
.file-ext {
|
||||
font-size: 0.6rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
padding: 0.15rem 0.35rem;
|
||||
border-radius: 3px;
|
||||
min-width: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.file-ext.xlsx { background: #dcfce7; color: #166534; }
|
||||
.file-ext.docx { background: #dbeafe; color: #1e40af; }
|
||||
.file-ext.pdf { background: #fee2e2; color: #991b1b; }
|
||||
.file-ext.png, .file-ext.jpg { background: #fef3c7; color: #92400e; }
|
||||
.file-ext.mov, .file-ext.mp4 { background: #f3e8ff; color: #7c3aed; }
|
||||
.file-ext.pptx { background: #ffedd5; color: #c2410c; }
|
||||
.file-ext.ttf { background: #f1f5f9; color: #475569; }
|
||||
|
||||
footer { text-align: center; padding: 2rem; color: #999; font-size: 0.75rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="back"><a href="/">← Album</a></div>
|
||||
<header>
|
||||
<h1>Drive Index</h1>
|
||||
<p>Amar Mascotas - Company Drive Structure</p>
|
||||
<div class="stats">
|
||||
<span class="stat">12 folders</span>
|
||||
<span class="stat">~235 files</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="notice">
|
||||
<strong>Preview Mode</strong>
|
||||
This shows the Drive folder structure with placeholder filenames. Real file data will be available once <strong>Google Vein</strong> (Google Drive sync) is implemented in Artery:
|
||||
<ul>
|
||||
<li>Auto-sync folder structure and file metadata</li>
|
||||
<li>Direct links to files (requires Google Workspace login)</li>
|
||||
<li>Live updates when Drive contents change</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">01. Identidad</span><span class="count">3 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Brand assets, logos, typography</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext png">png</span> logo_main.png</div>
|
||||
<div class="file"><span class="file-ext png">png</span> logo_alt.png</div>
|
||||
<div class="file"><span class="file-ext ttf">ttf</span> brand_font.ttf</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">02. Marketing Contenidos</span><span class="count">8 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Social media content, templates, testimonials</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext docx">docx</span> social_template.docx</div>
|
||||
<div class="file"><span class="file-ext png">png</span> post_image1.png</div>
|
||||
<div class="file"><span class="file-ext png">png</span> post_image2.png</div>
|
||||
<div class="file"><span class="file-ext mov">mov</span> event_video.mov</div>
|
||||
<div class="file"><span class="file-ext png">png</span> testimonials.png</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">03. Marketing Growth</span><span class="count">8 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Paid media campaigns, performance dashboards</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> dashboard_report.xlsx</div>
|
||||
<div class="file"><span class="file-ext png">png</span> ad_1080x1080.png</div>
|
||||
<div class="file"><span class="file-ext png">png</span> ad_1200x628.png</div>
|
||||
<div class="file"><span class="file-ext png">png</span> ad_stories.png</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">05. ATC - Operaciones</span><span class="count">12 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Process manuals, workflows, support docs</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext docx">docx</span> manual_procesos.docx</div>
|
||||
<div class="file"><span class="file-ext jpg">jpg</span> workflow_diagram.jpg</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> contacts_list.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> postal_codes.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> tasks_tracker.xlsx</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">06. Supply</span><span class="count">7 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Vet network, lab partnerships, onboarding</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> lab_prices.pdf</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> vet_onboarding.xlsx</div>
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> vet_cv_sample.pdf</div>
|
||||
<div class="file"><span class="file-ext mp4">mp4</span> meeting_recording.mp4</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">07. Finanzas</span><span class="count">7 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Invoices, accounting, finance tracking</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> finance_tracker.xlsx</div>
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> invoice_may.pdf</div>
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> invoice_jun.pdf</div>
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> invoice_jul.pdf</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">08. IT y Producto</span><span class="count">57 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Product specs, feature docs, references</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext docx">docx</span> product_index.docx</div>
|
||||
<div class="file"><span class="file-ext docx">docx</span> feature_specs.docx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> platform_states.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> medications_db.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> events_mapping.xlsx</div>
|
||||
<div class="file"><span class="file-ext pptx">pptx</span> benchmark.pptx</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">09. DATA</span><span class="count">2 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Database exports, data backups</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> pets_database.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> pets_backup.xlsx</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">Clientes - ventas</span><span class="count">22 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">CRM exports, sales data, client info</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> crm_export.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> churn_analysis.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> leads_2023.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> leads_2024.xlsx</div>
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> monthly_report.pdf</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">HR</span><span class="count">17 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Onboarding, job profiles, team docs</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext pptx">pptx</span> onboarding_deck.pptx</div>
|
||||
<div class="file"><span class="file-ext docx">docx</span> job_profile_sales.docx</div>
|
||||
<div class="file"><span class="file-ext pdf">pdf</span> job_profile_growth.pdf</div>
|
||||
<div class="file"><span class="file-ext jpg">jpg</span> team_photo.jpg</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">Pitch Decks</span><span class="count">24 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Investor presentations, company decks</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext pptx">pptx</span> pitch_deck_latest.pptx</div>
|
||||
<div class="file"><span class="file-ext docx">docx</span> video_script.docx</div>
|
||||
<div class="file"><span class="file-ext pptx">pptx</span> product_timeline.pptx</div>
|
||||
<div class="file"><span class="file-ext pptx">pptx</span> investor_update.pptx</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><span class="icon">▶</span><span class="name">z.Backup archivos viejos</span><span class="count">46 files</span></summary>
|
||||
<div class="folder-content">
|
||||
<div class="folder-desc">Legacy files, old exports</div>
|
||||
<div class="file-list">
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> old_vets_list.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> org_chart.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> data_room_2023.xlsx</div>
|
||||
<div class="file"><span class="file-ext xlsx">xlsx</span> legacy_export.xlsx</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<footer>pawprint/album</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
217
atlas/book/drive-index/index.md
Normal file
217
atlas/book/drive-index/index.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# Drive Index - Amar Mascotas
|
||||
|
||||
> vault source: `vault/drive/`
|
||||
> 48 files | Last indexed: 2025-12-08
|
||||
|
||||
## Overview by Area
|
||||
|
||||
| Area | Focus | Key Files | Workflow Potential |
|
||||
|------|-------|-----------|-------------------|
|
||||
| 01. Identidad | Brand assets | Illustrations, Typography | Design handoff |
|
||||
| 02. Marketing Contenidos | Social/Content | Posts, Event media, Templates | Content calendar |
|
||||
| 03. Marketing Growth | Paid/Performance | Ad creatives, Dashboards | Campaign tracking |
|
||||
| 05. ATC - Operaciones | Ops/Support | Process manual, Workflow diagram | Onboarding, SOP |
|
||||
| 06. Supply | Vets/Labs | Vet tracking, Lab prices, CVs | Vet onboarding |
|
||||
| 07. Finanzas | Accounting | Invoices, Finance sheet | Reconciliation |
|
||||
| Clientes | Sales/CRM | Fitsales export | Client tracking |
|
||||
| Pitch Decks | Presentations | Company pitch | Investor/partner |
|
||||
|
||||
---
|
||||
|
||||
## 01. Identidad Amar Mascotas
|
||||
Brand identity assets.
|
||||
|
||||
```
|
||||
Ilustraciones amar PNG/
|
||||
├── Asset 4@4x.png
|
||||
└── Asset 11@4x.png
|
||||
|
||||
Tipografia/Open_Sans/static/
|
||||
└── OpenSans_Condensed-Italic.ttf
|
||||
```
|
||||
|
||||
**Workflow relevance**: Design system, brand consistency checks.
|
||||
|
||||
---
|
||||
|
||||
## 02. Marketing Contenidos
|
||||
Social media content and production assets.
|
||||
|
||||
```
|
||||
Backup archivos viejos/
|
||||
├── Contenido evento amar 10 ago 2023/
|
||||
│ ├── IMG_7309.MOV
|
||||
│ └── video-output-(...).mov
|
||||
└── RRSS MSJ PREDETERMINADOS.docx ← Predefined social messages
|
||||
|
||||
Produccion audiovisual Jul 24/
|
||||
└── Fotos Produccion Audiovisual JUL24/
|
||||
└── Vacunacion Fotos Produccion JUL 24/
|
||||
└── IMG_4620.JPG
|
||||
|
||||
Redes sociales Amar/
|
||||
├── 02. FEBRERO 25 Finales/12-2/cambios/247.png
|
||||
├── 08. AGOSTO 24 Finales Amar/
|
||||
│ ├── 13.8/1.png
|
||||
│ └── 21.8/3.png
|
||||
└── Comentarios clientes felices JUN-JUL24/
|
||||
└── Captura de pantalla (...).png ← Testimonials
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- `RRSS MSJ PREDETERMINADOS.docx` = template for ATC/social responses
|
||||
- Monthly folders suggest content calendar exists
|
||||
- Testimonials capture = social proof workflow
|
||||
|
||||
---
|
||||
|
||||
## 03. Marketing Growth
|
||||
Paid media campaigns and performance tracking.
|
||||
|
||||
```
|
||||
Paid Media - Pauta digital/
|
||||
├── 05.MAY24 - ADS Testimonios/
|
||||
├── 08.AGO24 - ADS 10 OFF/
|
||||
│ ├── 1080 x 1080-17.png
|
||||
│ ├── 1200 x 628-32.png
|
||||
│ ├── 1200 x 628-33.png
|
||||
│ ├── 1200 x 628-38.png
|
||||
│ ├── 1200 x 628-39.png
|
||||
│ └── 960 x 1200-30.png
|
||||
├── Amar Mascotas - Reporte_Dash general(...)(2).xlsx
|
||||
└── Amar Mascotas - Reporte_Dash general(...)(3).xlsx
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- Ad size variants = multi-platform campaigns (Meta, Google)
|
||||
- Dashboard reports = performance tracking exists
|
||||
- Monthly campaign folders = structured campaign workflow
|
||||
|
||||
---
|
||||
|
||||
## 05. ATC - Operaciones
|
||||
Operations, customer service, and legacy data.
|
||||
|
||||
```
|
||||
Operaciones backup viejos/
|
||||
├── ATC - Manual de Procesos.docx ← KEY: Process documentation
|
||||
├── work Flow servicio veterinario.jpg ← KEY: Service workflow diagram
|
||||
├── Clientes a contactar.xlsx
|
||||
├── codigos_postales_y_barrios (1).xlsx ← Coverage/delivery zones
|
||||
├── domesticables ultimo.docx
|
||||
├── domesticables ultimo.pdf
|
||||
├── INFO.xlsx
|
||||
├── Reporte DOMESTICABLES 11_5.xlsx
|
||||
├── Tareas de Leila.xlsx
|
||||
└── entrega/
|
||||
├── prestashop/
|
||||
│ ├── clientes a escribir febrero 2023.csv
|
||||
│ └── datos web vieja/
|
||||
│ └── reembolsos por falta de stock.csv
|
||||
└── vendedores/somos pam/
|
||||
└── articulos desactivados en marzo 2023.csv
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- `ATC - Manual de Procesos.docx` = existing SOP (Gherkin source?)
|
||||
- `work Flow servicio veterinario.jpg` = visual process (BDD source?)
|
||||
- Legacy CSV = migration/history context
|
||||
- Task tracking = ops workflow exists
|
||||
|
||||
---
|
||||
|
||||
## 06. Supply (vetes-labo-clinicas)
|
||||
Veterinarian network and lab partnerships.
|
||||
|
||||
```
|
||||
Comunciacion/
|
||||
└── Veterinarios-46.jpg
|
||||
|
||||
Formularios- Viejos - no usar/
|
||||
└── Reuniones Matias/
|
||||
├── Grabación de pantalla 2024-08-20(...).mp4
|
||||
└── Grabación de pantalla 2024-08-21(...).mp4
|
||||
|
||||
Laboratorios/DiagnoTest/
|
||||
└── Diagnotest-ListaPrecios-Junio2025 (1).pdf ← Current pricing
|
||||
|
||||
Veterinarios postulantes/
|
||||
├── Aldana Becker - Colaboracion Amar Mascotas.docx
|
||||
├── Ana Christophersen/
|
||||
│ └── CV - Ana Christophersen.pdf
|
||||
└── Ingreso Veterinarios.xlsx ← Vet onboarding tracker
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- `Ingreso Veterinarios.xlsx` = vet recruitment/onboarding flow
|
||||
- Lab price list = service pricing reference
|
||||
- Meeting recordings = training/knowledge transfer
|
||||
|
||||
---
|
||||
|
||||
## 07. Finanzas y contabilidad
|
||||
Invoices and financial tracking.
|
||||
|
||||
```
|
||||
Facturas/
|
||||
├── May 23/Recibido/
|
||||
│ ├── FacturaAMARMASCOTASSA(...).pdf
|
||||
│ └── FacturaAMARMASCOTASSA(...).pdf
|
||||
├── Jun 23/Recibido/
|
||||
│ ├── nc 4-29.pdf
|
||||
│ └── nc 4-30.pdf
|
||||
└── Jul 23/Recibido/
|
||||
├── FAC 567 ZUCCONI ALAN EDGARDO.pdf
|
||||
└── Mercaba carlos roberto cozak.pdf
|
||||
|
||||
Finanzas Amar mascotas.xlsx ← Main finance tracker
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- Monthly invoice organization = accounting workflow
|
||||
- Finance xlsx = reconciliation process
|
||||
|
||||
---
|
||||
|
||||
## Clientes - ventas - devoluciones
|
||||
CRM and sales data.
|
||||
|
||||
```
|
||||
Excels Clientes - ventas - devoluciones/
|
||||
└── Fitsales CRM vinculados hasta el 06 dic.xlsx
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- CRM export = client tracking, sales funnel
|
||||
|
||||
---
|
||||
|
||||
## Pitch Decks - Presentaciones
|
||||
Company presentations.
|
||||
|
||||
```
|
||||
Copias Pitch Deck Cata/
|
||||
└── Copia de Pitch Deck - ABR 2025.pptx
|
||||
```
|
||||
|
||||
**Workflow relevance**:
|
||||
- Investor/partner communications
|
||||
|
||||
---
|
||||
|
||||
## Workflow Discovery Summary
|
||||
|
||||
### Documents to Review for BDD/Gherkin
|
||||
1. `05. ATC/ATC - Manual de Procesos.docx` - existing process documentation
|
||||
2. `05. ATC/work Flow servicio veterinario.jpg` - visual workflow
|
||||
3. `02. Marketing/RRSS MSJ PREDETERMINADOS.docx` - response templates
|
||||
|
||||
### Active Tracking Sheets
|
||||
- `06. Supply/Ingreso Veterinarios.xlsx` - vet onboarding
|
||||
- `Clientes/Fitsales CRM vinculados.xlsx` - client tracking
|
||||
- `07. Finanzas/Finanzas Amar mascotas.xlsx` - finance
|
||||
|
||||
### Potential Artery Connections
|
||||
- Fitsales CRM → pulse for client data
|
||||
- Finance xlsx → pulse for accounting
|
||||
- Vet tracker → pulse for supply management
|
||||
60
atlas/book/feature-flow/CLAUDE.md
Normal file
60
atlas/book/feature-flow/CLAUDE.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Feature Flow Book
|
||||
|
||||
## Purpose
|
||||
Presentation showing the feature standardization pipeline.
|
||||
|
||||
## The Pipeline
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||||
│ OPS TEMPLATES │ -> │ BDD/GHERKIN │ -> │ TESTS │
|
||||
│ │ │ │ │ │
|
||||
│ Non-technical │ │ .feature files │ │ Backend: │
|
||||
│ User flows │ │ Given/When/Then │ │ API contracts │
|
||||
│ From support │ │ Human readable │ │ Workflows │
|
||||
│ │ │ │ │ Frontend: │
|
||||
│ │ │ │ │ Page Objects │
|
||||
│ │ │ │ │ E2E specs │
|
||||
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## Files
|
||||
- `index-en.html` - English slide presentation (8 slides, arrow keys)
|
||||
- `index-es.html` - Spanish slide presentation (8 slides, arrow keys)
|
||||
|
||||
## Slides Structure
|
||||
1. Title
|
||||
2. Pipeline Overview (3 columns)
|
||||
3. Ops Templates
|
||||
4. BDD/Gherkin
|
||||
5. Gherkin File Organization (best practices)
|
||||
6. Backend Tests (amar_django_back structure)
|
||||
7. Frontend Tests (amar_frontend structure)
|
||||
8. Per-Feature Checklist
|
||||
|
||||
## Sources
|
||||
|
||||
### Ops Templates
|
||||
- `album/template/ops-flow/plantilla-flujo.md`
|
||||
- `def/work_plan/21-plantilla-flujos-usuario.md`
|
||||
|
||||
### BDD/Gherkin Examples
|
||||
- `def/work_plan/10-flow-turnero.md` (full gherkin + tests example)
|
||||
|
||||
### Test Structure References
|
||||
- `amar_django_back/tests/contracts/README.md`
|
||||
- `amar_frontend/tests/README.md`
|
||||
|
||||
## Editing
|
||||
Edit `index-en.html` or `index-es.html` directly.
|
||||
Slides are `<section>` elements. Arrow keys to navigate.
|
||||
|
||||
## Flow Checklist (per feature)
|
||||
|
||||
- [ ] Ops template filled by support team
|
||||
- [ ] Convert to .feature file (Gherkin spec)
|
||||
- [ ] Backend: API contract tests per endpoint
|
||||
- [ ] Backend: Workflow test (composition)
|
||||
- [ ] Frontend: Page Object (if new page)
|
||||
- [ ] Frontend: E2E spec (Playwright)
|
||||
- [ ] Wire to CI
|
||||
392
atlas/book/feature-flow/index-en.html
Normal file
392
atlas/book/feature-flow/index-en.html
Normal file
@@ -0,0 +1,392 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Feature Flow - Standardization Pipeline</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.slide { display: none; min-height: 100vh; padding: 3rem; }
|
||||
.slide.active { display: flex; flex-direction: column; }
|
||||
.nav { position: fixed; bottom: 2rem; right: 2rem; display: flex; gap: 0.5rem; z-index: 100; }
|
||||
.nav button { background: #334155; border: none; color: #e2e8f0; padding: 0.75rem 1.25rem; border-radius: 6px; cursor: pointer; font-size: 1rem; }
|
||||
.nav button:hover { background: #475569; }
|
||||
.nav .counter { background: transparent; padding: 0.75rem 1rem; color: #64748b; }
|
||||
.slide-title { justify-content: center; align-items: center; text-align: center; }
|
||||
.slide-title h1 { font-size: 3.5rem; font-weight: 700; margin-bottom: 1rem; background: linear-gradient(135deg, #6366f1, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
.slide-title p { font-size: 1.5rem; color: #94a3b8; }
|
||||
.slide-title .subtitle { margin-top: 3rem; font-size: 1rem; color: #64748b; }
|
||||
.pipeline { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; margin: 2rem 0; }
|
||||
.pipeline-step { background: #1e293b; border-radius: 12px; padding: 2rem; border-left: 4px solid; }
|
||||
.pipeline-step.ops { border-color: #22c55e; }
|
||||
.pipeline-step.bdd { border-color: #6366f1; }
|
||||
.pipeline-step.tests { border-color: #f59e0b; }
|
||||
.pipeline-step h3 { font-size: 1.25rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
|
||||
.pipeline-step .num { width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.85rem; font-weight: 700; }
|
||||
.pipeline-step.ops .num { background: #22c55e; color: #0f172a; }
|
||||
.pipeline-step.bdd .num { background: #6366f1; color: white; }
|
||||
.pipeline-step.tests .num { background: #f59e0b; color: #0f172a; }
|
||||
.pipeline-step ul { list-style: none; color: #94a3b8; font-size: 0.95rem; }
|
||||
.pipeline-step li { padding: 0.4rem 0; padding-left: 1rem; border-left: 2px solid #334155; margin-bottom: 0.25rem; }
|
||||
.slide h2 { font-size: 2rem; margin-bottom: 2rem; display: flex; align-items: center; gap: 1rem; }
|
||||
.slide h2 .badge { padding: 0.25rem 0.75rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
|
||||
.slide h2 .badge.ops { background: #22c55e; color: #0f172a; }
|
||||
.slide h2 .badge.bdd { background: #6366f1; color: white; }
|
||||
.slide h2 .badge.tests { background: #f59e0b; color: #0f172a; }
|
||||
pre { background: #1e293b; padding: 1.5rem; border-radius: 8px; overflow-x: auto; font-size: 0.8rem; line-height: 1.5; margin: 1rem 0; }
|
||||
.keyword { color: #c084fc; }
|
||||
.string { color: #4ade80; }
|
||||
.comment { color: #64748b; }
|
||||
.decorator { color: #f59e0b; }
|
||||
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; flex: 1; }
|
||||
.col { background: #1e293b; border-radius: 8px; padding: 1.5rem; }
|
||||
.col h4 { font-size: 0.85rem; color: #94a3b8; margin-bottom: 1rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
.checklist { list-style: none; font-size: 1.1rem; }
|
||||
.checklist li { padding: 0.75rem 0; display: flex; align-items: center; gap: 0.75rem; border-bottom: 1px solid #334155; }
|
||||
.checklist .check { width: 20px; height: 20px; border: 2px solid #475569; border-radius: 4px; }
|
||||
.flow-list { list-style: none; }
|
||||
.flow-list li { padding: 0.4rem 0; color: #94a3b8; font-size: 0.9rem; }
|
||||
.flow-list li strong { color: #e2e8f0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="slide slide-title active" data-slide="0">
|
||||
<h1>Feature Flow</h1>
|
||||
<p>Standardization Pipeline</p>
|
||||
<div class="subtitle">Ops Templates → BDD/Gherkin → Backend + Frontend Tests</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="1">
|
||||
<h2>Pipeline Overview</h2>
|
||||
<div class="pipeline">
|
||||
<div class="pipeline-step ops">
|
||||
<h3><span class="num">1</span> Ops Templates</h3>
|
||||
<ul>
|
||||
<li>Non-technical language</li>
|
||||
<li>User perspective flows</li>
|
||||
<li>From support/ops team</li>
|
||||
<li>Captures edge cases</li>
|
||||
<li>Documents known problems</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step bdd">
|
||||
<h3><span class="num">2</span> BDD/Gherkin</h3>
|
||||
<ul>
|
||||
<li>.feature files</li>
|
||||
<li>Given/When/Then syntax</li>
|
||||
<li>Human readable specs</li>
|
||||
<li>Single source of truth</li>
|
||||
<li>Maps to both test types</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step tests">
|
||||
<h3><span class="num">3</span> Tests</h3>
|
||||
<ul>
|
||||
<li><strong>Backend:</strong> API contracts</li>
|
||||
<li><strong>Backend:</strong> Workflows (compositions)</li>
|
||||
<li><strong>Frontend:</strong> Page Objects</li>
|
||||
<li><strong>Frontend:</strong> E2E specs (Playwright)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="2">
|
||||
<h2><span class="badge ops">1</span> Ops Templates</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Template Structure</h4>
|
||||
<pre>
|
||||
<span class="keyword">### [Flow Name]</span>
|
||||
|
||||
<span class="comment">User type:</span> Pet Owner / Vet / Admin
|
||||
<span class="comment">Entry point:</span> Page/button/link
|
||||
<span class="comment">Goal:</span> One sentence
|
||||
|
||||
<span class="keyword">Steps:</span>
|
||||
1. First action
|
||||
2. Second action
|
||||
3. ...
|
||||
|
||||
<span class="keyword">Expected result:</span>
|
||||
- What should happen
|
||||
|
||||
<span class="keyword">Common problems:</span>
|
||||
- Problem 1
|
||||
|
||||
<span class="keyword">Edge cases:</span>
|
||||
- Special case 1
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Source</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Template:</strong> album/template/ops-flow/</li>
|
||||
<li><strong>Reference:</strong> def/work_plan/21-plantilla</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Who Fills This</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Support team (daily user contact)</li>
|
||||
<li>Ops team (knows workarounds)</li>
|
||||
<li>Product (requirements)</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Output</h4>
|
||||
<ul class="flow-list">
|
||||
<li>One .md per flow</li>
|
||||
<li>Organized by user type</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="3">
|
||||
<h2><span class="badge bdd">2</span> BDD/Gherkin</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>.feature File</h4>
|
||||
<pre>
|
||||
<span class="keyword">Feature:</span> Turnero - Book appointment
|
||||
|
||||
<span class="keyword">Scenario:</span> Book vaccination for cat
|
||||
<span class="decorator">Given</span> I am on the turnero page
|
||||
<span class="decorator">When</span> I enter address <span class="string">"Av Santa Fe 1234"</span>
|
||||
<span class="decorator">And</span> I click <span class="string">"Next"</span>
|
||||
<span class="decorator">Then</span> a guest user should be created
|
||||
|
||||
<span class="decorator">When</span> I add pet <span class="string">"Koshka"</span> type <span class="string">"Cat"</span>
|
||||
<span class="decorator">And</span> I select <span class="string">"Vaccination"</span>
|
||||
<span class="decorator">Then</span> <span class="string">"Clinical consult"</span> is auto-added
|
||||
|
||||
<span class="keyword">Scenario:</span> Services filtered by pet type
|
||||
<span class="decorator">Given</span> I added a cat
|
||||
<span class="decorator">Then</span> I see cat vaccines
|
||||
<span class="decorator">And</span> I dont see dog vaccines
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Keywords</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature</strong> = one capability</li>
|
||||
<li><strong>Scenario</strong> = one behavior</li>
|
||||
<li><strong>Given</strong> = precondition</li>
|
||||
<li><strong>When</strong> = action</li>
|
||||
<li><strong>Then</strong> = expected result</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Reference</h4>
|
||||
<ul class="flow-list">
|
||||
<li>def/work_plan/10-flow-turnero.md</li>
|
||||
<li>Full example with Gherkin, API tests, Page Objects</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="4">
|
||||
<h2><span class="badge bdd">2b</span> Gherkin File Organization</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Correct: One Feature = One File</h4>
|
||||
<pre>
|
||||
<span class="keyword">features/</span>
|
||||
├── pet-owner/
|
||||
│ ├── registro.feature <span class="comment"># 6-8 scenarios</span>
|
||||
│ ├── reservar-turno.feature <span class="comment"># 10-15 scenarios</span>
|
||||
│ ├── gestion-mascotas.feature
|
||||
│ └── pago.feature
|
||||
├── veterinarian/
|
||||
│ └── ...
|
||||
└── backoffice/
|
||||
└── ...
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Anti-pattern: One Scenario = One File</h4>
|
||||
<pre style="border-left: 3px solid #ef4444;">
|
||||
<span class="comment"># DON'T do this</span>
|
||||
features/pet-owner/registro/
|
||||
├── registro-exitoso.feature
|
||||
├── registro-email-invalido.feature
|
||||
├── registro-password-corto.feature
|
||||
└── <span class="comment">... (dozens of tiny files)</span>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Why Multiple Scenarios per File</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature = Capability</strong> - one file describes one capability with all its behaviors</li>
|
||||
<li><strong>Context stays together</strong> - Background, Rules share context</li>
|
||||
<li><strong>Tooling expects it</strong> - test runners, reports, IDE navigation</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">When to Split</h4>
|
||||
<pre>
|
||||
<span class="comment"># Scenarios per file:</span>
|
||||
5-20 <span class="string">Normal, keep as is</span>
|
||||
20-40 <span class="string">Consider splitting</span>
|
||||
40+ <span class="string">Definitely split</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Folder Depth</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Good:</strong> 1-2 levels max</li>
|
||||
<li><strong>Avoid:</strong> deep nesting</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="5">
|
||||
<h2><span class="badge tests">3a</span> Backend Tests</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Structure (amar_django_back)</h4>
|
||||
<pre>
|
||||
tests/contracts/
|
||||
├── base.py <span class="comment"># mode switcher</span>
|
||||
├── endpoints.py <span class="comment"># API paths (single source)</span>
|
||||
├── helpers.py <span class="comment"># test data</span>
|
||||
│
|
||||
├── mascotas/ <span class="comment"># app tests</span>
|
||||
│ ├── test_pet_owners.py
|
||||
│ ├── test_pets.py
|
||||
│ └── test_coverage.py
|
||||
├── productos/
|
||||
│ ├── test_services.py
|
||||
│ └── test_cart.py
|
||||
├── solicitudes/
|
||||
│ └── test_service_requests.py
|
||||
│
|
||||
└── <span class="keyword">workflows/</span> <span class="comment"># compositions</span>
|
||||
└── test_turnero_general.py
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Two Test Modes</h4>
|
||||
<pre>
|
||||
<span class="comment"># Fast (Django test client)</span>
|
||||
pytest tests/contracts/
|
||||
|
||||
<span class="comment"># Live (real HTTP)</span>
|
||||
CONTRACT_TEST_MODE=live pytest
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Workflow = Composition</h4>
|
||||
<pre>
|
||||
<span class="comment"># Calls endpoints in sequence:</span>
|
||||
1. Check coverage
|
||||
2. Create pet owner
|
||||
3. Create pet
|
||||
4. Get services
|
||||
5. Create request
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Key Files</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>endpoints.py</strong> - change paths here only</li>
|
||||
<li><strong>helpers.py</strong> - sample data</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="6">
|
||||
<h2><span class="badge tests">3b</span> Frontend Tests</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Structure (amar_frontend)</h4>
|
||||
<pre>
|
||||
tests/e2e/
|
||||
├── <span class="keyword">pages/</span> <span class="comment"># Page Objects</span>
|
||||
│ ├── BasePage.ts
|
||||
│ ├── LoginPage.ts
|
||||
│ └── index.ts
|
||||
│
|
||||
└── login.spec.ts <span class="comment"># E2E test</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Page Object Pattern</h4>
|
||||
<pre>
|
||||
<span class="keyword">export class</span> LoginPage <span class="keyword">extends</span> BasePage {
|
||||
<span class="keyword">get</span> emailInput() {
|
||||
<span class="keyword">return</span> this.page.getByLabel(<span class="string">'Email'</span>);
|
||||
}
|
||||
|
||||
<span class="keyword">async</span> login(email, password) {
|
||||
<span class="keyword">await</span> this.emailInput.fill(email);
|
||||
<span class="keyword">await</span> this.passwordInput.fill(password);
|
||||
<span class="keyword">await</span> this.submitButton.click();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Running Tests</h4>
|
||||
<pre>
|
||||
<span class="comment"># All tests</span>
|
||||
npx playwright test
|
||||
|
||||
<span class="comment"># With UI</span>
|
||||
npx playwright test --ui
|
||||
|
||||
<span class="comment"># Specific file</span>
|
||||
npx playwright test login.spec.ts
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Locator Priority</h4>
|
||||
<ul class="flow-list">
|
||||
<li>1. getByRole() - buttons, links</li>
|
||||
<li>2. getByLabel() - form fields</li>
|
||||
<li>3. getByText() - visible text</li>
|
||||
<li>4. getByTestId() - data-testid</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1rem;">Avoid</h4>
|
||||
<ul class="flow-list">
|
||||
<li>CSS class selectors</li>
|
||||
<li>Complex XPath</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="7">
|
||||
<h2>Per-Feature Checklist</h2>
|
||||
<ul class="checklist">
|
||||
<li><span class="check"></span> Ops template filled (support team)</li>
|
||||
<li><span class="check"></span> Convert to .feature file (Gherkin spec)</li>
|
||||
<li><span class="check"></span> Backend: API contract tests per endpoint</li>
|
||||
<li><span class="check"></span> Backend: Workflow test (composition)</li>
|
||||
<li><span class="check"></span> Frontend: Page Object (if new page)</li>
|
||||
<li><span class="check"></span> Frontend: E2E spec (Playwright)</li>
|
||||
<li><span class="check"></span> Wire to CI</li>
|
||||
</ul>
|
||||
<div style="margin-top: 2rem; color: #64748b; font-size: 0.9rem;">
|
||||
<p><strong>Full example:</strong> def/work_plan/10-flow-turnero.md</p>
|
||||
<p><strong>Backend README:</strong> amar_django_back/tests/contracts/README.md</p>
|
||||
<p><strong>Frontend README:</strong> amar_frontend/tests/README.md</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="nav">
|
||||
<button onclick="prevSlide()">←</button>
|
||||
<span class="counter"><span id="current">1</span>/<span id="total">8</span></span>
|
||||
<button onclick="nextSlide()">→</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let current = 0;
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const total = slides.length;
|
||||
document.getElementById('total').textContent = total;
|
||||
function showSlide(n) {
|
||||
slides.forEach(s => s.classList.remove('active'));
|
||||
current = (n + total) % total;
|
||||
slides[current].classList.add('active');
|
||||
document.getElementById('current').textContent = current + 1;
|
||||
}
|
||||
function nextSlide() { showSlide(current + 1); }
|
||||
function prevSlide() { showSlide(current - 1); }
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'ArrowRight' || e.key === ' ') nextSlide();
|
||||
if (e.key === 'ArrowLeft') prevSlide();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
470
atlas/book/feature-flow/index-es.html
Normal file
470
atlas/book/feature-flow/index-es.html
Normal file
@@ -0,0 +1,470 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Feature Flow - Pipeline de Estandarizacion</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.slide {
|
||||
display: none;
|
||||
min-height: 100vh;
|
||||
padding: 3rem;
|
||||
}
|
||||
.slide.active { display: flex; flex-direction: column; }
|
||||
.nav {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
z-index: 100;
|
||||
}
|
||||
.nav button {
|
||||
background: #334155;
|
||||
border: none;
|
||||
color: #e2e8f0;
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.nav button:hover { background: #475569; }
|
||||
.nav .counter {
|
||||
background: transparent;
|
||||
padding: 0.75rem 1rem;
|
||||
color: #64748b;
|
||||
}
|
||||
.slide-title {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.slide-title h1 {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(135deg, #6366f1, #a855f7);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.slide-title p { font-size: 1.5rem; color: #94a3b8; }
|
||||
.slide-title .subtitle { margin-top: 3rem; font-size: 1rem; color: #64748b; }
|
||||
.pipeline {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
.pipeline-step {
|
||||
background: #1e293b;
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
.pipeline-step.ops { border-color: #22c55e; }
|
||||
.pipeline-step.bdd { border-color: #6366f1; }
|
||||
.pipeline-step.tests { border-color: #f59e0b; }
|
||||
.pipeline-step h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.pipeline-step .num {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.pipeline-step.ops .num { background: #22c55e; color: #0f172a; }
|
||||
.pipeline-step.bdd .num { background: #6366f1; color: white; }
|
||||
.pipeline-step.tests .num { background: #f59e0b; color: #0f172a; }
|
||||
.pipeline-step ul { list-style: none; color: #94a3b8; font-size: 0.95rem; }
|
||||
.pipeline-step li { padding: 0.4rem 0; padding-left: 1rem; border-left: 2px solid #334155; margin-bottom: 0.25rem; }
|
||||
.slide h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
.slide h2 .badge {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.slide h2 .badge.ops { background: #22c55e; color: #0f172a; }
|
||||
.slide h2 .badge.bdd { background: #6366f1; color: white; }
|
||||
.slide h2 .badge.tests { background: #f59e0b; color: #0f172a; }
|
||||
pre {
|
||||
background: #1e293b;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.keyword { color: #c084fc; }
|
||||
.string { color: #4ade80; }
|
||||
.comment { color: #64748b; }
|
||||
.decorator { color: #f59e0b; }
|
||||
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; flex: 1; }
|
||||
.col { background: #1e293b; border-radius: 8px; padding: 1.5rem; }
|
||||
.col h4 { font-size: 0.9rem; color: #94a3b8; margin-bottom: 1rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
.checklist { list-style: none; font-size: 1.1rem; }
|
||||
.checklist li { padding: 0.75rem 0; display: flex; align-items: center; gap: 0.75rem; border-bottom: 1px solid #334155; }
|
||||
.checklist .check { width: 20px; height: 20px; border: 2px solid #475569; border-radius: 4px; }
|
||||
.flow-list { list-style: none; }
|
||||
.flow-list li { padding: 0.5rem 0; color: #94a3b8; }
|
||||
.flow-list li strong { color: #e2e8f0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="slide slide-title active" data-slide="0">
|
||||
<h1>Feature Flow</h1>
|
||||
<p>Pipeline de Estandarizacion</p>
|
||||
<div class="subtitle">Templates Ops → BDD/Gherkin → Tests Backend + Frontend</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="1">
|
||||
<h2>Vision General del Pipeline</h2>
|
||||
<div class="pipeline">
|
||||
<div class="pipeline-step ops">
|
||||
<h3><span class="num">1</span> Templates Ops</h3>
|
||||
<ul>
|
||||
<li>Lenguaje no tecnico</li>
|
||||
<li>Flujos desde el usuario</li>
|
||||
<li>Del equipo de soporte/ops</li>
|
||||
<li>Captura casos borde</li>
|
||||
<li>Documenta problemas conocidos</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step bdd">
|
||||
<h3><span class="num">2</span> BDD/Gherkin</h3>
|
||||
<ul>
|
||||
<li>Archivos .feature</li>
|
||||
<li>Sintaxis Given/When/Then</li>
|
||||
<li>Specs legibles</li>
|
||||
<li>Fuente unica de verdad</li>
|
||||
<li>Mapea a ambos tipos de test</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step tests">
|
||||
<h3><span class="num">3</span> Tests</h3>
|
||||
<ul>
|
||||
<li><strong>Backend:</strong> Contratos API</li>
|
||||
<li><strong>Backend:</strong> Workflows (composiciones)</li>
|
||||
<li><strong>Frontend:</strong> Page Objects</li>
|
||||
<li><strong>Frontend:</strong> E2E specs (Playwright)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="2">
|
||||
<h2><span class="badge ops">1</span> Templates Ops</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Estructura de la Plantilla</h4>
|
||||
<pre>
|
||||
<span class="keyword">### [Nombre del Flujo]</span>
|
||||
|
||||
<span class="comment">Tipo de usuario:</span> Dueno / Vet / Admin
|
||||
<span class="comment">Donde empieza:</span> Pagina/boton/link
|
||||
<span class="comment">Objetivo:</span> Una oracion
|
||||
|
||||
<span class="keyword">Pasos:</span>
|
||||
1. Primera accion
|
||||
2. Segunda accion
|
||||
3. ...
|
||||
|
||||
<span class="keyword">Que deberia pasar:</span>
|
||||
- Resultado esperado
|
||||
|
||||
<span class="keyword">Problemas comunes:</span>
|
||||
- Problema 1
|
||||
|
||||
<span class="keyword">Casos especiales:</span>
|
||||
- Caso especial 1
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Fuente</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Plantilla:</strong> album/template/ops-flow/</li>
|
||||
<li><strong>Referencia:</strong> def/work_plan/21-plantilla</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Quien Completa Esto</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Equipo de soporte (contacto diario)</li>
|
||||
<li>Equipo de ops (conoce workarounds)</li>
|
||||
<li>Producto (requerimientos)</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Output</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Un .md por flujo</li>
|
||||
<li>Organizado por tipo de usuario</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="3">
|
||||
<h2><span class="badge bdd">2</span> BDD/Gherkin</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Archivo .feature</h4>
|
||||
<pre>
|
||||
<span class="keyword">Feature:</span> Turnero - Reservar turno
|
||||
|
||||
<span class="keyword">Scenario:</span> Reservar vacuna para gato
|
||||
<span class="decorator">Given</span> estoy en la pagina del turnero
|
||||
<span class="decorator">When</span> ingreso direccion <span class="string">"Av Santa Fe 1234"</span>
|
||||
<span class="decorator">And</span> hago click en <span class="string">"Siguiente"</span>
|
||||
<span class="decorator">Then</span> se crea un usuario invitado
|
||||
|
||||
<span class="decorator">When</span> agrego mascota <span class="string">"Koshka"</span> tipo <span class="string">"Gato"</span>
|
||||
<span class="decorator">And</span> selecciono <span class="string">"Vacunacion"</span>
|
||||
<span class="decorator">Then</span> <span class="string">"Consulta clinica"</span> se agrega auto
|
||||
|
||||
<span class="keyword">Scenario:</span> Servicios filtrados por tipo
|
||||
<span class="decorator">Given</span> agregue un gato
|
||||
<span class="decorator">Then</span> veo vacunas felinas
|
||||
<span class="decorator">And</span> no veo vacunas caninas
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Palabras Clave</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature</strong> = una funcionalidad</li>
|
||||
<li><strong>Scenario</strong> = un comportamiento</li>
|
||||
<li><strong>Given</strong> = precondicion</li>
|
||||
<li><strong>When</strong> = accion</li>
|
||||
<li><strong>Then</strong> = resultado esperado</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Referencia</h4>
|
||||
<ul class="flow-list">
|
||||
<li>def/work_plan/10-flow-turnero.md</li>
|
||||
<li>Ejemplo completo con Gherkin, tests API, Page Objects</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="4">
|
||||
<h2><span class="badge bdd">2b</span> Organizacion de Archivos Gherkin</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Correcto: Una Feature = Un Archivo</h4>
|
||||
<pre>
|
||||
<span class="keyword">features/</span>
|
||||
├── pet-owner/
|
||||
│ ├── registro.feature <span class="comment"># 6-8 escenarios</span>
|
||||
│ ├── reservar-turno.feature <span class="comment"># 10-15 escenarios</span>
|
||||
│ ├── gestion-mascotas.feature
|
||||
│ └── pago.feature
|
||||
├── veterinarian/
|
||||
│ └── ...
|
||||
└── backoffice/
|
||||
└── ...
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Anti-patron: Un Escenario = Un Archivo</h4>
|
||||
<pre style="border-left: 3px solid #ef4444;">
|
||||
<span class="comment"># NO hacer esto</span>
|
||||
features/pet-owner/registro/
|
||||
├── registro-exitoso.feature
|
||||
├── registro-email-invalido.feature
|
||||
├── registro-password-corto.feature
|
||||
└── <span class="comment">... (docenas de archivos pequeños)</span>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Por Que Multiples Escenarios por Archivo</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature = Capacidad</strong> - un archivo describe una capacidad con todos sus comportamientos</li>
|
||||
<li><strong>Contexto junto</strong> - Background, Rules comparten contexto</li>
|
||||
<li><strong>Tooling lo espera</strong> - test runners, reportes, navegacion IDE</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Cuando Dividir</h4>
|
||||
<pre>
|
||||
<span class="comment"># Escenarios por archivo:</span>
|
||||
5-20 <span class="string">Normal, mantener</span>
|
||||
20-40 <span class="string">Considerar dividir</span>
|
||||
40+ <span class="string">Definitivamente dividir</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Profundidad de Carpetas</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Bien:</strong> 1-2 niveles max</li>
|
||||
<li><strong>Evitar:</strong> anidamiento profundo</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="5">
|
||||
<h2><span class="badge tests">3a</span> Tests Backend</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Estructura (amar_django_back)</h4>
|
||||
<pre>
|
||||
tests/contracts/
|
||||
├── base.py <span class="comment"># switcher de modo</span>
|
||||
├── endpoints.py <span class="comment"># paths API (fuente unica)</span>
|
||||
├── helpers.py <span class="comment"># datos de prueba</span>
|
||||
│
|
||||
├── mascotas/ <span class="comment"># tests por app</span>
|
||||
│ ├── test_pet_owners.py
|
||||
│ ├── test_pets.py
|
||||
│ └── test_coverage.py
|
||||
├── productos/
|
||||
│ ├── test_services.py
|
||||
│ └── test_cart.py
|
||||
├── solicitudes/
|
||||
│ └── test_service_requests.py
|
||||
│
|
||||
└── <span class="keyword">workflows/</span> <span class="comment"># composiciones</span>
|
||||
└── test_turnero_general.py
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Dos Modos de Test</h4>
|
||||
<pre>
|
||||
<span class="comment"># Rapido (Django test client)</span>
|
||||
pytest tests/contracts/
|
||||
|
||||
<span class="comment"># Live (HTTP real)</span>
|
||||
CONTRACT_TEST_MODE=live pytest
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Workflow = Composicion</h4>
|
||||
<pre>
|
||||
<span class="comment"># Llama endpoints en secuencia:</span>
|
||||
1. Check cobertura
|
||||
2. Crear pet owner
|
||||
3. Crear mascota
|
||||
4. Obtener servicios
|
||||
5. Crear solicitud
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Archivos Clave</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>endpoints.py</strong> - cambiar paths solo aca</li>
|
||||
<li><strong>helpers.py</strong> - datos de ejemplo</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="6">
|
||||
<h2><span class="badge tests">3b</span> Tests Frontend</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Estructura (amar_frontend)</h4>
|
||||
<pre>
|
||||
tests/e2e/
|
||||
├── <span class="keyword">pages/</span> <span class="comment"># Page Objects</span>
|
||||
│ ├── BasePage.ts
|
||||
│ ├── LoginPage.ts
|
||||
│ └── index.ts
|
||||
│
|
||||
└── login.spec.ts <span class="comment"># test E2E</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Patron Page Object</h4>
|
||||
<pre>
|
||||
<span class="keyword">export class</span> LoginPage <span class="keyword">extends</span> BasePage {
|
||||
<span class="keyword">get</span> emailInput() {
|
||||
<span class="keyword">return</span> this.page.getByLabel(<span class="string">'Email'</span>);
|
||||
}
|
||||
|
||||
<span class="keyword">async</span> login(email, password) {
|
||||
<span class="keyword">await</span> this.emailInput.fill(email);
|
||||
<span class="keyword">await</span> this.passwordInput.fill(password);
|
||||
<span class="keyword">await</span> this.submitButton.click();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Ejecutar Tests</h4>
|
||||
<pre>
|
||||
<span class="comment"># Todos los tests</span>
|
||||
npx playwright test
|
||||
|
||||
<span class="comment"># Con UI</span>
|
||||
npx playwright test --ui
|
||||
|
||||
<span class="comment"># Archivo especifico</span>
|
||||
npx playwright test login.spec.ts
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Prioridad de Locators</h4>
|
||||
<ul class="flow-list">
|
||||
<li>1. getByRole() - botones, links</li>
|
||||
<li>2. getByLabel() - campos de form</li>
|
||||
<li>3. getByText() - texto visible</li>
|
||||
<li>4. getByTestId() - data-testid</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1rem;">Evitar</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Selectores de clases CSS</li>
|
||||
<li>XPath complejos</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="7">
|
||||
<h2>Checklist por Feature</h2>
|
||||
<ul class="checklist">
|
||||
<li><span class="check"></span> Template ops completado (equipo soporte)</li>
|
||||
<li><span class="check"></span> Convertir a archivo .feature (spec Gherkin)</li>
|
||||
<li><span class="check"></span> Backend: Tests de contrato API por endpoint</li>
|
||||
<li><span class="check"></span> Backend: Test workflow (composicion)</li>
|
||||
<li><span class="check"></span> Frontend: Page Object (si es pagina nueva)</li>
|
||||
<li><span class="check"></span> Frontend: E2E spec (Playwright)</li>
|
||||
<li><span class="check"></span> Conectar a CI</li>
|
||||
</ul>
|
||||
<div style="margin-top: 2rem; color: #64748b; font-size: 0.9rem;">
|
||||
<p><strong>Ejemplo completo:</strong> def/work_plan/10-flow-turnero.md</p>
|
||||
<p><strong>README Backend:</strong> amar_django_back/tests/contracts/README.md</p>
|
||||
<p><strong>README Frontend:</strong> amar_frontend/tests/README.md</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="nav">
|
||||
<button onclick="prevSlide()">←</button>
|
||||
<span class="counter"><span id="current">1</span>/<span id="total">8</span></span>
|
||||
<button onclick="nextSlide()">→</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let current = 0;
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const total = slides.length;
|
||||
document.getElementById('total').textContent = total;
|
||||
function showSlide(n) {
|
||||
slides.forEach(s => s.classList.remove('active'));
|
||||
current = (n + total) % total;
|
||||
slides[current].classList.add('active');
|
||||
document.getElementById('current').textContent = current + 1;
|
||||
}
|
||||
function nextSlide() { showSlide(current + 1); }
|
||||
function prevSlide() { showSlide(current - 1); }
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'ArrowRight' || e.key === ' ') nextSlide();
|
||||
if (e.key === 'ArrowLeft') prevSlide();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
79
atlas/book/feature-form-samples/CLAUDE.md
Normal file
79
atlas/book/feature-form-samples/CLAUDE.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Ops Templates Sample Book
|
||||
|
||||
## Purpose
|
||||
**SAMPLE DATA** - Example filled ops/support templates for demonstration and testing purposes. These are realistic examples to work with while actual definitions are pending from ops/support team.
|
||||
|
||||
Use these to:
|
||||
- Understand the template structure
|
||||
- Test tooling and workflows
|
||||
- Demo the feature-flow pipeline
|
||||
|
||||
Real templates will follow the same structure but with validated content.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
ops-templates-sample/
|
||||
├── pet-owner/ # 5 flows for pet owners
|
||||
│ ├── 01-registro.md
|
||||
│ ├── 02-reservar-turno.md
|
||||
│ ├── 03-gestion-mascotas.md
|
||||
│ ├── 04-pago-turno.md
|
||||
│ └── 05-historial-medico.md
|
||||
├── veterinarian/ # 5 flows for vets
|
||||
│ ├── 01-aceptar-solicitud.md
|
||||
│ ├── 02-gestion-agenda.md
|
||||
│ ├── 03-realizar-visita.md
|
||||
│ ├── 04-zonas-cobertura.md
|
||||
│ └── 05-historial-pacientes.md
|
||||
└── backoffice/ # 5 flows for admins
|
||||
├── 01-gestion-solicitudes.md
|
||||
├── 02-gestion-usuarios.md
|
||||
├── 03-gestion-servicios.md
|
||||
├── 04-reembolsos.md
|
||||
└── 05-reportes.md
|
||||
```
|
||||
|
||||
## Template Format
|
||||
|
||||
Each file follows the standard ops template:
|
||||
|
||||
```markdown
|
||||
# [Nombre del Flujo]
|
||||
|
||||
## Tipo de usuario
|
||||
## Donde empieza
|
||||
## Que quiere hacer el usuario
|
||||
## Pasos
|
||||
## Que deberia pasar
|
||||
## Problemas comunes
|
||||
## Casos especiales
|
||||
## Flujos relacionados
|
||||
## Notas tecnicas
|
||||
```
|
||||
|
||||
## Corresponding Gherkin
|
||||
|
||||
Each template has corresponding `.feature` files in:
|
||||
- `album/book/gherkin-sample/es/` - Spanish keywords (Dado/Cuando/Entonces)
|
||||
- `album/book/gherkin-sample/en/` - English keywords (Given/When/Then)
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Support/Ops**: Use templates to document new flows
|
||||
2. **Dev**: Convert templates to Gherkin specs
|
||||
3. **QA**: Use Gherkin to derive test cases
|
||||
4. **Product**: Review templates for completeness
|
||||
|
||||
## Source Template
|
||||
|
||||
The base template is at: `album/template/ops-flow/plantilla-flujo.md`
|
||||
|
||||
## Coverage
|
||||
|
||||
| Area | Flows | Status |
|
||||
|------|-------|--------|
|
||||
| Pet Owner | 5 | Complete |
|
||||
| Veterinarian | 5 | Complete |
|
||||
| Backoffice | 5 | Complete |
|
||||
| **Total** | **15** | |
|
||||
335
atlas/book/feature-form-samples/detail.html
Normal file
335
atlas/book/feature-form-samples/detail.html
Normal file
@@ -0,0 +1,335 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ filename }} - Ops Template</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: #f8fafc;
|
||||
color: #1e293b;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container { max-width: 800px; margin: 0 auto; padding: 2rem 1rem; }
|
||||
header { margin-bottom: 1.5rem; }
|
||||
.breadcrumb { font-size: 0.9rem; color: #64748b; margin-bottom: 0.5rem; }
|
||||
.breadcrumb a { color: #15803d; text-decoration: none; }
|
||||
.breadcrumb a:hover { text-decoration: underline; }
|
||||
h1 { font-size: 1.5rem; color: #15803d; }
|
||||
.meta {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.meta span {
|
||||
background: #f1f5f9;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
color: #64748b;
|
||||
}
|
||||
.meta .sample { background: #fef3c7; color: #92400e; }
|
||||
|
||||
/* Form-like styling */
|
||||
.form-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.form-header {
|
||||
background: linear-gradient(135deg, #15803d, #22c55e);
|
||||
color: white;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
.form-header h2 { font-size: 1.1rem; font-weight: 600; }
|
||||
.form-body { padding: 1.5rem; }
|
||||
|
||||
.field {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.field:last-child { margin-bottom: 0; }
|
||||
.field-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #64748b;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
.field-label .edit-icon {
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.field-label .edit-icon:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.field-value {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
color: #1e293b;
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
.field-value:focus-within {
|
||||
border-color: #15803d;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(21, 128, 61, 0.1);
|
||||
}
|
||||
.field-value.multiline {
|
||||
min-height: 4rem;
|
||||
}
|
||||
.field-value ul, .field-value ol {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
.field-value li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.field-value p {
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
.field-value p:last-child { margin-bottom: 0; }
|
||||
.field-textarea {
|
||||
display: none;
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
padding: 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
font-family: inherit;
|
||||
border: 1px solid #15803d;
|
||||
border-radius: 6px;
|
||||
resize: vertical;
|
||||
box-shadow: 0 0 0 3px rgba(21, 128, 61, 0.1);
|
||||
}
|
||||
.field.editing .field-value { display: none; }
|
||||
.field.editing .field-textarea { display: block; }
|
||||
|
||||
/* Special field styles */
|
||||
.field-steps .field-value {
|
||||
counter-reset: step;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
.field-steps ol {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.field-steps li {
|
||||
position: relative;
|
||||
padding-left: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.field-steps li::before {
|
||||
counter-increment: step;
|
||||
content: counter(step);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background: #15803d;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.field-problems .field-value {
|
||||
background: #fef2f2;
|
||||
border-color: #fecaca;
|
||||
}
|
||||
.field-problems li::before {
|
||||
content: '!';
|
||||
color: #dc2626;
|
||||
font-weight: bold;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.field-special .field-value {
|
||||
background: #fffbeb;
|
||||
border-color: #fde68a;
|
||||
}
|
||||
|
||||
.field-technical .field-value {
|
||||
background: #f0fdf4;
|
||||
border-color: #bbf7d0;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.field-technical code {
|
||||
background: #dcfce7;
|
||||
padding: 0.1rem 0.35rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.sidebar h3 { font-size: 0.8rem; color: #15803d; margin-bottom: 0.75rem; text-transform: uppercase; }
|
||||
.sidebar a {
|
||||
display: block;
|
||||
color: #15803d;
|
||||
text-decoration: none;
|
||||
padding: 0.35rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.sidebar a:hover { text-decoration: underline; }
|
||||
|
||||
footer {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
footer a { color: #15803d; text-decoration: none; }
|
||||
footer a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="breadcrumb">
|
||||
<a href="/">Album</a> / <a href="/book/ops-templates/">Ops Templates</a> / {{ user_type }}
|
||||
</div>
|
||||
<h1>{{ filename.replace('.md', '').replace('-', ' ').title() }}</h1>
|
||||
<div class="meta">
|
||||
<span>{{ user_type }}</span>
|
||||
<span class="sample">Sample</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="form-card">
|
||||
<div class="form-header">
|
||||
<h2>User Flow Template</h2>
|
||||
</div>
|
||||
<div class="form-body" id="form-content">
|
||||
<!-- Content will be parsed and inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<h3>Related</h3>
|
||||
<a href="/book/gherkin/es/{{ user_type }}/{{ filename.replace('.md', '.feature') }}">Gherkin (Spanish)</a>
|
||||
<a href="/book/gherkin/en/{{ user_type }}/{{ filename.replace('.md', '.feature') }}">Gherkin (English)</a>
|
||||
<a href="/book/feature-flow/">Feature Flow Pipeline</a>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<a href="/book/ops-templates/">← All Templates</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const rawContent = {{ content | tojson }};
|
||||
|
||||
function parseMarkdown(md) {
|
||||
const sections = {};
|
||||
let currentSection = null;
|
||||
let currentContent = [];
|
||||
|
||||
const lines = md.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('## ')) {
|
||||
if (currentSection) {
|
||||
sections[currentSection] = currentContent.join('\n').trim();
|
||||
}
|
||||
currentSection = line.replace('## ', '').trim();
|
||||
currentContent = [];
|
||||
} else if (line.startsWith('# ')) {
|
||||
sections['_title'] = line.replace('# ', '').trim();
|
||||
} else if (currentSection) {
|
||||
currentContent.push(line);
|
||||
}
|
||||
}
|
||||
if (currentSection) {
|
||||
sections[currentSection] = currentContent.join('\n').trim();
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
function formatContent(content) {
|
||||
if (!content) return '';
|
||||
|
||||
// Convert markdown lists to HTML
|
||||
let html = content
|
||||
.replace(/^(\d+)\.\s+(.*)$/gm, '<li>$2</li>')
|
||||
.replace(/^-\s+(.*)$/gm, '<li>$1</li>')
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
|
||||
// Wrap consecutive li elements in ul/ol
|
||||
if (html.includes('<li>')) {
|
||||
if (content.match(/^\d+\./m)) {
|
||||
html = '<ol>' + html + '</ol>';
|
||||
} else {
|
||||
html = '<ul>' + html + '</ul>';
|
||||
}
|
||||
}
|
||||
|
||||
// Convert line breaks to paragraphs for non-list content
|
||||
if (!html.includes('<li>')) {
|
||||
html = html.split('\n').filter(l => l.trim()).map(l => '<p>' + l + '</p>').join('');
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function createField(label, content, className = '') {
|
||||
if (!content) return '';
|
||||
return `
|
||||
<div class="field ${className}">
|
||||
<label class="field-label">${label} <span class="edit-icon" onclick="toggleEdit(this)" title="Edit field">✎</span></label>
|
||||
<div class="field-value ${content.includes('\n') ? 'multiline' : ''}">${formatContent(content)}</div>
|
||||
<textarea class="field-textarea">${content}</textarea>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function toggleEdit(icon) {
|
||||
const field = icon.closest('.field');
|
||||
field.classList.toggle('editing');
|
||||
if (field.classList.contains('editing')) {
|
||||
field.querySelector('.field-textarea').focus();
|
||||
}
|
||||
}
|
||||
|
||||
const sections = parseMarkdown(rawContent);
|
||||
const container = document.getElementById('form-content');
|
||||
|
||||
const fieldMap = [
|
||||
{ key: 'Tipo de usuario', label: 'Tipo de Usuario', class: '' },
|
||||
{ key: 'Donde empieza', label: 'Punto de Entrada', class: '' },
|
||||
{ key: 'Que quiere hacer el usuario', label: 'Objetivo del Usuario', class: '' },
|
||||
{ key: 'Pasos', label: 'Pasos', class: 'field-steps' },
|
||||
{ key: 'Que deberia pasar', label: 'Resultado Esperado', class: '' },
|
||||
{ key: 'Problemas comunes', label: 'Problemas Comunes', class: 'field-problems' },
|
||||
{ key: 'Casos especiales', label: 'Casos Especiales', class: 'field-special' },
|
||||
{ key: 'Flujos relacionados', label: 'Flujos Relacionados', class: '' },
|
||||
{ key: 'Notas tecnicas', label: 'Notas Tecnicas', class: 'field-technical' },
|
||||
];
|
||||
|
||||
let html = '';
|
||||
for (const field of fieldMap) {
|
||||
if (sections[field.key]) {
|
||||
html += createField(field.label, sections[field.key], field.class);
|
||||
}
|
||||
}
|
||||
container.innerHTML = html;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,92 @@
|
||||
# Gestion de Solicitudes de Servicio
|
||||
|
||||
## Tipo de usuario
|
||||
Administrador / Operaciones
|
||||
|
||||
## Donde empieza
|
||||
Backoffice `/admin/` -> Dashboard o Seccion "Solicitudes"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Ver, filtrar y gestionar todas las solicitudes de servicio del sistema
|
||||
|
||||
## Pasos - Ver solicitudes
|
||||
|
||||
1. Acceder al backoffice
|
||||
2. Ir a seccion "Solicitudes"
|
||||
3. Ver listado con todas las solicitudes
|
||||
4. Usar filtros para encontrar solicitudes especificas:
|
||||
- Por estado (Pendiente, Coordinado, Pagado, Completado, Cancelado)
|
||||
- Por fecha
|
||||
- Por barrio/zona
|
||||
- Por veterinario
|
||||
- Por dueno
|
||||
|
||||
## Pasos - Ver detalle de solicitud
|
||||
|
||||
1. Click en una solicitud del listado
|
||||
2. Ver toda la informacion:
|
||||
- Datos del dueno
|
||||
- Datos de la mascota
|
||||
- Servicios solicitados
|
||||
- Fechas preferidas
|
||||
- Veterinario asignado (si hay)
|
||||
- Estado de pago
|
||||
- Historial de cambios de estado
|
||||
|
||||
## Pasos - Asignar veterinario manualmente
|
||||
|
||||
1. Abrir solicitud en estado "Pendiente"
|
||||
2. Click en "Asignar veterinario"
|
||||
3. Ver lista de vets disponibles en la zona
|
||||
4. Seleccionar veterinario
|
||||
5. Seleccionar fecha y hora
|
||||
6. Confirmar asignacion
|
||||
7. Sistema notifica al vet y al dueno
|
||||
|
||||
## Pasos - Cambiar estado manualmente
|
||||
|
||||
1. Abrir solicitud
|
||||
2. Click en "Cambiar estado"
|
||||
3. Seleccionar nuevo estado
|
||||
4. Ingresar motivo (si aplica)
|
||||
5. Confirmar
|
||||
6. Se registra en el historial
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Vision completa de todas las solicitudes
|
||||
- Capacidad de intervenir cuando algo falla
|
||||
- Trazabilidad de cambios
|
||||
- Notificaciones a las partes afectadas
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Solicitud sin vet disponible en la zona
|
||||
- Usuario pago pero webhook fallo
|
||||
- Vet no puede asistir y hay que reasignar
|
||||
- Dueno quiere cancelar pero ya pago
|
||||
- Solicitudes duplicadas del mismo dueno
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Solicitud urgente sin vets disponibles
|
||||
- Reasignacion a ultimo momento
|
||||
- Cancelacion con reembolso
|
||||
- Solicitud con datos incompletos
|
||||
- Dueno no responde a confirmaciones
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Proceso de reembolso
|
||||
- Gestion de usuarios
|
||||
- Reportes
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /solicitudes/api/v1/service-requests/` (listado con filtros)
|
||||
- `GET /solicitudes/api/v1/service-requests/{id}/` (detalle)
|
||||
- `PATCH /solicitudes/api/v1/service-requests/{id}/` (asignar vet)
|
||||
- `PATCH /solicitudes/api/v1/change-service-request-state/` (cambiar estado)
|
||||
- Estados validos: pending, coordinated, payed, completed, cancelled
|
||||
- Permisos: solo staff puede ver todas las solicitudes
|
||||
@@ -0,0 +1,106 @@
|
||||
# Gestion de Usuarios
|
||||
|
||||
## Tipo de usuario
|
||||
Administrador / Operaciones
|
||||
|
||||
## Donde empieza
|
||||
Backoffice `/admin/` -> Seccion "Usuarios" o "Duenos" / "Veterinarios"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Administrar duenos de mascotas y veterinarios del sistema
|
||||
|
||||
## Pasos - Ver/Buscar duenos
|
||||
|
||||
1. Ir a "Duenos de mascotas"
|
||||
2. Ver listado de todos los duenos
|
||||
3. Usar buscador (nombre, email, telefono)
|
||||
4. Filtrar por:
|
||||
- Tipo (Registrado / Invitado)
|
||||
- Fecha de registro
|
||||
- Con/sin turnos activos
|
||||
|
||||
## Pasos - Ver detalle de dueno
|
||||
|
||||
1. Click en un dueno
|
||||
2. Ver informacion completa:
|
||||
- Datos personales
|
||||
- Direcciones registradas
|
||||
- Mascotas
|
||||
- Historial de solicitudes
|
||||
- Historial de pagos
|
||||
|
||||
## Pasos - Editar dueno
|
||||
|
||||
1. En el detalle, click "Editar"
|
||||
2. Modificar campos permitidos:
|
||||
- Nombre
|
||||
- Telefono
|
||||
- Email
|
||||
- Direccion
|
||||
3. Guardar cambios
|
||||
|
||||
## Pasos - Gestionar veterinarios
|
||||
|
||||
1. Ir a "Veterinarios"
|
||||
2. Ver listado con:
|
||||
- Nombre y matricula
|
||||
- Zonas de cobertura
|
||||
- Estado (Activo/Inactivo)
|
||||
- Turnos del mes
|
||||
|
||||
## Pasos - Agregar nuevo veterinario
|
||||
|
||||
1. Click en "Agregar veterinario"
|
||||
2. Completar datos:
|
||||
- Nombre completo
|
||||
- Numero de matricula
|
||||
- Email
|
||||
- Telefono
|
||||
- Especialidades
|
||||
- Zonas de cobertura inicial
|
||||
3. Crear credenciales de acceso
|
||||
4. Guardar
|
||||
|
||||
## Pasos - Desactivar veterinario
|
||||
|
||||
1. Abrir perfil del vet
|
||||
2. Click en "Desactivar"
|
||||
3. Confirmar
|
||||
4. Vet deja de recibir solicitudes
|
||||
5. Visitas ya aceptadas se mantienen o se reasignan
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Gestion completa de usuarios del sistema
|
||||
- Capacidad de corregir datos erroneos
|
||||
- Alta/baja de veterinarios
|
||||
- Vision del estado de cada usuario
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Duenos duplicados (se registraron con email diferente)
|
||||
- Vet quiere darse de baja pero tiene citas pendientes
|
||||
- Email incorrecto y no reciben notificaciones
|
||||
- Cuenta de invitado que quiere convertirse en registrada
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Fusionar cuentas duplicadas
|
||||
- Transferir mascota a otro dueno
|
||||
- Vet de vacaciones temporalmente
|
||||
- Cambio de numero de matricula
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Gestion de solicitudes (ver historial por usuario)
|
||||
- Reportes (filtrar por usuario)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /mascotas/api/v1/pet-owners/` (duenos)
|
||||
- `GET /mascotas/api/v1/veterinarians/` (vets)
|
||||
- `POST /mascotas/api/v1/veterinarians/` (crear vet)
|
||||
- `PATCH /mascotas/api/v1/veterinarians/{id}/` (editar/desactivar)
|
||||
- Permisos: solo staff puede modificar usuarios
|
||||
- Soft delete para mantener historial
|
||||
@@ -0,0 +1,98 @@
|
||||
# Gestion de Servicios y Precios
|
||||
|
||||
## Tipo de usuario
|
||||
Administrador
|
||||
|
||||
## Donde empieza
|
||||
Backoffice `/admin/` -> Seccion "Servicios" o Django Admin -> Productos
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Administrar el catalogo de servicios veterinarios y sus precios
|
||||
|
||||
## Pasos - Ver servicios
|
||||
|
||||
1. Ir a "Servicios"
|
||||
2. Ver listado de todos los servicios
|
||||
3. Filtrar por:
|
||||
- Categoria (Vacunacion, Consulta, Cirugia, etc.)
|
||||
- Tipo de mascota (Perro, Gato, Ambos)
|
||||
- Estado (Activo/Inactivo)
|
||||
|
||||
## Pasos - Agregar nuevo servicio
|
||||
|
||||
1. Click en "Agregar servicio"
|
||||
2. Completar datos:
|
||||
- Nombre del servicio
|
||||
- Descripcion
|
||||
- Categoria
|
||||
- Tipo de mascota aplicable
|
||||
- Precio base
|
||||
- Duracion estimada
|
||||
- Requiere otros servicios (dependencias)
|
||||
3. Guardar
|
||||
|
||||
## Pasos - Editar servicio
|
||||
|
||||
1. Abrir servicio existente
|
||||
2. Modificar campos
|
||||
3. Guardar
|
||||
4. Cambios aplican a nuevas solicitudes
|
||||
|
||||
## Pasos - Gestionar precios
|
||||
|
||||
1. Abrir servicio
|
||||
2. Ir a seccion "Precios"
|
||||
3. Ver precio actual
|
||||
4. Para cambiar precio:
|
||||
- Click "Agregar precio"
|
||||
- Ingresar monto
|
||||
- Definir fecha de vigencia
|
||||
- Guardar
|
||||
5. Precio anterior queda en historial
|
||||
|
||||
## Pasos - Configurar combos
|
||||
|
||||
1. Ir a "Combos" o "Paquetes"
|
||||
2. Crear nuevo combo:
|
||||
- Nombre del combo
|
||||
- Servicios incluidos
|
||||
- Precio del combo (con descuento)
|
||||
- Condiciones de aplicacion
|
||||
3. Guardar
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Catalogo actualizado de servicios
|
||||
- Precios correctos en el turnero
|
||||
- Combos aplicados automaticamente
|
||||
- Historial de precios para facturacion
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Cambio de precio afecta solicitudes ya creadas
|
||||
- Servicio deprecado pero con solicitudes pendientes
|
||||
- Combo no se aplica automaticamente
|
||||
- Precio diferente por zona/vet
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Servicio con precio variable (depende de peso mascota)
|
||||
- Servicio estacional (solo ciertas epocas)
|
||||
- Promocion temporal
|
||||
- Precio especial por vet
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Turnero (muestra servicios disponibles)
|
||||
- Facturacion (usa precios)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /productos/api/v1/services/` (servicios)
|
||||
- `POST /productos/api/v1/services/` (crear)
|
||||
- `PATCH /productos/api/v1/services/{id}/` (editar)
|
||||
- `GET /productos/api/v1/prices/` (precios)
|
||||
- `POST /productos/api/v1/prices/` (nuevo precio)
|
||||
- Modelo Price tiene fecha de vigencia
|
||||
- Precio se congela al momento de crear solicitud
|
||||
@@ -0,0 +1,82 @@
|
||||
# Proceso de Reembolso
|
||||
|
||||
## Tipo de usuario
|
||||
Administrador / Operaciones
|
||||
|
||||
## Donde empieza
|
||||
Backoffice -> Solicitud pagada -> "Procesar reembolso" o Seccion "Pagos"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Procesar un reembolso para un turno que fue cancelado despues del pago
|
||||
|
||||
## Pasos - Identificar solicitud a reembolsar
|
||||
|
||||
1. Buscar la solicitud por:
|
||||
- ID de solicitud
|
||||
- Nombre del dueno
|
||||
- Numero de operacion de Mercado Pago
|
||||
2. Verificar que esta en estado "Pagado"
|
||||
3. Verificar que el pago fue exitoso
|
||||
|
||||
## Pasos - Procesar reembolso
|
||||
|
||||
1. Abrir detalle de la solicitud
|
||||
2. Click en "Procesar reembolso"
|
||||
3. Ver informacion del pago:
|
||||
- Monto pagado
|
||||
- Fecha de pago
|
||||
- Metodo de pago
|
||||
- ID de operacion MP
|
||||
4. Seleccionar tipo de reembolso:
|
||||
- Total
|
||||
- Parcial (ingresar monto)
|
||||
5. Ingresar motivo del reembolso
|
||||
6. Confirmar
|
||||
7. Sistema procesa reembolso en Mercado Pago
|
||||
8. Actualizar estado de la solicitud
|
||||
|
||||
## Pasos - Verificar reembolso
|
||||
|
||||
1. Ir a seccion "Pagos" o historial de la solicitud
|
||||
2. Verificar que el reembolso fue procesado
|
||||
3. Ver estado del reembolso en Mercado Pago
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Reembolso procesado en Mercado Pago
|
||||
- Dueno recibe el dinero (segun tiempos de MP)
|
||||
- Solicitud actualiza estado
|
||||
- Registro en el historial
|
||||
- Dueno notificado por email
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Reembolso parcial no soportado por el metodo de pago
|
||||
- Plazo de reembolso vencido (politica de MP)
|
||||
- Dueno reclama que no recibio el dinero
|
||||
- Reembolso duplicado por error
|
||||
- Pago original en disputa
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Reembolso por mala atencion
|
||||
- Reembolso parcial (solo algunos servicios)
|
||||
- Visita incompleta (reembolso proporcional)
|
||||
- Dueno quiere credito en vez de reembolso
|
||||
- Reembolso fuera de plazo (solucion alternativa)
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Gestion de solicitudes
|
||||
- Cancelacion de turno
|
||||
- Atencion al cliente
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- API Mercado Pago: `POST /v1/payments/{id}/refunds`
|
||||
- Tiempos de acreditacion:
|
||||
- Tarjeta credito: 1-2 estados de cuenta
|
||||
- Tarjeta debito: 5-10 dias habiles
|
||||
- Dinero en cuenta MP: inmediato
|
||||
- Plazo maximo para reembolso: 180 dias desde el pago
|
||||
- Se debe guardar log de todos los reembolsos
|
||||
@@ -0,0 +1,99 @@
|
||||
# Reportes y Dashboard
|
||||
|
||||
## Tipo de usuario
|
||||
Administrador / Gerencia
|
||||
|
||||
## Donde empieza
|
||||
Backoffice `/admin/` -> Dashboard o Seccion "Reportes"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Ver metricas del negocio, generar reportes y analizar datos
|
||||
|
||||
## Pasos - Ver dashboard
|
||||
|
||||
1. Acceder al backoffice
|
||||
2. Ver dashboard principal con:
|
||||
- Solicitudes del dia/semana/mes
|
||||
- Ingresos del periodo
|
||||
- Visitas completadas
|
||||
- Conversion (solicitudes -> pagos)
|
||||
- Vets activos
|
||||
|
||||
## Pasos - Generar reporte de solicitudes
|
||||
|
||||
1. Ir a "Reportes" -> "Solicitudes"
|
||||
2. Seleccionar rango de fechas
|
||||
3. Filtrar por:
|
||||
- Estado
|
||||
- Zona
|
||||
- Veterinario
|
||||
- Tipo de servicio
|
||||
4. Click "Generar"
|
||||
5. Ver tabla con resultados
|
||||
6. Opcion de exportar (CSV/Excel)
|
||||
|
||||
## Pasos - Generar reporte de ingresos
|
||||
|
||||
1. Ir a "Reportes" -> "Ingresos"
|
||||
2. Seleccionar periodo
|
||||
3. Agrupar por:
|
||||
- Dia/Semana/Mes
|
||||
- Veterinario
|
||||
- Servicio
|
||||
- Zona
|
||||
4. Ver grafico y tabla
|
||||
5. Exportar si es necesario
|
||||
|
||||
## Pasos - Reporte de veterinarios
|
||||
|
||||
1. Ir a "Reportes" -> "Veterinarios"
|
||||
2. Ver para cada vet:
|
||||
- Visitas completadas
|
||||
- Calificacion promedio
|
||||
- Ingresos generados
|
||||
- Tasa de cancelacion
|
||||
3. Filtrar por periodo
|
||||
4. Ordenar por metrica
|
||||
|
||||
## Pasos - Exportar datos
|
||||
|
||||
1. En cualquier reporte, click "Exportar"
|
||||
2. Seleccionar formato (CSV, Excel)
|
||||
3. Descargar archivo
|
||||
4. Usar para analisis externo o contabilidad
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Vision clara del estado del negocio
|
||||
- Datos para toma de decisiones
|
||||
- Reportes exportables para contabilidad
|
||||
- Identificacion de tendencias
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Datos no actualizados en tiempo real
|
||||
- Filtros que no devuelven lo esperado
|
||||
- Discrepancia entre reportes y realidad
|
||||
- Exportacion con formato incorrecto
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Reporte de prueba vs produccion
|
||||
- Datos historicos antes de implementar filtro
|
||||
- Comparativa año vs año
|
||||
- Reporte personalizado para inversores
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Gestion de solicitudes (fuente de datos)
|
||||
- Facturacion AFIP
|
||||
- Google Sheets sync
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- API:
|
||||
- `GET /mascotas/api/v1/stats-summary` (metricas principales)
|
||||
- `GET /solicitudes/api/v1/service-requests/?date_from=X&date_to=Y` (datos crudos)
|
||||
- Google Sheets: sincronizacion automatica via Celery
|
||||
- Dashboard actualiza cada 5 minutos
|
||||
- Reportes pesados: generar async y notificar cuando estan listos
|
||||
@@ -0,0 +1,55 @@
|
||||
# Registro de Usuario
|
||||
|
||||
## Tipo de usuario
|
||||
Dueno de mascota (nuevo)
|
||||
|
||||
## Donde empieza
|
||||
Pagina principal -> Boton "Registrarse" o `/register`
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Crear una cuenta nueva para poder reservar turnos y gestionar sus mascotas
|
||||
|
||||
## Pasos
|
||||
|
||||
1. Click en "Registrarse" en la pagina principal
|
||||
2. Ingresar email
|
||||
3. Ingresar contraseña
|
||||
4. Confirmar contraseña
|
||||
5. Aceptar terminos y condiciones
|
||||
6. Click en "Crear cuenta"
|
||||
7. Recibir email de verificacion
|
||||
8. Click en el link de verificacion
|
||||
9. Cuenta activada, redirige al dashboard
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Usuario creado en el sistema
|
||||
- Email de bienvenida enviado
|
||||
- Puede iniciar sesion inmediatamente
|
||||
- Ve su dashboard vacio (sin mascotas, sin turnos)
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- El email ya esta registrado pero el usuario no lo recuerda
|
||||
- El email de verificacion llega a spam
|
||||
- La contraseña no cumple los requisitos minimos
|
||||
- El usuario cierra el browser antes de verificar y no encuentra el email despues
|
||||
- Usuarios intentan registrarse con email de otra persona
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Si el email existe como usuario invitado (del turnero), deberia linkear las cuentas
|
||||
- Si el usuario ya tiene cuenta pero no verificada, reenviar email de verificacion
|
||||
- Registro desde el flujo de turnero (ya ingreso datos, solo falta contraseña)
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Login (despues de registrarse)
|
||||
- Turnero (puede disparar registro)
|
||||
- Recuperar contraseña (si ya tenia cuenta y no recuerda)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- API: `POST /common/users/create/`
|
||||
- Validacion de contraseña: minimo 8 caracteres, 1 numero, 1 mayuscula
|
||||
- Verificacion expira en 24 horas
|
||||
@@ -0,0 +1,77 @@
|
||||
# Reservar Turno (Turnero)
|
||||
|
||||
## Tipo de usuario
|
||||
Dueno de mascota (invitado, registrado, o recurrente)
|
||||
|
||||
## Donde empieza
|
||||
Pagina principal -> Boton "Agendar visita" o enlace directo `/turnero`
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Reservar un turno veterinario a domicilio para su mascota
|
||||
|
||||
## Pasos
|
||||
|
||||
1. Click en "Agendar visita"
|
||||
2. Ingresar direccion en el popup de cobertura
|
||||
3. Sistema verifica si hay cobertura en esa zona
|
||||
4. Si hay cobertura, click "Siguiente"
|
||||
5. Completar datos de la mascota:
|
||||
- Nombre
|
||||
- Tipo (Perro/Gato)
|
||||
- Raza (opcional)
|
||||
- Edad aproximada
|
||||
- Peso aproximado
|
||||
- Si esta castrado/a
|
||||
6. Seleccionar categoria de servicio (Vacunacion, Consulta, etc.)
|
||||
7. Seleccionar servicios especificos
|
||||
8. Ver que algunos servicios se agregan automaticamente (ej: consulta clinica)
|
||||
9. Seleccionar fechas preferidas (hasta 3)
|
||||
10. Seleccionar franja horaria preferida
|
||||
11. Ingresar datos de contacto:
|
||||
- Nombre completo
|
||||
- Telefono
|
||||
- Email
|
||||
12. Revisar resumen
|
||||
13. Enviar solicitud
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Solicitud creada con estado "Pendiente"
|
||||
- Email de confirmacion al usuario
|
||||
- Notificacion a veterinarios de la zona
|
||||
- Aparece en "Mis turnos" del usuario
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- No hay cobertura en la zona del usuario
|
||||
- El autocompletado de direcciones no encuentra la calle
|
||||
- Usuario no puede quitar "Consulta clinica" cuando selecciona vacunacion
|
||||
- Confunden "fecha preferida" con "fecha confirmada"
|
||||
- No entienden por que no pueden elegir hora exacta
|
||||
- El total no se muestra hasta el final y sorprende al usuario
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Usuario invitado: se crea como guest, puede registrarse despues
|
||||
- Usuario logueado: datos pre-llenados, mascota puede ser existente
|
||||
- Usuario recurrente (mismo email): sistema detecta y sugiere linkear
|
||||
- Mascota ya castrada: no mostrar servicio de castracion
|
||||
- Servicios combo: algunos incluyen otros automaticamente
|
||||
- Zona sin cobertura: mostrar formulario para avisar cuando haya
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Ver mis turnos (despues de reservar)
|
||||
- Pagar turno (cuando se coordina)
|
||||
- Cancelar turno
|
||||
- Agregar mascota (si ya tiene cuenta)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `POST /mascotas/api/v1/coverage-check/` (verificar cobertura)
|
||||
- `POST /mascotas/api/v1/pet-owners/` (crear dueno invitado)
|
||||
- `POST /mascotas/api/v1/pets/` (crear mascota)
|
||||
- `GET /productos/api/v1/services/` (obtener servicios)
|
||||
- `POST /solicitudes/api/v1/service-requests/` (crear solicitud)
|
||||
- Estados de solicitud: pending -> coordinated -> payed -> completed
|
||||
@@ -0,0 +1,79 @@
|
||||
# Gestion de Mascotas
|
||||
|
||||
## Tipo de usuario
|
||||
Dueno de mascota (registrado)
|
||||
|
||||
## Donde empieza
|
||||
Dashboard -> Seccion "Mis mascotas" o `/user/pets`
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Agregar, editar o ver informacion de sus mascotas
|
||||
|
||||
## Pasos - Agregar mascota
|
||||
|
||||
1. Ir a "Mis mascotas"
|
||||
2. Click en "Agregar mascota"
|
||||
3. Completar formulario:
|
||||
- Nombre (obligatorio)
|
||||
- Tipo: Perro o Gato (obligatorio)
|
||||
- Raza (opcional, lista segun tipo)
|
||||
- Fecha de nacimiento o edad aproximada
|
||||
- Peso en kg (opcional)
|
||||
- Sexo
|
||||
- Esta castrado/a (checkbox)
|
||||
- Foto (opcional)
|
||||
4. Guardar
|
||||
|
||||
## Pasos - Editar mascota
|
||||
|
||||
1. Ir a "Mis mascotas"
|
||||
2. Click en la tarjeta de la mascota
|
||||
3. Click en "Editar"
|
||||
4. Modificar campos deseados
|
||||
5. Guardar
|
||||
|
||||
## Pasos - Ver historial medico
|
||||
|
||||
1. Ir a "Mis mascotas"
|
||||
2. Click en la tarjeta de la mascota
|
||||
3. Ver seccion "Historial de visitas"
|
||||
4. Click en una visita para ver el informe completo
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Mascota aparece en el listado
|
||||
- Disponible para seleccionar en el turnero
|
||||
- Historial medico accesible
|
||||
- Foto visible en la tarjeta
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Usuarios quieren agregar mascotas de otros tipos (conejo, ave) - no soportado
|
||||
- No encuentran donde ver el historial medico
|
||||
- Quieren eliminar mascota pero tiene turnos pendientes
|
||||
- Foto muy grande no sube
|
||||
- Confunden edad con fecha de nacimiento
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Mascota creada desde turnero como invitado: aparece cuando linkea cuenta
|
||||
- Mascota fallecida: deberia poder marcarse (soft delete)
|
||||
- Multiples mascotas con mismo nombre: permitido pero confuso
|
||||
- Cambio de peso significativo: registrar historial?
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Reservar turno (seleccionar mascota existente)
|
||||
- Ver historial medico
|
||||
- Registro (crear primera mascota)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /mascotas/api/v1/pets/` (listar)
|
||||
- `POST /mascotas/api/v1/pets/` (crear)
|
||||
- `PATCH /mascotas/api/v1/pets/{id}/` (editar)
|
||||
- `DELETE /mascotas/api/v1/pets/{id}/` (soft delete)
|
||||
- `GET /mascotas/api/v1/vet-visits/?pet={id}` (historial)
|
||||
- Tipos soportados: solo Perro y Gato por ahora
|
||||
- Razas: lista predefinida por tipo
|
||||
@@ -0,0 +1,66 @@
|
||||
# Pago de Turno
|
||||
|
||||
## Tipo de usuario
|
||||
Dueno de mascota (con turno coordinado)
|
||||
|
||||
## Donde empieza
|
||||
Email de confirmacion de turno -> Link "Pagar" o Dashboard -> Mis turnos -> Boton "Pagar"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Pagar un turno que ya fue coordinado con fecha y veterinario asignado
|
||||
|
||||
## Pasos
|
||||
|
||||
1. Recibir email/notificacion de que el turno fue coordinado
|
||||
2. Ver detalle del turno con fecha, hora y veterinario asignado
|
||||
3. Verificar el monto total
|
||||
4. Click en "Pagar"
|
||||
5. Redireccion a Mercado Pago
|
||||
6. Seleccionar metodo de pago:
|
||||
- Tarjeta de credito/debito
|
||||
- Dinero en cuenta MP
|
||||
- Transferencia bancaria
|
||||
- Efectivo (Rapipago/PagoFacil)
|
||||
7. Completar pago
|
||||
8. Redireccion de vuelta a la plataforma
|
||||
9. Ver confirmacion de pago exitoso
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Estado del turno cambia a "Pagado"
|
||||
- Email de confirmacion de pago
|
||||
- Comprobante disponible
|
||||
- Veterinario notificado
|
||||
- Turno aparece como confirmado en el calendario
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Usuario cierra el browser durante el pago y no sabe si se proceso
|
||||
- Pago rechazado por fondos insuficientes
|
||||
- Link de pago expira y no pueden pagr
|
||||
- Quieren pagar en efectivo el dia de la visita
|
||||
- Precio cambio entre coordinacion y pago
|
||||
- Quieren aplicar descuento/cupon y no encuentran donde
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Pago parcial: no soportado actualmente
|
||||
- Pago con tarjeta rechazada: puede reintentar
|
||||
- Pago por transferencia: demora en acreditarse (pendiente de confirmacion)
|
||||
- Usuario paga pero webhook falla: requiere intervencion manual
|
||||
- Turno cancelado despues de pagar: proceso de reembolso
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Ver mis turnos (antes y despues de pagar)
|
||||
- Cancelar turno (si decide no pagar)
|
||||
- Contactar soporte (problemas con pago)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `POST /payments/api/v1/requests/{id}/preference/` (generar link MP)
|
||||
- Webhook: `POST /payments/mp/webhook/` (notificacion de MP)
|
||||
- Integracion: Mercado Pago Checkout Pro
|
||||
- Estados: coordinated -> payed (despues de webhook exitoso)
|
||||
- Timeout de preferencia: 24 horas
|
||||
@@ -0,0 +1,79 @@
|
||||
# Ver Historial Medico
|
||||
|
||||
## Tipo de usuario
|
||||
Dueno de mascota (registrado, con visitas completadas)
|
||||
|
||||
## Donde empieza
|
||||
Dashboard -> Mis mascotas -> Mascota -> "Ver historial" o Mis turnos -> Turno completado -> "Ver informe"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Ver los informes medicos de las visitas veterinarias de su mascota
|
||||
|
||||
## Pasos - Desde mascotas
|
||||
|
||||
1. Ir a "Mis mascotas"
|
||||
2. Seleccionar la mascota
|
||||
3. Ver seccion "Historial de visitas"
|
||||
4. Lista de visitas ordenadas por fecha (mas reciente primero)
|
||||
5. Click en una visita para ver detalle
|
||||
|
||||
## Pasos - Desde turnos
|
||||
|
||||
1. Ir a "Mis turnos"
|
||||
2. Filtrar por "Completados"
|
||||
3. Click en un turno completado
|
||||
4. Click en "Ver informe medico"
|
||||
|
||||
## Que muestra el informe
|
||||
|
||||
- Fecha de la visita
|
||||
- Veterinario que atendio
|
||||
- Motivo de consulta / servicios realizados
|
||||
- Examen fisico:
|
||||
- Peso registrado
|
||||
- Temperatura
|
||||
- Frecuencia cardiaca
|
||||
- Observaciones generales
|
||||
- Diagnostico
|
||||
- Tratamiento indicado
|
||||
- Medicamentos recetados (nombre, dosis, frecuencia, duracion)
|
||||
- Estudios solicitados
|
||||
- Proximos pasos / seguimiento recomendado
|
||||
- Observaciones adicionales
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Usuario puede ver todos los informes de sus mascotas
|
||||
- Puede descargar/imprimir el informe
|
||||
- Informacion clara y entendible
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Visita completada pero informe no cargado todavia
|
||||
- Usuario quiere editar algo del informe (no puede, es del vet)
|
||||
- Terminologia medica confusa
|
||||
- No encuentra informe de visita antigua
|
||||
- Quiere compartir informe con otro veterinario
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Visita sin informe: vet no lo cargo (deberia ser obligatorio)
|
||||
- Informe incompleto: campos vacios
|
||||
- Multiples visitas mismo dia: mostrar todas
|
||||
- Mascota transferida de otro dueno: historial previo?
|
||||
- Estudios pendientes: mostrar estado
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Gestion de mascotas
|
||||
- Reservar turno de seguimiento
|
||||
- Contactar veterinario (dudas sobre informe)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /mascotas/api/v1/vet-visits/?pet={id}` (listar visitas)
|
||||
- `GET /mascotas/api/v1/vet-visits/{id}/` (detalle visita)
|
||||
- `GET /mascotas/api/v1/vetvisitreport/?vet_visit={id}` (informe)
|
||||
- PDF: generado on-demand o pre-generado?
|
||||
- Permisos: solo dueno de la mascota puede ver
|
||||
@@ -0,0 +1,77 @@
|
||||
# Aceptar/Rechazar Solicitud de Servicio
|
||||
|
||||
## Tipo de usuario
|
||||
Veterinario
|
||||
|
||||
## Donde empieza
|
||||
Dashboard veterinario `/vet/` -> Seccion "Solicitudes pendientes"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Revisar las solicitudes de servicio en su zona y decidir si las acepta o rechaza
|
||||
|
||||
## Pasos - Aceptar solicitud
|
||||
|
||||
1. Ingresar al dashboard veterinario
|
||||
2. Ver listado de solicitudes pendientes en su zona de cobertura
|
||||
3. Click en una solicitud para ver detalle:
|
||||
- Datos del dueno (nombre, telefono, direccion)
|
||||
- Datos de la mascota (nombre, tipo, edad, historial previo)
|
||||
- Servicios solicitados
|
||||
- Fechas preferidas por el dueno
|
||||
4. Verificar disponibilidad en esas fechas
|
||||
5. Click en "Aceptar"
|
||||
6. Seleccionar fecha y hora disponible
|
||||
7. Confirmar aceptacion
|
||||
|
||||
## Pasos - Rechazar solicitud
|
||||
|
||||
1. Ver detalle de la solicitud
|
||||
2. Click en "Rechazar"
|
||||
3. Opcionalmente seleccionar motivo:
|
||||
- No tengo disponibilidad
|
||||
- Fuera de mi zona
|
||||
- No realizo este servicio
|
||||
- Otro
|
||||
4. Confirmar rechazo
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
Al aceptar:
|
||||
- Solicitud pasa a estado "Coordinado"
|
||||
- Vet queda asignado a la solicitud
|
||||
- Dueno recibe notificacion con fecha/hora y datos del vet
|
||||
- Aparece en el calendario del vet
|
||||
|
||||
Al rechazar:
|
||||
- Solicitud sigue disponible para otros vets
|
||||
- Si la rechazo yo, ya no la veo en mi lista
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Multiples vets aceptan la misma solicitud casi simultaneamente
|
||||
- El vet acepta pero el dueno no puede en esa fecha
|
||||
- No hay suficientes vets en la zona y la solicitud queda sin atender
|
||||
- Vet acepta por error y quiere deshacer
|
||||
- Las fechas preferidas ya pasaron
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Solicitud con mascota que el vet ya atendio antes: mostrar historial
|
||||
- Solicitud urgente/emergencia: destacar visualmente
|
||||
- Vet con agenda completa: no deberia poder aceptar
|
||||
- Solicitud con multiples mascotas: revisar tiempo necesario
|
||||
- Zona limite: solicitud que aparece para vets de zonas adyacentes
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Ver mi agenda (despues de aceptar)
|
||||
- Realizar visita
|
||||
- Cancelar visita aceptada
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /solicitudes/api/v1/service-requests/?state=pending&veterinarian_area=X`
|
||||
- `PATCH /solicitudes/api/v1/service-requests/{id}/` (aceptar/rechazar)
|
||||
- Race condition: backend debe manejar caso de aceptacion simultanea
|
||||
- Push notification al dueno cuando se acepta
|
||||
@@ -0,0 +1,87 @@
|
||||
# Gestion de Agenda
|
||||
|
||||
## Tipo de usuario
|
||||
Veterinario
|
||||
|
||||
## Donde empieza
|
||||
Dashboard veterinario `/vet/` -> Menu "Mi agenda" o `/vet/turnos`
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Ver su calendario de visitas, gestionar disponibilidad y marcar dias no disponibles
|
||||
|
||||
## Pasos - Ver agenda
|
||||
|
||||
1. Ir a "Mi agenda"
|
||||
2. Ver calendario con:
|
||||
- Visitas confirmadas (con detalle al hacer click)
|
||||
- Dias/horarios bloqueados
|
||||
- Disponibilidad por franja horaria
|
||||
3. Navegar entre semanas/meses
|
||||
4. Filtrar por estado (pendientes, confirmadas, completadas)
|
||||
|
||||
## Pasos - Configurar disponibilidad semanal
|
||||
|
||||
1. Ir a "Configuracion" -> "Mi disponibilidad"
|
||||
2. Para cada dia de la semana:
|
||||
- Marcar si trabajo ese dia
|
||||
- Definir franja horaria (ej: 9:00-18:00)
|
||||
- Definir pausa (ej: 13:00-14:00)
|
||||
3. Guardar configuracion
|
||||
|
||||
## Pasos - Bloquear dias especificos
|
||||
|
||||
1. En el calendario, click en un dia
|
||||
2. Seleccionar "Marcar como no disponible"
|
||||
3. Opcionalmente agregar motivo (vacaciones, curso, etc.)
|
||||
4. Confirmar
|
||||
5. Ese dia no aparece como opcion para nuevas solicitudes
|
||||
|
||||
## Pasos - Ver detalle de visita
|
||||
|
||||
1. Click en una visita en el calendario
|
||||
2. Ver informacion completa:
|
||||
- Dueno: nombre, telefono, direccion
|
||||
- Mascota: nombre, tipo, edad, foto
|
||||
- Servicios a realizar
|
||||
- Historial previo de esa mascota
|
||||
- Estado de pago
|
||||
- Notas especiales
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Calendario siempre actualizado con visitas
|
||||
- No puedo recibir solicitudes en dias bloqueados
|
||||
- Cambios de disponibilidad afectan solo solicitudes futuras
|
||||
- Puedo ver toda la informacion necesaria para la visita
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Vet olvida desbloquear dia despues de terminar vacaciones
|
||||
- Visitas muy seguidas en zonas distantes (logistica)
|
||||
- Vet quiere mover visita a otro horario
|
||||
- Visita cancelada a ultimo momento
|
||||
- Vet no marco indisponibilidad y le llegan solicitudes que no puede atender
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Visita reprogramada: mostrar historial de cambios
|
||||
- Visita cancelada por el dueno: liberar espacio en agenda
|
||||
- Emergencia: poder agregar visita fuera de horario normal
|
||||
- Feriados: bloquear automaticamente?
|
||||
- Multiples visitas mismo dia/zona: optimizar ruta?
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Aceptar solicitud (agrega a la agenda)
|
||||
- Realizar visita (inicia desde la agenda)
|
||||
- Gestionar zonas de cobertura
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /mascotas/api/v1/vet-visits/?veterinarian=X`
|
||||
- `GET /mascotas/api/v1/vet_availabilities/`
|
||||
- `PUT /mascotas/api/v1/vet_availabilities/{id}/`
|
||||
- `POST /mascotas/api/v1/vet_unavailabilities/` (dias bloqueados)
|
||||
- Vista calendario: usar libreria tipo FullCalendar
|
||||
- Colores por estado: pendiente (amarillo), pagado (verde), completado (azul)
|
||||
@@ -0,0 +1,97 @@
|
||||
# Realizar Visita y Crear Informe
|
||||
|
||||
## Tipo de usuario
|
||||
Veterinario
|
||||
|
||||
## Donde empieza
|
||||
Agenda del dia -> Visita programada -> "Iniciar visita" o `/vet/visita/{id}`
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Registrar la visita medica, crear el informe clinico y completar el flujo
|
||||
|
||||
## Pasos - Antes de la visita
|
||||
|
||||
1. Ver agenda del dia
|
||||
2. Click en la visita programada
|
||||
3. Ver datos del dueno (direccion, telefono) para llegar
|
||||
4. Ver datos de la mascota e historial
|
||||
5. Preparar insumos segun servicios solicitados
|
||||
|
||||
## Pasos - Iniciar visita
|
||||
|
||||
1. Al llegar al domicilio, click "Iniciar visita"
|
||||
2. Estado cambia a "En progreso"
|
||||
3. Se registra hora de inicio
|
||||
|
||||
## Pasos - Crear informe medico
|
||||
|
||||
1. Durante o despues de la atencion, ir a "Crear informe"
|
||||
2. Completar examen fisico:
|
||||
- Peso actual
|
||||
- Temperatura
|
||||
- Frecuencia cardiaca
|
||||
- Frecuencia respiratoria
|
||||
- Estado general (mucosas, hidratacion, etc.)
|
||||
3. Registrar hallazgos clinicos
|
||||
4. Escribir diagnostico
|
||||
5. Indicar tratamiento:
|
||||
- Descripcion del tratamiento
|
||||
- Medicamentos recetados (nombre, dosis, frecuencia, duracion)
|
||||
- Indicaciones especiales
|
||||
6. Solicitar estudios si es necesario:
|
||||
- Tipo de estudio
|
||||
- Laboratorio sugerido
|
||||
7. Programar seguimiento:
|
||||
- Fecha sugerida de control
|
||||
- Motivo del seguimiento
|
||||
8. Agregar observaciones adicionales
|
||||
9. Guardar informe
|
||||
|
||||
## Pasos - Completar visita
|
||||
|
||||
1. Verificar que el informe este guardado
|
||||
2. Click en "Completar visita"
|
||||
3. Confirmar finalizacion
|
||||
4. Se genera factura automaticamente (AFIP)
|
||||
5. Dueno recibe notificacion con acceso al informe
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Informe queda guardado y asociado a la mascota
|
||||
- Dueno puede ver el informe desde su cuenta
|
||||
- Se genera factura electronica
|
||||
- Visita se marca como "Completada"
|
||||
- Vet puede ver la visita en su historial
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Vet olvida completar el informe y cierra la visita
|
||||
- Mala conexion a internet durante la visita
|
||||
- Informe muy largo y se pierde por timeout
|
||||
- Dueno no esta en el domicilio
|
||||
- Mascota agresiva, no se puede atender
|
||||
- Necesita derivar a clinica
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Visita cancelada in situ: registrar motivo, posible cargo
|
||||
- Emergencia durante visita rutinaria: agregar servicios
|
||||
- Multiples mascotas en la visita: un informe por cada una
|
||||
- Vet necesita segundo opinion: derivar a especialista
|
||||
- Fallecimiento durante la visita: protocolo especial
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Ver historial de paciente (antes de atender)
|
||||
- Cobro de la visita (Mercado Pago del vet)
|
||||
- Seguimiento post-visita
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `PATCH /mascotas/api/v1/vet-visits/{id}/` (cambiar estado)
|
||||
- `POST /mascotas/api/v1/vetvisitreport/` (crear informe)
|
||||
- `GET /mascotas/api/v1/vet-visits/?pet=X` (historial previo)
|
||||
- Integracion AFIP via django_afip
|
||||
- Guardado automatico del informe cada 30 segundos
|
||||
- Modo offline: guardar localmente y sincronizar despues
|
||||
@@ -0,0 +1,66 @@
|
||||
# Gestionar Zonas de Cobertura
|
||||
|
||||
## Tipo de usuario
|
||||
Veterinario
|
||||
|
||||
## Donde empieza
|
||||
Dashboard veterinario -> Menu "Mi cobertura" o Perfil -> "Zonas de atencion"
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Definir en que barrios/zonas esta dispuesto a atender visitas a domicilio
|
||||
|
||||
## Pasos - Ver zonas actuales
|
||||
|
||||
1. Ir a "Mi cobertura"
|
||||
2. Ver mapa con zonas actualmente cubiertas
|
||||
3. Ver listado de barrios seleccionados
|
||||
|
||||
## Pasos - Agregar zona
|
||||
|
||||
1. En el mapa o listado, buscar el barrio
|
||||
2. Click en el barrio para seleccionarlo
|
||||
3. El barrio se agrega a mi cobertura
|
||||
4. Guardar cambios
|
||||
|
||||
## Pasos - Quitar zona
|
||||
|
||||
1. En mi listado de zonas
|
||||
2. Click en "X" o deseleccionar el barrio
|
||||
3. Confirmar eliminacion
|
||||
4. Guardar cambios
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Solo recibo solicitudes de las zonas que seleccione
|
||||
- Si agrego zona, empiezo a ver solicitudes pendientes de esa zona
|
||||
- Si quito zona, dejo de ver nuevas solicitudes de esa zona
|
||||
- Visitas ya aceptadas no se afectan
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Vet quiere zona muy especifica (solo algunas calles)
|
||||
- Zonas con poca demanda, vet no recibe solicitudes
|
||||
- Zonas con mucha demanda, vet saturado
|
||||
- Limites de zona confusos (calle pertenece a 2 barrios)
|
||||
- Vet quiere cobertura condicional (solo ciertos dias)
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Zona nueva no listada: solicitar a ops que la agregue
|
||||
- Cobertura temporal (ej: cubriendo a otro vet)
|
||||
- Cobertura por horario (mañana en zona A, tarde en zona B)
|
||||
- Vet se muda: actualizar todas las zonas
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Aceptar solicitud (solo veo solicitudes de mis zonas)
|
||||
- Ver agenda (optimizar ruta por zonas)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /common/api/v1/neighborhoods/` (lista de barrios)
|
||||
- `GET /mascotas/api/v1/veterinarians/{id}/` (zonas actuales)
|
||||
- `PATCH /mascotas/api/v1/veterinarians/{id}/` (actualizar zonas)
|
||||
- Modelo: Veterinarian tiene M2M con Neighborhood
|
||||
- Coeficiente de distancia por barrio para calcular viabilidad
|
||||
@@ -0,0 +1,81 @@
|
||||
# Ver Historial de Pacientes
|
||||
|
||||
## Tipo de usuario
|
||||
Veterinario
|
||||
|
||||
## Donde empieza
|
||||
Dashboard veterinario -> "Historia clinica" o `/vet/historia-clinica`
|
||||
|
||||
## Que quiere hacer el usuario
|
||||
Buscar y revisar el historial medico de pacientes que ha atendido
|
||||
|
||||
## Pasos - Buscar paciente
|
||||
|
||||
1. Ir a "Historia clinica"
|
||||
2. Usar el buscador:
|
||||
- Por nombre del dueno
|
||||
- Por nombre de la mascota
|
||||
- Por telefono
|
||||
- Por email
|
||||
3. Ver resultados de busqueda
|
||||
4. Click en el paciente deseado
|
||||
|
||||
## Pasos - Ver historial de mascota
|
||||
|
||||
1. Seleccionar la mascota
|
||||
2. Ver ficha de la mascota:
|
||||
- Datos basicos (nombre, tipo, raza, edad)
|
||||
- Foto
|
||||
- Peso historico
|
||||
- Estado de vacunacion
|
||||
3. Ver listado de visitas (todas, no solo las mias)
|
||||
4. Click en una visita para ver el informe completo
|
||||
|
||||
## Pasos - Revisar informe anterior
|
||||
|
||||
1. En el listado de visitas, click en una
|
||||
2. Ver informe completo:
|
||||
- Fecha y veterinario que atendio
|
||||
- Examen fisico
|
||||
- Diagnostico
|
||||
- Tratamiento
|
||||
- Medicamentos indicados
|
||||
- Estudios solicitados
|
||||
- Seguimiento
|
||||
|
||||
## Que deberia pasar
|
||||
|
||||
- Puedo ver historial completo de pacientes que atendi
|
||||
- Puedo ver informes de otros vets (para continuidad de atencion)
|
||||
- Tengo contexto para tomar decisiones clinicas
|
||||
|
||||
## Problemas comunes
|
||||
|
||||
- Busqueda no encuentra al paciente (nombre mal escrito)
|
||||
- Mascota atendida por otro vet, no tengo acceso?
|
||||
- Historial muy extenso, dificil encontrar info relevante
|
||||
- Informes anteriores incompletos o ilegibles
|
||||
- Duplicados de pacientes (mismo dueno registrado 2 veces)
|
||||
|
||||
## Casos especiales
|
||||
|
||||
- Mascota nueva sin historial
|
||||
- Mascota atendida en otra clinica (historial externo)
|
||||
- Dueno tiene multiples mascotas
|
||||
- Mascota transferida a otro dueno
|
||||
- Paciente de urgencia/emergencia sin cita previa
|
||||
|
||||
## Flujos relacionados
|
||||
|
||||
- Realizar visita (consultar historial antes/durante)
|
||||
- Aceptar solicitud (ver si ya lo atendi antes)
|
||||
|
||||
## Notas tecnicas
|
||||
|
||||
- APIs:
|
||||
- `GET /mascotas/api/v1/pet-owners/?search=X`
|
||||
- `GET /mascotas/api/v1/pets/?pet_owner=X`
|
||||
- `GET /mascotas/api/v1/vet-visits/?pet=X`
|
||||
- `GET /mascotas/api/v1/vetvisitreport/?vet_visit=X`
|
||||
- Permisos: vet puede ver historial de cualquier paciente que haya atendido
|
||||
- Busqueda: usar indice de texto completo para rendimiento
|
||||
143
atlas/book/feature-form-samples/index.html
Normal file
143
atlas/book/feature-form-samples/index.html
Normal file
@@ -0,0 +1,143 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ops Templates Sample - Album</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: #f8fafc;
|
||||
color: #1e293b;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container { max-width: 1000px; margin: 0 auto; padding: 2rem 1rem; }
|
||||
header { margin-bottom: 2rem; }
|
||||
.breadcrumb { font-size: 0.9rem; color: #64748b; margin-bottom: 0.5rem; }
|
||||
.breadcrumb a { color: #15803d; text-decoration: none; }
|
||||
.breadcrumb a:hover { text-decoration: underline; }
|
||||
h1 { font-size: 2rem; color: #15803d; }
|
||||
.subtitle { color: #64748b; margin-top: 0.5rem; }
|
||||
.sample-badge {
|
||||
display: inline-block;
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.user-type {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.user-type h2 {
|
||||
font-size: 1.25rem;
|
||||
color: #15803d;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.user-type h2::before {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #22c55e;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.templates-list { list-style: none; }
|
||||
.templates-list li {
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
.templates-list li:last-child { border-bottom: none; }
|
||||
.templates-list a {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0;
|
||||
text-decoration: none;
|
||||
color: #1e293b;
|
||||
}
|
||||
.templates-list a:hover { color: #15803d; }
|
||||
.templates-list .name { font-weight: 500; }
|
||||
.templates-list .arrow { color: #94a3b8; }
|
||||
.info-box {
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-top: 2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.info-box h3 { font-size: 0.9rem; color: #15803d; margin-bottom: 0.5rem; }
|
||||
footer {
|
||||
margin-top: 3rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
footer a { color: #15803d; text-decoration: none; }
|
||||
footer a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="breadcrumb"><a href="/">Album</a> / Ops Templates</div>
|
||||
<h1>Ops Templates Sample</h1>
|
||||
<p class="subtitle">User flow documentation templates - non-technical format for support/ops teams</p>
|
||||
<span class="sample-badge">Sample Data</span>
|
||||
</header>
|
||||
|
||||
<div class="user-type">
|
||||
<h2>Pet Owner</h2>
|
||||
<ul class="templates-list">
|
||||
<li><a href="/book/feature-form-samples/larder/pet-owner/01-registro.md"><span class="name">01. Registro de Usuario</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/pet-owner/02-reservar-turno.md"><span class="name">02. Reservar Turno</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/pet-owner/03-gestion-mascotas.md"><span class="name">03. Gestion de Mascotas</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/pet-owner/04-pago-turno.md"><span class="name">04. Pago de Turno</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/pet-owner/05-historial-medico.md"><span class="name">05. Historial Medico</span><span class="arrow">→</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="user-type">
|
||||
<h2>Veterinarian</h2>
|
||||
<ul class="templates-list">
|
||||
<li><a href="/book/feature-form-samples/larder/veterinarian/01-aceptar-solicitud.md"><span class="name">01. Aceptar/Rechazar Solicitud</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/veterinarian/02-gestion-agenda.md"><span class="name">02. Gestion de Agenda</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/veterinarian/03-realizar-visita.md"><span class="name">03. Realizar Visita</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/veterinarian/04-zonas-cobertura.md"><span class="name">04. Zonas de Cobertura</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/veterinarian/05-historial-pacientes.md"><span class="name">05. Historial de Pacientes</span><span class="arrow">→</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="user-type">
|
||||
<h2>Backoffice</h2>
|
||||
<ul class="templates-list">
|
||||
<li><a href="/book/feature-form-samples/larder/backoffice/01-gestion-solicitudes.md"><span class="name">01. Gestion de Solicitudes</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/backoffice/02-gestion-usuarios.md"><span class="name">02. Gestion de Usuarios</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/backoffice/03-gestion-servicios.md"><span class="name">03. Gestion de Servicios</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/backoffice/04-reembolsos.md"><span class="name">04. Reembolsos</span><span class="arrow">→</span></a></li>
|
||||
<li><a href="/book/feature-form-samples/larder/backoffice/05-reportes.md"><span class="name">05. Reportes</span><span class="arrow">→</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<h3>Template Structure</h3>
|
||||
<p>Each template includes: User type, Entry point, Goal, Steps, Expected result, Common problems, Edge cases, Related flows, Technical notes.</p>
|
||||
<p style="margin-top: 0.5rem;">These templates transform into <a href="/book/gherkin-samples/">Gherkin specs</a> for testing.</p>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<a href="/">← Album</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
91
atlas/book/feature-form-samples/template/feature-form.md
Normal file
91
atlas/book/feature-form-samples/template/feature-form.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Plantilla: Documentacion de Flujos
|
||||
|
||||
## Para: Equipo de Soporte/Operaciones
|
||||
|
||||
Usa esta plantilla para documentar cualquier accion/flujo del sistema.
|
||||
|
||||
---
|
||||
|
||||
## Plantilla
|
||||
|
||||
```
|
||||
### [Nombre del Flujo]
|
||||
|
||||
**Tipo de usuario:** [Dueno de mascota / Veterinario / Admin]
|
||||
|
||||
**Donde empieza:** [Que pagina/boton/link]
|
||||
|
||||
**Que quiere hacer el usuario:** [Objetivo en una oracion]
|
||||
|
||||
**Pasos:**
|
||||
1. [Primera cosa que hace el usuario]
|
||||
2. [Segunda cosa que hace el usuario]
|
||||
3. [etc.]
|
||||
|
||||
**Que deberia pasar:** [Resultado esperado cuando todo funciona]
|
||||
|
||||
**Problemas comunes:**
|
||||
- [Problema 1]
|
||||
- [Problema 2]
|
||||
|
||||
**Casos especiales:**
|
||||
- [Caso especial 1]
|
||||
- [Caso especial 2]
|
||||
|
||||
**Flujos relacionados:** [Otros flujos que se conectan con este]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ejemplo Completo
|
||||
|
||||
### Reservar turno de vacunacion
|
||||
|
||||
**Tipo de usuario:** Dueno de mascota
|
||||
|
||||
**Donde empieza:** Pagina principal -> Boton "Agendar visita"
|
||||
|
||||
**Que quiere hacer el usuario:** Reservar un turno de vacunacion para su gato
|
||||
|
||||
**Pasos:**
|
||||
1. Click en "Agendar visita" en la pagina principal
|
||||
2. Ingresar direccion en el popup
|
||||
3. Click en "Siguiente"
|
||||
4. Completar datos mascota: nombre, tipo (gato), edad
|
||||
5. Seleccionar categoria "Vacunacion"
|
||||
6. Elegir la vacuna especifica
|
||||
7. Ver que "Consulta clinica" se agrega automaticamente
|
||||
8. Elegir fechas preferidas
|
||||
9. Ingresar datos de contacto (nombre, telefono, email)
|
||||
10. Enviar la solicitud
|
||||
|
||||
**Que deberia pasar:**
|
||||
- Ver mensaje de confirmacion
|
||||
- Recibir email de confirmacion
|
||||
- La solicitud aparece en "Mis turnos" con estado "Pendiente"
|
||||
|
||||
**Problemas comunes:**
|
||||
- Los usuarios intentan quitar "Consulta clinica" y no pueden
|
||||
- El autocompletado de direcciones a veces no encuentra su calle
|
||||
- Algunos usuarios no ven el link "Ya soy cliente" y crean cuentas duplicadas
|
||||
|
||||
**Casos especiales:**
|
||||
- Si la mascota ya esta castrada, el servicio de castracion no deberia aparecer
|
||||
- Si el usuario abandona a mitad del flujo, sus datos se guardan como "invitado"
|
||||
- Algunos barrios no tienen cobertura - deberia mostrar un error claro
|
||||
|
||||
**Flujos relacionados:**
|
||||
- "Ver mis turnos" (para ver el estado despues de reservar)
|
||||
- "Pagar turno" (cuando se solicita el pago)
|
||||
|
||||
---
|
||||
|
||||
## Preguntas a Considerar
|
||||
|
||||
1. **Camino feliz:** Que pasa cuando todo funciona perfecto?
|
||||
2. **Validacion:** Que errores puede ver el usuario?
|
||||
3. **Permisos:** Quien puede hacer esto?
|
||||
4. **Estados:** Esta accion cambia segun algun estado?
|
||||
5. **Dependencias:** Esto requiere que algo mas haya pasado antes?
|
||||
6. **Efectos secundarios:** Esto dispara emails, notificaciones?
|
||||
7. **Deshacer:** El usuario puede revertir esta accion?
|
||||
114
atlas/book/gherkin-samples/CLAUDE.md
Normal file
114
atlas/book/gherkin-samples/CLAUDE.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Gherkin Sample Book
|
||||
|
||||
## Purpose
|
||||
**SAMPLE DATA** - Example BDD/Gherkin `.feature` files for demonstration and testing purposes. These show realistic examples of how feature files will look when actual ops templates are defined.
|
||||
|
||||
Use these to:
|
||||
- Understand Gherkin structure and patterns
|
||||
- Compare Spanish vs English keywords
|
||||
- Test tooling (behave, pytest-bdd, playwright-bdd)
|
||||
- Demo the feature-flow pipeline
|
||||
|
||||
Real feature files will be derived from validated ops templates.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
gherkin-sample/
|
||||
├── es/ # Spanish keywords (Dado/Cuando/Entonces)
|
||||
│ ├── pet-owner/
|
||||
│ ├── veterinarian/
|
||||
│ └── backoffice/
|
||||
└── en/ # English keywords (Given/When/Then)
|
||||
├── pet-owner/
|
||||
├── veterinarian/
|
||||
└── backoffice/
|
||||
```
|
||||
|
||||
## Language Versions
|
||||
|
||||
**Spanish Keywords (`es/`)**
|
||||
- Uses `# language: es` directive
|
||||
- Keywords: Característica, Escenario, Dado, Cuando, Entonces, Y, Pero
|
||||
- Keywords: Esquema del escenario, Ejemplos, Antecedentes, Regla
|
||||
- For teams preferring Spanish BDD syntax
|
||||
|
||||
**English Keywords (`en/`)**
|
||||
- Standard Gherkin syntax
|
||||
- Keywords: Feature, Scenario, Given, When, Then, And, But
|
||||
- Keywords: Scenario Outline, Examples, Background, Rule
|
||||
- Same Spanish content, different keywords
|
||||
|
||||
## Feature Files
|
||||
|
||||
### Pet Owner (5 files)
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| 01-registro.feature | User registration flow |
|
||||
| 02-reservar-turno.feature | Book appointment (turnero) |
|
||||
| 03-gestion-mascotas.feature | Pet management |
|
||||
| 04-pago-turno.feature | Payment flow |
|
||||
| 05-historial-medico.feature | Medical history |
|
||||
|
||||
### Veterinarian (5 files)
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| 01-aceptar-solicitud.feature | Accept/reject requests |
|
||||
| 02-gestion-agenda.feature | Schedule management |
|
||||
| 03-realizar-visita.feature | Conduct visit & report |
|
||||
| 04-zonas-cobertura.feature | Coverage area management |
|
||||
| 05-historial-pacientes.feature | Patient history |
|
||||
|
||||
### Backoffice (5 files)
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| 01-gestion-solicitudes.feature | Request management |
|
||||
| 02-gestion-usuarios.feature | User management |
|
||||
| 03-gestion-servicios.feature | Services & pricing |
|
||||
| 04-reembolsos.feature | Refund process |
|
||||
| 05-reportes.feature | Reports & dashboard |
|
||||
|
||||
## Gherkin Patterns Used
|
||||
|
||||
### Basic
|
||||
- Feature, Scenario, Given/When/Then
|
||||
|
||||
### Intermediate
|
||||
- **Background**: Shared setup for all scenarios
|
||||
- **Scenario Outline + Examples**: Test variations without duplication
|
||||
- **Data Tables**: Multiple items in one step
|
||||
|
||||
### Advanced
|
||||
- **Rule**: Group related scenarios
|
||||
- **Doc Strings**: Large text blocks (diagnoses, reports)
|
||||
- **Tags**: For filtering (@smoke, @critical, @wip)
|
||||
|
||||
## Comment Headers
|
||||
|
||||
Each file includes metadata comments:
|
||||
```gherkin
|
||||
# Fuente: album/book/ops-templates/...
|
||||
# Drive: [Google Drive reference]
|
||||
# Tests Backend: pytest tests/contracts/...
|
||||
# Tests Frontend: npx playwright test ...
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
To run/validate these files:
|
||||
|
||||
**Python**
|
||||
- behave
|
||||
- pytest-bdd
|
||||
|
||||
**JavaScript/TypeScript**
|
||||
- playwright-bdd
|
||||
- jest-cucumber
|
||||
|
||||
**IDE Support**
|
||||
- VS Code: Cucumber (Gherkin) Full Support extension
|
||||
- JetBrains: Built-in Gherkin plugin
|
||||
|
||||
## Source Templates
|
||||
|
||||
Sample ops templates at: `album/book/ops-templates-sample/`
|
||||
161
atlas/book/gherkin-samples/detail.html
Normal file
161
atlas/book/gherkin-samples/detail.html
Normal file
@@ -0,0 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ filename }} - Gherkin</title>
|
||||
<link rel="stylesheet" href="/static/prism/prism-tomorrow.min.css">
|
||||
<link rel="stylesheet" href="/static/prism/prism-line-numbers.min.css">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container { max-width: 900px; margin: 0 auto; padding: 2rem 1rem; }
|
||||
header { margin-bottom: 2rem; }
|
||||
.breadcrumb { font-size: 0.9rem; color: #64748b; margin-bottom: 0.5rem; }
|
||||
.breadcrumb a { color: #818cf8; text-decoration: none; }
|
||||
.breadcrumb a:hover { text-decoration: underline; }
|
||||
h1 { font-size: 1.5rem; color: #e2e8f0; }
|
||||
.meta {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.meta span {
|
||||
background: #1e293b;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
.meta .lang-es { border-left: 3px solid #f59e0b; }
|
||||
.meta .lang-en { border-left: 3px solid #3b82f6; }
|
||||
.meta .sample { background: #422006; color: #fbbf24; }
|
||||
.content {
|
||||
background: #1e293b;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content-header {
|
||||
background: #334155;
|
||||
padding: 0.75rem 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.content-header .filename { color: #94a3b8; }
|
||||
.content-header .copy-btn {
|
||||
background: #475569;
|
||||
border: none;
|
||||
color: #e2e8f0;
|
||||
padding: 0.35rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.content-header .copy-btn:hover { background: #64748b; }
|
||||
pre[class*="language-"] {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
code[class*="language-"] {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
}
|
||||
.sidebar {
|
||||
margin-top: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.sidebar-box {
|
||||
background: #1e293b;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
.sidebar-box h3 { font-size: 0.8rem; color: #64748b; text-transform: uppercase; margin-bottom: 0.75rem; }
|
||||
.sidebar-box a {
|
||||
display: block;
|
||||
color: #818cf8;
|
||||
text-decoration: none;
|
||||
padding: 0.35rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.sidebar-box a:hover { text-decoration: underline; }
|
||||
footer {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #334155;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
footer a { color: #818cf8; text-decoration: none; }
|
||||
footer a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="breadcrumb">
|
||||
<a href="/">Album</a> / <a href="/book/gherkin/">Gherkin</a> / {{ lang }} / {{ user_type }}
|
||||
</div>
|
||||
<h1>{{ filename.replace('.feature', '').replace('-', ' ').title() }}</h1>
|
||||
<div class="meta">
|
||||
<span class="lang-{{ lang }}">{{ 'Espanol' if lang == 'es' else 'English' }}</span>
|
||||
<span>{{ user_type }}</span>
|
||||
<span class="sample">Sample</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<div class="content-header">
|
||||
<span class="filename">{{ filename }}</span>
|
||||
<button class="copy-btn" onclick="copyContent()">Copy</button>
|
||||
</div>
|
||||
<pre class="line-numbers"><code id="gherkin-content" class="language-gherkin">{{ content }}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-box">
|
||||
<h3>Source</h3>
|
||||
<a href="/book/ops-templates/{{ user_type }}/{{ filename.replace('.feature', '.md') }}">Ops Template</a>
|
||||
</div>
|
||||
<div class="sidebar-box">
|
||||
<h3>Other Language</h3>
|
||||
{% if lang == 'es' %}
|
||||
<a href="/book/gherkin/en/{{ user_type }}/{{ filename }}">English Version</a>
|
||||
{% else %}
|
||||
<a href="/book/gherkin/es/{{ user_type }}/{{ filename }}">Spanish Version</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="sidebar-box">
|
||||
<h3>Pipeline</h3>
|
||||
<a href="/book/feature-flow/">Feature Flow</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<a href="/book/gherkin/">← All Gherkin Files</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="/static/prism/prism.min.js"></script>
|
||||
<script src="/static/prism/prism-gherkin.min.js"></script>
|
||||
<script src="/static/prism/prism-line-numbers.min.js"></script>
|
||||
<script>
|
||||
function copyContent() {
|
||||
const content = document.getElementById('gherkin-content').textContent;
|
||||
navigator.clipboard.writeText(content);
|
||||
const btn = document.querySelector('.copy-btn');
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => btn.textContent = 'Copy', 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,104 @@
|
||||
# Fuente: album/book/ops-templates/backoffice/01-gestion-solicitudes.md
|
||||
# Drive: 05. ATC - Operaciones/Procedimientos
|
||||
# Tests Backend: pytest tests/contracts/solicitudes/test_service_requests.py
|
||||
# Tests Frontend: npx playwright test admin-requests.spec.ts
|
||||
|
||||
Feature: Gestion de solicitudes de servicio
|
||||
|
||||
Como administrador del backoffice
|
||||
Quiero gestionar todas las solicitudes del sistema
|
||||
Para asegurar que se atiendan correctamente
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como administrador
|
||||
And estoy en la seccion "Solicitudes" del backoffice
|
||||
|
||||
# ============================================
|
||||
# VER Y FILTRAR SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver listado de todas las solicitudes
|
||||
When cargo la pagina de solicitudes
|
||||
Then deberia ver un listado paginado
|
||||
And cada solicitud deberia mostrar:
|
||||
| campo |
|
||||
| ID |
|
||||
| Dueno |
|
||||
| Mascota |
|
||||
| Servicios |
|
||||
| Estado |
|
||||
| Fecha |
|
||||
|
||||
Scenario Outline: Filtrar solicitudes por estado
|
||||
When filtro por estado "<estado>"
|
||||
Then solo deberia ver solicitudes con estado "<estado>"
|
||||
|
||||
Examples:
|
||||
| estado |
|
||||
| Pendiente |
|
||||
| Coordinado |
|
||||
| Pagado |
|
||||
| Completado |
|
||||
| Cancelado |
|
||||
|
||||
# ============================================
|
||||
# ASIGNAR VETERINARIO MANUALMENTE
|
||||
# ============================================
|
||||
|
||||
Scenario: Asignar veterinario a solicitud pendiente
|
||||
Given que hay una solicitud pendiente en "Palermo"
|
||||
And no fue aceptada por ningun veterinario
|
||||
When abro el detalle de la solicitud
|
||||
And hago click en "Asignar veterinario"
|
||||
Then deberia ver lista de veterinarios con cobertura en Palermo
|
||||
|
||||
When selecciono "Dra. Garcia"
|
||||
And selecciono fecha "15 de enero" hora "10:00"
|
||||
And confirmo la asignacion
|
||||
Then la solicitud deberia pasar a estado "Coordinado"
|
||||
And deberia estar asignada a Dra. Garcia
|
||||
And el dueno deberia recibir notificacion
|
||||
And el veterinario deberia recibir notificacion
|
||||
|
||||
# ============================================
|
||||
# CAMBIAR ESTADO MANUALMENTE
|
||||
# ============================================
|
||||
|
||||
Scenario: Cambiar estado de solicitud
|
||||
Given que hay una solicitud en estado "Coordinado"
|
||||
And el pago se proceso pero el webhook fallo
|
||||
When abro la solicitud
|
||||
And hago click en "Cambiar estado"
|
||||
And selecciono "Pagado"
|
||||
And ingreso motivo "Pago confirmado manualmente - ID MP: 12345"
|
||||
And confirmo el cambio
|
||||
Then la solicitud deberia pasar a "Pagado"
|
||||
And el cambio deberia registrarse en el historial
|
||||
|
||||
Rule: Solo ciertos cambios de estado son validos
|
||||
|
||||
Scenario: No puedo volver a estado anterior
|
||||
Given que hay una solicitud en estado "Completado"
|
||||
When intento cambiar el estado a "Pagado"
|
||||
Then deberia ver error "No se puede volver a un estado anterior"
|
||||
|
||||
Scenario: Puedo cancelar desde cualquier estado
|
||||
Given que hay una solicitud en estado "Coordinado"
|
||||
When cambio el estado a "Cancelado"
|
||||
And ingreso motivo de cancelacion
|
||||
Then la solicitud deberia cancelarse
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Reasignar veterinario
|
||||
Given que hay una solicitud asignada a "Dr. Lopez"
|
||||
And Dr. Lopez no puede asistir
|
||||
When abro la solicitud
|
||||
And hago click en "Reasignar"
|
||||
And selecciono otro veterinario
|
||||
And confirmo
|
||||
Then Dr. Lopez deberia ser notificado de la desasignacion
|
||||
And el nuevo vet deberia ser notificado
|
||||
And el dueno deberia ser notificado del cambio
|
||||
@@ -0,0 +1,88 @@
|
||||
# Fuente: album/book/ops-templates/backoffice/02-gestion-usuarios.md
|
||||
# Drive: 05. ATC - Operaciones/Procedimientos
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_pet_owners.py
|
||||
# Tests Frontend: npx playwright test admin-users.spec.ts
|
||||
|
||||
Feature: Gestion de usuarios
|
||||
|
||||
Como administrador del backoffice
|
||||
Quiero gestionar duenos y veterinarios
|
||||
Para mantener la base de usuarios actualizada
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como administrador
|
||||
And estoy en el backoffice
|
||||
|
||||
# ============================================
|
||||
# GESTION DE DUENOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Buscar dueno de mascota
|
||||
Given que estoy en la seccion "Duenos"
|
||||
When busco "maria@ejemplo.com"
|
||||
Then deberia ver a Maria Garcia en los resultados
|
||||
And deberia ver sus mascotas listadas
|
||||
|
||||
Scenario: Ver perfil completo de dueno
|
||||
Given que encontre a "Maria Garcia"
|
||||
When hago click en su perfil
|
||||
Then deberia ver:
|
||||
| seccion | contenido |
|
||||
| Datos personales | Nombre, email, telefono |
|
||||
| Direcciones | Direcciones registradas |
|
||||
| Mascotas | Lista de mascotas |
|
||||
| Historial | Solicitudes anteriores |
|
||||
|
||||
Scenario: Editar datos de dueno
|
||||
Given que estoy viendo el perfil de un dueno
|
||||
When hago click en "Editar"
|
||||
And cambio el telefono a "1155559999"
|
||||
And guardo los cambios
|
||||
Then el telefono deberia actualizarse
|
||||
|
||||
# ============================================
|
||||
# GESTION DE VETERINARIOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Agregar nuevo veterinario
|
||||
Given que estoy en la seccion "Veterinarios"
|
||||
When hago click en "Agregar veterinario"
|
||||
And completo los datos:
|
||||
| campo | valor |
|
||||
| Nombre | Dr. Juan Perez |
|
||||
| Matricula | MV-12345 |
|
||||
| Email | jperez@ejemplo.com |
|
||||
| Telefono | 1155551234 |
|
||||
And selecciono zonas de cobertura:
|
||||
| zona |
|
||||
| Palermo |
|
||||
| Recoleta |
|
||||
And genero credenciales de acceso
|
||||
And guardo
|
||||
Then deberia crearse el veterinario
|
||||
And deberia poder loguearse con sus credenciales
|
||||
|
||||
Scenario: Intentar desactivar veterinario con citas pendientes
|
||||
Given que el veterinario "Dra. Garcia" tiene 3 citas pendientes
|
||||
When intento desactivarla
|
||||
Then deberia ver advertencia "Tiene 3 citas pendientes"
|
||||
And deberia ver opciones:
|
||||
| opcion |
|
||||
| Reasignar citas y desactivar |
|
||||
| Cancelar |
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Fusionar cuentas duplicadas
|
||||
Given que hay dos cuentas para el mismo dueno:
|
||||
| cuenta | email | mascotas |
|
||||
| Cuenta1 | maria@gmail.com | Luna |
|
||||
| Cuenta2 | maria@hotmail.com | Rocky |
|
||||
When selecciono ambas cuentas
|
||||
And hago click en "Fusionar"
|
||||
And elijo Cuenta1 como principal
|
||||
And confirmo
|
||||
Then deberia existir solo Cuenta1
|
||||
And deberia tener ambas mascotas
|
||||
@@ -0,0 +1,91 @@
|
||||
# Fuente: album/book/ops-templates/backoffice/03-gestion-servicios.md
|
||||
# Drive: 08. IT y Producto/Catalogo Servicios
|
||||
# Tests Backend: pytest tests/contracts/productos/test_services.py
|
||||
# Tests Frontend: npx playwright test admin-services.spec.ts
|
||||
|
||||
Feature: Gestion de servicios y precios
|
||||
|
||||
Como administrador
|
||||
Quiero gestionar el catalogo de servicios
|
||||
Para mantener la oferta actualizada
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como administrador
|
||||
And estoy en la seccion "Servicios"
|
||||
|
||||
# ============================================
|
||||
# VER CATALOGO DE SERVICIOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver listado de servicios
|
||||
When cargo la pagina de servicios
|
||||
Then deberia ver todos los servicios organizados por categoria
|
||||
And cada servicio deberia mostrar:
|
||||
| campo |
|
||||
| Nombre |
|
||||
| Categoria |
|
||||
| Tipo mascota |
|
||||
| Precio actual |
|
||||
| Estado |
|
||||
|
||||
# ============================================
|
||||
# CREAR Y EDITAR SERVICIOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Agregar nuevo servicio
|
||||
When hago click en "Agregar servicio"
|
||||
And completo los datos:
|
||||
| campo | valor |
|
||||
| Nombre | Vacuna Quintuple Felina |
|
||||
| Descripcion | Protege contra 5 enfermedades |
|
||||
| Categoria | Vacunacion |
|
||||
| Tipo mascota | Gato |
|
||||
| Precio | 15000 |
|
||||
And guardo el servicio
|
||||
Then el servicio deberia crearse
|
||||
And deberia aparecer en el turnero para gatos
|
||||
|
||||
Scenario: Desactivar servicio
|
||||
Given que existe el servicio "Servicio Antiguo"
|
||||
When abro el servicio
|
||||
And hago click en "Desactivar"
|
||||
And confirmo
|
||||
Then el servicio no deberia aparecer en el turnero
|
||||
|
||||
# ============================================
|
||||
# GESTION DE PRECIOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Actualizar precio de servicio
|
||||
Given que "Consulta clinica" tiene precio actual de 10000
|
||||
When abro el servicio
|
||||
And voy a la seccion "Precios"
|
||||
And hago click en "Agregar precio"
|
||||
And ingreso nuevo precio 12000
|
||||
And selecciono fecha de vigencia "01/02/2024"
|
||||
And guardo
|
||||
Then deberia crearse el nuevo precio
|
||||
And el precio anterior deberia quedar en historial
|
||||
|
||||
Rule: El precio se congela al crear la solicitud
|
||||
|
||||
Scenario: Cambio de precio no afecta solicitudes existentes
|
||||
Given que hay una solicitud pendiente con "Consulta clinica" a 10000
|
||||
When cambio el precio de "Consulta clinica" a 12000
|
||||
Then la solicitud deberia mantener el precio de 10000
|
||||
|
||||
# ============================================
|
||||
# CONFIGURAR COMBOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Crear combo de servicios
|
||||
When voy a "Combos"
|
||||
And hago click en "Agregar combo"
|
||||
And configuro:
|
||||
| campo | valor |
|
||||
| Nombre | Plan Preventivo Felino |
|
||||
| Servicios | Vacunacion, Desparasitacion |
|
||||
| Precio combo | 25000 |
|
||||
And guardo
|
||||
Then el combo deberia crearse
|
||||
And deberia aplicarse automaticamente en el turnero
|
||||
@@ -0,0 +1,86 @@
|
||||
# Fuente: album/book/ops-templates/backoffice/04-reembolsos.md
|
||||
# Drive: 07. Finanzas y contabilidad/Reembolsos
|
||||
# Tests Backend: pytest tests/contracts/payments/test_refunds.py
|
||||
# Tests Frontend: npx playwright test admin-refunds.spec.ts
|
||||
|
||||
Feature: Proceso de reembolso
|
||||
|
||||
Como administrador
|
||||
Quiero procesar reembolsos de pagos
|
||||
Para resolver cancelaciones y problemas
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como administrador
|
||||
And estoy en el backoffice
|
||||
|
||||
# ============================================
|
||||
# PROCESAR REEMBOLSO
|
||||
# ============================================
|
||||
|
||||
Scenario: Reembolso total exitoso
|
||||
Given que hay una solicitud pagada por 15000
|
||||
And la visita fue cancelada
|
||||
When abro la solicitud
|
||||
And hago click en "Procesar reembolso"
|
||||
And selecciono "Reembolso total"
|
||||
And ingreso motivo "Cancelacion por indisponibilidad del veterinario"
|
||||
And confirmo el reembolso
|
||||
Then deberia enviarse la solicitud de reembolso a Mercado Pago
|
||||
And deberia ver mensaje "Reembolso en proceso"
|
||||
And el dueno deberia recibir email de confirmacion
|
||||
|
||||
Scenario: Reembolso parcial
|
||||
Given que hay una solicitud pagada por 20000
|
||||
And solo se realizo parte del servicio
|
||||
When proceso reembolso parcial por 10000
|
||||
And ingreso motivo "Servicio parcialmente completado"
|
||||
And confirmo
|
||||
Then deberia procesarse reembolso por 10000
|
||||
And deberia quedar registro del monto reembolsado
|
||||
|
||||
# ============================================
|
||||
# TIEMPOS DE ACREDITACION
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Informar tiempo de acreditacion segun metodo
|
||||
Given que el pago original fue con "<metodo>"
|
||||
When proceso el reembolso
|
||||
Then deberia informar al usuario:
|
||||
"""
|
||||
El reembolso se acreditara en <tiempo>
|
||||
"""
|
||||
|
||||
Examples:
|
||||
| metodo | tiempo |
|
||||
| Tarjeta credito | 1-2 resumenes de cuenta |
|
||||
| Tarjeta debito | 5-10 dias habiles |
|
||||
| Dinero en cuenta MP | forma inmediata |
|
||||
|
||||
# ============================================
|
||||
# VALIDACIONES
|
||||
# ============================================
|
||||
|
||||
Scenario: No puedo reembolsar mas del monto pagado
|
||||
Given que hay una solicitud pagada por 15000
|
||||
When intento reembolsar 20000
|
||||
Then deberia ver error "El monto supera el pago original"
|
||||
|
||||
Scenario: Reembolso duplicado
|
||||
Given que ya procese un reembolso total para una solicitud
|
||||
When intento procesar otro reembolso
|
||||
Then deberia ver error "Esta solicitud ya fue reembolsada"
|
||||
|
||||
Scenario: Pago fuera de plazo de reembolso
|
||||
Given que hay un pago de hace 200 dias
|
||||
When intento reembolsar
|
||||
Then deberia ver advertencia "Fuera de plazo de reembolso de MP (180 dias)"
|
||||
|
||||
# ============================================
|
||||
# SEGUIMIENTO
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver estado del reembolso
|
||||
Given que procese un reembolso hace 2 dias
|
||||
When veo el detalle del reembolso
|
||||
Then deberia ver el estado actual en Mercado Pago
|
||||
And deberia ver historial de estados
|
||||
97
atlas/book/gherkin-samples/en/backoffice/05-reportes.feature
Normal file
97
atlas/book/gherkin-samples/en/backoffice/05-reportes.feature
Normal file
@@ -0,0 +1,97 @@
|
||||
# Fuente: album/book/ops-templates/backoffice/05-reportes.md
|
||||
# Drive: 07. Finanzas y contabilidad/Reportes
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_stats.py
|
||||
# Tests Frontend: npx playwright test admin-reports.spec.ts
|
||||
|
||||
Feature: Reportes y dashboard
|
||||
|
||||
Como administrador o gerente
|
||||
Quiero ver metricas y generar reportes
|
||||
Para tomar decisiones informadas
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como administrador
|
||||
And estoy en el backoffice
|
||||
|
||||
# ============================================
|
||||
# DASHBOARD
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver dashboard principal
|
||||
When accedo al dashboard
|
||||
Then deberia ver metricas resumidas:
|
||||
| metrica | periodo |
|
||||
| Solicitudes nuevas | Hoy |
|
||||
| Visitas completadas | Semana |
|
||||
| Ingresos | Mes |
|
||||
| Veterinarios activos | Actual |
|
||||
| Tasa de conversion | Mes |
|
||||
|
||||
Scenario: Ver grafico de tendencia
|
||||
When veo el grafico de solicitudes
|
||||
Then deberia ver la evolucion de los ultimos 30 dias
|
||||
And deberia poder comparar con periodo anterior
|
||||
|
||||
# ============================================
|
||||
# REPORTE DE SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Scenario: Generar reporte de solicitudes
|
||||
When voy a "Reportes" -> "Solicitudes"
|
||||
And selecciono periodo "Enero 2024"
|
||||
And hago click en "Generar"
|
||||
Then deberia ver tabla con solicitudes del periodo
|
||||
And deberia ver totales por estado
|
||||
|
||||
Scenario: Filtrar reporte por multiples criterios
|
||||
When genero reporte con filtros:
|
||||
| filtro | valor |
|
||||
| Periodo | Enero 2024 |
|
||||
| Zona | Palermo |
|
||||
| Veterinario | Dra. Garcia |
|
||||
Then deberia ver solo solicitudes que cumplan todos los criterios
|
||||
|
||||
# ============================================
|
||||
# REPORTE DE INGRESOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver ingresos por periodo
|
||||
When voy a "Reportes" -> "Ingresos"
|
||||
And selecciono "Ultimo trimestre"
|
||||
Then deberia ver:
|
||||
| dato |
|
||||
| Ingresos totales |
|
||||
| Cantidad de pagos |
|
||||
| Ticket promedio |
|
||||
| Reembolsos realizados |
|
||||
| Ingreso neto |
|
||||
|
||||
Scenario: Ingresos agrupados por veterinario
|
||||
When agrupo el reporte por "Veterinario"
|
||||
Then deberia ver para cada vet:
|
||||
| dato |
|
||||
| Visitas completadas |
|
||||
| Ingresos generados |
|
||||
| Porcentaje del total |
|
||||
|
||||
# ============================================
|
||||
# EXPORTACION
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Exportar reporte en diferentes formatos
|
||||
Given que tengo un reporte generado
|
||||
When hago click en "Exportar"
|
||||
And selecciono formato "<formato>"
|
||||
Then deberia descargarse el archivo en formato <formato>
|
||||
|
||||
Examples:
|
||||
| formato |
|
||||
| CSV |
|
||||
| Excel |
|
||||
| PDF |
|
||||
|
||||
Scenario: Exportar reporte grande de forma asincrona
|
||||
Given que genere un reporte con mas de 10000 registros
|
||||
When hago click en "Exportar"
|
||||
Then deberia ver mensaje "Generando exportacion..."
|
||||
And deberia recibir notificacion cuando este listo
|
||||
91
atlas/book/gherkin-samples/en/pet-owner/01-registro.feature
Normal file
91
atlas/book/gherkin-samples/en/pet-owner/01-registro.feature
Normal file
@@ -0,0 +1,91 @@
|
||||
# Fuente: album/book/ops-templates/pet-owner/01-registro.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/common/test_users.py
|
||||
# Tests Frontend: npx playwright test auth.spec.ts
|
||||
|
||||
Feature: Registro de usuario
|
||||
|
||||
Como visitante de la plataforma
|
||||
Quiero poder crear una cuenta
|
||||
Para gestionar mis mascotas y reservar turnos
|
||||
|
||||
# ============================================
|
||||
# CAMINO FELIZ
|
||||
# ============================================
|
||||
|
||||
Scenario: Registro exitoso con datos validos
|
||||
Given que estoy en la pagina de registro
|
||||
When ingreso email "nuevo@ejemplo.com"
|
||||
And ingreso contraseña "Password123"
|
||||
And confirmo contraseña "Password123"
|
||||
And acepto los terminos y condiciones
|
||||
And hago click en "Crear cuenta"
|
||||
Then deberia ver mensaje "Te enviamos un email de verificacion"
|
||||
And deberia recibir email de verificacion
|
||||
|
||||
Scenario: Verificar email y activar cuenta
|
||||
Given que me registre con email "nuevo@ejemplo.com"
|
||||
And recibi el email de verificacion
|
||||
When hago click en el link de verificacion
|
||||
Then mi cuenta deberia estar activa
|
||||
And deberia ser redirigido al dashboard
|
||||
|
||||
# ============================================
|
||||
# VALIDACIONES
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Registro con datos invalidos
|
||||
Given que estoy en la pagina de registro
|
||||
When ingreso email "<email>"
|
||||
And ingreso contraseña "<password>"
|
||||
And confirmo contraseña "<confirmacion>"
|
||||
And hago click en "Crear cuenta"
|
||||
Then deberia ver error "<mensaje_error>"
|
||||
|
||||
Examples:
|
||||
| email | password | confirmacion | mensaje_error |
|
||||
| invalido | Password123 | Password123 | Email invalido |
|
||||
| test@test.com | 123 | 123 | Contraseña muy corta |
|
||||
| test@test.com | password | password | Debe contener al menos un numero |
|
||||
| test@test.com | Password123 | Diferente123 | Las contraseñas no coinciden |
|
||||
|
||||
Scenario: Registro con email ya existente
|
||||
Given que existe un usuario con email "existente@ejemplo.com"
|
||||
And estoy en la pagina de registro
|
||||
When ingreso email "existente@ejemplo.com"
|
||||
And completo el resto del formulario correctamente
|
||||
And hago click en "Crear cuenta"
|
||||
Then deberia ver error "Este email ya esta registrado"
|
||||
And deberia ver link "Recuperar contraseña"
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Registro linkea con cuenta invitado existente
|
||||
# Usuario que reservo turno como invitado y ahora quiere registrarse
|
||||
Given que existe un usuario invitado con email "invitado@ejemplo.com"
|
||||
And ese usuario tiene una mascota "Luna" registrada
|
||||
And estoy en la pagina de registro
|
||||
When me registro con email "invitado@ejemplo.com"
|
||||
And verifico mi cuenta
|
||||
Then deberia ver mi mascota "Luna" en el dashboard
|
||||
And deberia ver mis turnos anteriores
|
||||
|
||||
Scenario: Registro desde flujo de turnero
|
||||
# Usuario empezo a reservar turno y decide crear cuenta
|
||||
Given que estoy en el paso final del turnero
|
||||
And ingrese mis datos de contacto
|
||||
When hago click en "Crear cuenta para guardar mis datos"
|
||||
Then deberia ver formulario simplificado
|
||||
And mi email ya deberia estar pre-llenado
|
||||
And solo deberia ingresar contraseña
|
||||
|
||||
Scenario: Reenviar email de verificacion
|
||||
Given que me registre pero no verifique mi cuenta
|
||||
And estoy en la pagina de login
|
||||
When intento iniciar sesion
|
||||
Then deberia ver "Tu cuenta no esta verificada"
|
||||
And deberia ver boton "Reenviar email"
|
||||
When hago click en "Reenviar email"
|
||||
Then deberia recibir nuevo email de verificacion
|
||||
@@ -0,0 +1,140 @@
|
||||
# Fuente: album/book/ops-templates/pet-owner/02-reservar-turno.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/workflows/test_turnero_general.py
|
||||
# Tests Frontend: npx playwright test turnero.spec.ts
|
||||
# Relacionado: def/work_plan/10-flow-turnero.md
|
||||
|
||||
Feature: Reservar turno veterinario (Turnero)
|
||||
|
||||
Como dueno de mascota
|
||||
Quiero reservar un turno veterinario a domicilio
|
||||
Para que atiendan a mi mascota sin salir de casa
|
||||
|
||||
Background:
|
||||
Given que estoy en la pagina del turnero
|
||||
|
||||
# ============================================
|
||||
# VERIFICACION DE COBERTURA
|
||||
# ============================================
|
||||
|
||||
Scenario: Verificar cobertura en zona disponible
|
||||
When ingreso direccion "Av Santa Fe 1234, CABA"
|
||||
Then deberia ver mensaje "Tenemos cobertura en tu zona"
|
||||
And deberia poder continuar al siguiente paso
|
||||
|
||||
Scenario: Zona sin cobertura
|
||||
When ingreso direccion "Calle Principal 100, Ushuaia"
|
||||
Then deberia ver mensaje "Aun no tenemos cobertura en tu zona"
|
||||
And deberia ver formulario "Avisame cuando lleguen"
|
||||
|
||||
# ============================================
|
||||
# FLUJO COMPLETO POR TIPO DE USUARIO
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Reservar turno como <tipo_usuario>
|
||||
Given que soy un usuario <tipo_usuario>
|
||||
And tengo cobertura en mi zona
|
||||
When completo los datos de mi mascota:
|
||||
| campo | valor |
|
||||
| nombre | Luna |
|
||||
| tipo | Gato |
|
||||
| edad | 2 años |
|
||||
| castrada | Si |
|
||||
And selecciono servicios:
|
||||
| servicio |
|
||||
| Vacunacion |
|
||||
And selecciono fechas preferidas:
|
||||
| fecha | franja |
|
||||
| 2024-01-15 | Mañana |
|
||||
| 2024-01-16 | Tarde |
|
||||
And completo datos de contacto con email "<email>"
|
||||
And envio la solicitud
|
||||
Then deberia crearse una solicitud en estado "Pendiente"
|
||||
And el dueno deberia ser <estado_dueno>
|
||||
And deberia recibir email de confirmacion
|
||||
|
||||
Examples:
|
||||
| tipo_usuario | email | estado_dueno |
|
||||
| invitado | nuevo@test.com | creado como invitado |
|
||||
| registrado | user@test.com | mi cuenta existente |
|
||||
| recurrente | conocido@test.com | identificado por email |
|
||||
|
||||
# ============================================
|
||||
# SELECCION DE SERVICIOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Servicios filtrados por tipo de mascota
|
||||
Given que agregue una mascota tipo "Gato"
|
||||
When veo los servicios disponibles
|
||||
Then deberia ver "Vacuna triple felina"
|
||||
And deberia ver "Vacuna antirabica"
|
||||
But no deberia ver "Vacuna sextuple canina"
|
||||
|
||||
Scenario: Consulta clinica se agrega automaticamente con vacunacion
|
||||
Given que estoy seleccionando servicios
|
||||
When selecciono "Vacunacion"
|
||||
Then "Consulta clinica" deberia agregarse automaticamente
|
||||
And deberia ver nota "Incluye revision general"
|
||||
And no deberia poder quitar "Consulta clinica"
|
||||
|
||||
Scenario: Servicios combo con descuento
|
||||
Given que estoy seleccionando servicios
|
||||
When agrego los siguientes servicios:
|
||||
| servicio |
|
||||
| Vacunacion |
|
||||
| Desparasitacion |
|
||||
| Antipulgas |
|
||||
Then deberia ver "Plan preventivo completo"
|
||||
And el total deberia incluir descuento de combo
|
||||
|
||||
Scenario: Castracion no disponible para mascota castrada
|
||||
Given que mi mascota esta marcada como castrada
|
||||
When veo los servicios disponibles
|
||||
Then no deberia ver "Castracion"
|
||||
|
||||
# ============================================
|
||||
# DATOS DE CONTACTO Y CUENTA
|
||||
# ============================================
|
||||
|
||||
Scenario: Pre-llenado de datos para usuario logueado
|
||||
Given que estoy logueado como "maria@ejemplo.com"
|
||||
And tengo registrada mascota "Firulais"
|
||||
When inicio el flujo de turnero
|
||||
Then mi direccion deberia estar pre-llenada
|
||||
And deberia poder seleccionar "Firulais" de mis mascotas
|
||||
And mis datos de contacto ya deberian estar completos
|
||||
|
||||
Scenario: Detectar usuario existente por email
|
||||
Given que soy usuario invitado
|
||||
And existe una cuenta con email "existente@ejemplo.com"
|
||||
When ingreso email "existente@ejemplo.com" en datos de contacto
|
||||
Then deberia ver "Ya tenes cuenta con este email"
|
||||
And deberia ver opciones:
|
||||
| opcion |
|
||||
| Iniciar sesion |
|
||||
| Continuar como invitado |
|
||||
|
||||
# ============================================
|
||||
# EDGE CASES
|
||||
# ============================================
|
||||
|
||||
Scenario: Usuario abandona flujo a mitad
|
||||
Given que complete los datos de mascota
|
||||
And cerre el navegador sin enviar
|
||||
When vuelvo a la pagina del turnero
|
||||
Then deberia poder recuperar mi progreso
|
||||
# Nota: datos guardados en localStorage o session
|
||||
|
||||
Scenario: Multiples mascotas en una solicitud
|
||||
Given que quiero atender a 2 mascotas
|
||||
When agrego mascota "Luna" tipo "Gato"
|
||||
And agrego mascota "Rocky" tipo "Perro"
|
||||
And selecciono servicios para cada una
|
||||
Then deberia crearse una solicitud con 2 mascotas
|
||||
And el precio deberia reflejar ambas
|
||||
|
||||
Scenario: Franja horaria especifica
|
||||
Given que solo puedo por la mañana
|
||||
When selecciono franja "Mañana (9-12hs)"
|
||||
Then la solicitud deberia registrar esa preferencia
|
||||
# Nota: Es preferencia, no garantia
|
||||
@@ -0,0 +1,153 @@
|
||||
# Fuente: album/book/ops-templates/pet-owner/03-gestion-mascotas.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_pets.py
|
||||
# Tests Frontend: npx playwright test pets.spec.ts
|
||||
|
||||
Feature: Gestion de mascotas
|
||||
|
||||
Como dueno de mascota registrado
|
||||
Quiero gestionar la informacion de mis mascotas
|
||||
Para tenerla actualizada y acceder a su historial medico
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como dueno de mascota
|
||||
And estoy en la seccion "Mis mascotas"
|
||||
|
||||
# ============================================
|
||||
# AGREGAR MASCOTA
|
||||
# ============================================
|
||||
|
||||
Scenario: Agregar mascota con datos minimos
|
||||
When hago click en "Agregar mascota"
|
||||
And completo el formulario:
|
||||
| campo | valor |
|
||||
| nombre | Luna |
|
||||
| tipo | Gato |
|
||||
And hago click en "Guardar"
|
||||
Then deberia ver "Luna" en mi lista de mascotas
|
||||
And Luna deberia aparecer disponible en el turnero
|
||||
|
||||
Scenario: Agregar mascota con datos completos
|
||||
When hago click en "Agregar mascota"
|
||||
And completo el formulario:
|
||||
| campo | valor |
|
||||
| nombre | Rocky |
|
||||
| tipo | Perro |
|
||||
| raza | Labrador |
|
||||
| fecha_nacimiento | 2020-03-15 |
|
||||
| peso | 25 |
|
||||
| sexo | Macho |
|
||||
| castrado | Si |
|
||||
And subo una foto de Rocky
|
||||
And hago click en "Guardar"
|
||||
Then deberia ver "Rocky" con su foto en mi lista
|
||||
And deberia ver badge "Castrado"
|
||||
|
||||
Scenario Outline: Validacion de datos de mascota
|
||||
When intento agregar mascota con <campo> igual a "<valor>"
|
||||
Then deberia ver error "<mensaje>"
|
||||
|
||||
Examples:
|
||||
| campo | valor | mensaje |
|
||||
| nombre | | El nombre es obligatorio |
|
||||
| nombre | A | Nombre muy corto |
|
||||
| tipo | | Selecciona el tipo |
|
||||
| peso | -5 | El peso debe ser positivo |
|
||||
| peso | 500 | Peso fuera de rango |
|
||||
|
||||
# ============================================
|
||||
# EDITAR MASCOTA
|
||||
# ============================================
|
||||
|
||||
Scenario: Editar peso de mascota
|
||||
Given que tengo una mascota "Luna" con peso 4kg
|
||||
When edito a Luna
|
||||
And cambio el peso a 5kg
|
||||
And guardo los cambios
|
||||
Then Luna deberia mostrar peso "5 kg"
|
||||
|
||||
Scenario: Marcar mascota como castrada
|
||||
Given que tengo una mascota "Rocky" no castrado
|
||||
When edito a Rocky
|
||||
And marco "Esta castrado"
|
||||
And guardo los cambios
|
||||
Then Rocky deberia mostrar badge "Castrado"
|
||||
And el servicio "Castracion" no deberia aparecer para Rocky en el turnero
|
||||
|
||||
Scenario: Actualizar foto de mascota
|
||||
Given que tengo una mascota "Luna" sin foto
|
||||
When edito a Luna
|
||||
And subo una nueva foto
|
||||
And guardo los cambios
|
||||
Then deberia ver la foto de Luna en su tarjeta
|
||||
|
||||
# ============================================
|
||||
# ELIMINAR MASCOTA
|
||||
# ============================================
|
||||
|
||||
Scenario: Eliminar mascota sin historial
|
||||
Given que tengo una mascota "Nuevo" sin visitas
|
||||
When hago click en "Eliminar" para Nuevo
|
||||
And confirmo la eliminacion
|
||||
Then Nuevo no deberia aparecer en mi lista
|
||||
# Nota: Es soft delete
|
||||
|
||||
Scenario: Eliminar mascota con historial medico
|
||||
Given que tengo una mascota "Luna" con visitas anteriores
|
||||
When hago click en "Eliminar" para Luna
|
||||
Then deberia ver advertencia "Luna tiene historial medico"
|
||||
And deberia ver "El historial se conservara pero no podras verlo"
|
||||
When confirmo la eliminacion
|
||||
Then Luna no deberia aparecer en mi lista
|
||||
|
||||
Scenario: No puedo eliminar mascota con turno pendiente
|
||||
Given que tengo una mascota "Rocky" con turno pendiente
|
||||
When intento eliminar a Rocky
|
||||
Then deberia ver error "Rocky tiene turnos pendientes"
|
||||
And deberia ver sugerencia "Cancela los turnos primero"
|
||||
|
||||
# ============================================
|
||||
# VER HISTORIAL MEDICO
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver historial de visitas de mascota
|
||||
Given que tengo una mascota "Luna" con 3 visitas completadas
|
||||
When hago click en Luna
|
||||
And voy a la seccion "Historial"
|
||||
Then deberia ver 3 visitas listadas
|
||||
And deberian estar ordenadas por fecha descendente
|
||||
|
||||
Scenario: Ver detalle de visita
|
||||
Given que tengo una mascota "Luna" con visitas
|
||||
When veo el historial de Luna
|
||||
And hago click en la primera visita
|
||||
Then deberia ver:
|
||||
| campo |
|
||||
| Fecha |
|
||||
| Veterinario |
|
||||
| Diagnostico |
|
||||
| Tratamiento |
|
||||
| Medicamentos |
|
||||
|
||||
Scenario: Mascota sin historial
|
||||
Given que tengo una mascota "Nuevo" recien agregada
|
||||
When veo el perfil de Nuevo
|
||||
Then la seccion "Historial" deberia estar vacia
|
||||
And deberia ver mensaje "Aun no hay visitas registradas"
|
||||
And deberia ver boton "Reservar primer turno"
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Mascota heredada de cuenta invitado
|
||||
Given que me registre con email "juan@test.com"
|
||||
And previamente reserve turno como invitado para "Firulais"
|
||||
When voy a "Mis mascotas"
|
||||
Then deberia ver "Firulais" en mi lista
|
||||
And deberia ver su historial de visitas previas
|
||||
|
||||
Scenario: Razas filtradas por tipo
|
||||
When agrego una mascota tipo "Gato"
|
||||
Then las razas disponibles deberian ser razas de gato
|
||||
And no deberia ver razas de perro como "Labrador"
|
||||
140
atlas/book/gherkin-samples/en/pet-owner/04-pago-turno.feature
Normal file
140
atlas/book/gherkin-samples/en/pet-owner/04-pago-turno.feature
Normal file
@@ -0,0 +1,140 @@
|
||||
# Fuente: album/book/ops-templates/pet-owner/04-pago-turno.md
|
||||
# Drive: 07. Finanzas y contabilidad/Mercado Pago
|
||||
# Tests Backend: pytest tests/contracts/payments/test_mercadopago.py
|
||||
# Tests Frontend: npx playwright test payment.spec.ts
|
||||
|
||||
Feature: Pago de turno
|
||||
|
||||
Como dueno de mascota con turno coordinado
|
||||
Quiero pagar mi turno online
|
||||
Para confirmar la visita del veterinario
|
||||
|
||||
Background:
|
||||
Given que tengo un turno en estado "Coordinado"
|
||||
And el turno tiene asignado veterinario "Dra. Garcia"
|
||||
And la fecha asignada es "15 de enero a las 10:00"
|
||||
|
||||
# ============================================
|
||||
# FLUJO DE PAGO EXITOSO
|
||||
# ============================================
|
||||
|
||||
Scenario: Pagar turno con tarjeta de credito
|
||||
Given que estoy en el detalle de mi turno coordinado
|
||||
When hago click en "Pagar"
|
||||
Then deberia ser redirigido a Mercado Pago
|
||||
And deberia ver el monto correcto
|
||||
|
||||
When selecciono "Tarjeta de credito"
|
||||
And completo los datos de la tarjeta
|
||||
And confirmo el pago
|
||||
Then deberia volver a la plataforma
|
||||
And deberia ver "Pago exitoso"
|
||||
And el turno deberia estar en estado "Pagado"
|
||||
|
||||
Scenario: Recibir confirmacion de pago
|
||||
Given que complete el pago exitosamente
|
||||
Then deberia recibir email de confirmacion
|
||||
And el email deberia contener:
|
||||
| campo |
|
||||
| Fecha del turno |
|
||||
| Direccion |
|
||||
| Veterinario asignado |
|
||||
| Monto pagado |
|
||||
| Numero de operacion |
|
||||
|
||||
# ============================================
|
||||
# METODOS DE PAGO
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Pagar con diferentes metodos
|
||||
Given que estoy en Mercado Pago
|
||||
When selecciono metodo "<metodo>"
|
||||
And completo el pago
|
||||
Then el pago deberia ser <resultado>
|
||||
And el estado de acreditacion deberia ser "<acreditacion>"
|
||||
|
||||
Examples:
|
||||
| metodo | resultado | acreditacion |
|
||||
| Tarjeta credito | exitoso | inmediata |
|
||||
| Tarjeta debito | exitoso | inmediata |
|
||||
| Dinero en cuenta | exitoso | inmediata |
|
||||
| Transferencia | pendiente | 1-2 dias |
|
||||
| Rapipago | pendiente | hasta 24hs |
|
||||
|
||||
# ============================================
|
||||
# MANEJO DE ERRORES
|
||||
# ============================================
|
||||
|
||||
Scenario: Pago rechazado por fondos insuficientes
|
||||
Given que estoy en Mercado Pago
|
||||
When intento pagar con tarjeta sin fondos
|
||||
Then deberia ver error "Fondos insuficientes"
|
||||
And deberia poder reintentar con otra tarjeta
|
||||
And el turno deberia seguir en estado "Coordinado"
|
||||
|
||||
Scenario: Usuario cancela el pago
|
||||
Given que estoy en Mercado Pago
|
||||
When hago click en "Volver al sitio"
|
||||
Then deberia volver a la plataforma
|
||||
And deberia ver mensaje "El pago fue cancelado"
|
||||
And deberia ver boton "Reintentar pago"
|
||||
And el turno deberia seguir en estado "Coordinado"
|
||||
|
||||
Scenario: Cierre de browser durante pago
|
||||
Given que estoy en Mercado Pago
|
||||
And cierro el navegador accidentalmente
|
||||
When vuelvo a la plataforma
|
||||
And voy a "Mis turnos"
|
||||
Then deberia poder ver el estado real del pago
|
||||
# Si se proceso: Pagado. Si no: Coordinado con opcion de pagar
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Link de pago expirado
|
||||
Given que recibi el link de pago hace mas de 24 horas
|
||||
When hago click en el link
|
||||
Then deberia ver "Este link ha expirado"
|
||||
And deberia ver "Contacta a soporte para generar uno nuevo"
|
||||
|
||||
Scenario: Intento pagar turno ya pagado
|
||||
Given que mi turno ya esta en estado "Pagado"
|
||||
When accedo al link de pago
|
||||
Then deberia ver "Este turno ya fue pagado"
|
||||
And deberia ver boton "Ver detalle del turno"
|
||||
|
||||
Scenario: Precio cambio desde la coordinacion
|
||||
# Caso muy raro pero posible
|
||||
Given que el precio del servicio aumento desde que se coordino
|
||||
When voy a pagar
|
||||
Then deberia ver el precio original acordado
|
||||
# El precio se congela al momento de coordinacion
|
||||
|
||||
# ============================================
|
||||
# INTEGRACION CON WEBHOOK
|
||||
# ============================================
|
||||
|
||||
Rule: El estado del turno se actualiza via webhook de Mercado Pago
|
||||
|
||||
Scenario: Webhook confirma pago aprobado
|
||||
Given que el usuario completo el pago en Mercado Pago
|
||||
When Mercado Pago envia webhook con status "approved"
|
||||
Then el turno deberia cambiar a estado "Pagado"
|
||||
And el veterinario deberia recibir notificacion
|
||||
And el usuario deberia recibir email de confirmacion
|
||||
|
||||
Scenario: Webhook informa pago pendiente
|
||||
Given que el usuario pago con transferencia bancaria
|
||||
When Mercado Pago envia webhook con status "pending"
|
||||
Then el turno deberia quedarse en "Coordinado"
|
||||
And deberia registrarse el pago pendiente
|
||||
And el usuario deberia recibir email "Esperando acreditacion"
|
||||
|
||||
Scenario: Webhook falla pero pago se proceso
|
||||
# Caso de error que requiere intervencion manual
|
||||
Given que el usuario pago exitosamente
|
||||
But el webhook fallo por error de red
|
||||
Then el turno seguira en "Coordinado"
|
||||
And el equipo de ops deberia recibir alerta
|
||||
And deberian poder actualizar manualmente
|
||||
@@ -0,0 +1,147 @@
|
||||
# Fuente: album/book/ops-templates/pet-owner/05-historial-medico.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_visits.py
|
||||
# Tests Frontend: npx playwright test medical-history.spec.ts
|
||||
|
||||
Feature: Ver historial medico
|
||||
|
||||
Como dueno de mascota
|
||||
Quiero ver el historial medico de mis mascotas
|
||||
Para tener registro de sus visitas y tratamientos
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como dueno de mascota
|
||||
And tengo una mascota "Luna" con visitas completadas
|
||||
|
||||
# ============================================
|
||||
# ACCESO AL HISTORIAL
|
||||
# ============================================
|
||||
|
||||
Scenario: Acceder al historial desde mascotas
|
||||
Given que estoy en "Mis mascotas"
|
||||
When hago click en "Luna"
|
||||
And voy a la seccion "Historial de visitas"
|
||||
Then deberia ver lista de visitas de Luna
|
||||
|
||||
Scenario: Acceder al historial desde turnos
|
||||
Given que estoy en "Mis turnos"
|
||||
And filtro por "Completados"
|
||||
When hago click en un turno de Luna
|
||||
And hago click en "Ver informe medico"
|
||||
Then deberia ver el informe de esa visita
|
||||
|
||||
# ============================================
|
||||
# VISUALIZACION DE VISITAS
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver lista de visitas ordenadas
|
||||
Given que Luna tiene visitas en las siguientes fechas:
|
||||
| fecha |
|
||||
| 2024-01-15 |
|
||||
| 2023-11-20 |
|
||||
| 2023-06-10 |
|
||||
When veo el historial de Luna
|
||||
Then deberia ver las visitas ordenadas de mas reciente a mas antigua
|
||||
And la primera deberia ser del "15 de enero 2024"
|
||||
|
||||
Scenario: Ver detalle completo de informe
|
||||
When hago click en una visita completada
|
||||
Then deberia ver los siguientes datos:
|
||||
| seccion | contenido |
|
||||
| Informacion | Fecha, veterinario, servicios |
|
||||
| Examen fisico | Peso, temperatura, FC, obs |
|
||||
| Diagnostico | Descripcion del diagnostico |
|
||||
| Tratamiento | Plan de tratamiento |
|
||||
| Medicamentos | Lista con dosis y frecuencia |
|
||||
| Estudios | Estudios solicitados y estado |
|
||||
| Seguimiento | Recomendaciones, proximo control |
|
||||
|
||||
Scenario: Ver informe con medicamentos recetados
|
||||
Given que la visita incluye medicamentos:
|
||||
"""
|
||||
1. Amoxicilina 250mg - 1 comprimido cada 12hs por 7 dias
|
||||
2. Meloxicam 1.5mg - 1 comprimido por dia por 3 dias
|
||||
"""
|
||||
When veo el informe
|
||||
Then deberia ver la lista de medicamentos
|
||||
And cada medicamento deberia mostrar:
|
||||
| campo |
|
||||
| Nombre |
|
||||
| Dosis |
|
||||
| Frecuencia |
|
||||
| Duracion |
|
||||
|
||||
# ============================================
|
||||
# DESCARGA Y COMPARTIR
|
||||
# ============================================
|
||||
|
||||
Scenario: Descargar informe como PDF
|
||||
Given que estoy viendo un informe medico
|
||||
When hago click en "Descargar PDF"
|
||||
Then deberia descargarse un archivo PDF
|
||||
And el PDF deberia contener toda la informacion del informe
|
||||
And deberia tener el logo de Amar Mascotas
|
||||
|
||||
Scenario: Imprimir informe
|
||||
Given que estoy viendo un informe medico
|
||||
When hago click en "Imprimir"
|
||||
Then deberia abrirse el dialogo de impresion
|
||||
And el formato deberia ser optimizado para impresion
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Visita completada sin informe cargado
|
||||
# El vet no cargo el informe todavia
|
||||
Given que Luna tiene una visita marcada como "Completada"
|
||||
But el veterinario no cargo el informe aun
|
||||
When intento ver el informe
|
||||
Then deberia ver mensaje "Informe pendiente"
|
||||
And deberia ver "El veterinario esta completando el informe"
|
||||
|
||||
Scenario: Informe con estudios pendientes
|
||||
Given que el informe solicita estudios de laboratorio
|
||||
And los resultados aun no estan disponibles
|
||||
When veo el informe
|
||||
Then deberia ver seccion "Estudios solicitados"
|
||||
And deberia ver estado "Pendiente de resultados"
|
||||
And deberia ver nota "Se notificara cuando esten listos"
|
||||
|
||||
Scenario: Mascota con multiples visitas el mismo dia
|
||||
# Raro pero posible: consulta de urgencia + seguimiento
|
||||
Given que Luna tuvo 2 visitas el 15 de enero
|
||||
When veo el historial
|
||||
Then deberia ver ambas visitas listadas
|
||||
And deberian estar diferenciadas por hora
|
||||
|
||||
# ============================================
|
||||
# PERMISOS Y PRIVACIDAD
|
||||
# ============================================
|
||||
|
||||
Rule: Solo el dueno puede ver el historial de su mascota
|
||||
|
||||
Scenario: Dueno ve historial de su mascota
|
||||
Given que soy dueno de Luna
|
||||
When accedo al historial de Luna
|
||||
Then deberia poder verlo completo
|
||||
|
||||
Scenario: No puedo ver historial de mascota ajena
|
||||
Given que existe mascota "Rocky" de otro dueno
|
||||
When intento acceder al historial de Rocky
|
||||
Then deberia ver error "No tienes acceso"
|
||||
# 403 Forbidden
|
||||
|
||||
# ============================================
|
||||
# BUSQUEDA Y FILTROS
|
||||
# ============================================
|
||||
|
||||
Scenario: Filtrar historial por tipo de servicio
|
||||
Given que Luna tiene visitas de vacunacion y consulta
|
||||
When filtro por "Vacunacion"
|
||||
Then solo deberia ver visitas de vacunacion
|
||||
|
||||
Scenario: Buscar en historial por fecha
|
||||
When busco visitas del "2023"
|
||||
Then solo deberia ver visitas del 2023
|
||||
And no deberia ver visitas del 2024
|
||||
@@ -0,0 +1,109 @@
|
||||
# Fuente: album/book/ops-templates/veterinarian/01-aceptar-solicitud.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/solicitudes/test_service_requests.py
|
||||
# Tests Frontend: npx playwright test vet-requests.spec.ts
|
||||
|
||||
Feature: Aceptar o rechazar solicitudes de servicio
|
||||
|
||||
Como veterinario de la plataforma
|
||||
Quiero revisar y responder a solicitudes en mi zona
|
||||
Para gestionar mi agenda de visitas a domicilio
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como veterinario
|
||||
And tengo cobertura en los barrios "Palermo" y "Recoleta"
|
||||
And estoy en el dashboard de veterinario
|
||||
|
||||
# ============================================
|
||||
# VER SOLICITUDES PENDIENTES
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver lista de solicitudes en mi zona
|
||||
When veo la seccion "Solicitudes pendientes"
|
||||
Then deberia ver solo solicitudes de "Palermo" y "Recoleta"
|
||||
And no deberia ver solicitudes de otros barrios
|
||||
|
||||
Scenario: Ver detalle de solicitud
|
||||
Given que hay una solicitud pendiente
|
||||
When hago click en la solicitud
|
||||
Then deberia ver:
|
||||
| seccion | contenido |
|
||||
| Dueno | Nombre, telefono, direccion |
|
||||
| Mascota | Nombre, tipo, edad, foto |
|
||||
| Servicios | Lista de servicios solicitados |
|
||||
| Fechas | Fechas preferidas por el dueno |
|
||||
| Historial | Visitas anteriores si las hay |
|
||||
|
||||
# ============================================
|
||||
# ACEPTAR SOLICITUD
|
||||
# ============================================
|
||||
|
||||
Scenario: Aceptar solicitud con fecha disponible
|
||||
Given que hay una solicitud para el barrio "Palermo"
|
||||
And el dueno prefiere fechas:
|
||||
| fecha | franja |
|
||||
| 2024-01-15 | Mañana |
|
||||
| 2024-01-16 | Tarde |
|
||||
And tengo disponibilidad el 15 de enero a las 10:00
|
||||
When hago click en "Aceptar"
|
||||
And selecciono fecha "15 de enero" hora "10:00"
|
||||
And confirmo la aceptacion
|
||||
Then la solicitud deberia pasar a estado "Coordinado"
|
||||
And deberia quedar asignada a mi
|
||||
And el dueno deberia recibir notificacion con mis datos
|
||||
And la visita deberia aparecer en mi agenda
|
||||
|
||||
Scenario: Aceptar solicitud con datos de mascota que ya atendi
|
||||
Given que hay una solicitud para mascota "Luna"
|
||||
And yo atendi a "Luna" anteriormente
|
||||
When veo el detalle de la solicitud
|
||||
Then deberia ver badge "Paciente recurrente"
|
||||
And deberia ver el historial de mis visitas anteriores a Luna
|
||||
|
||||
# ============================================
|
||||
# RECHAZAR SOLICITUD
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Rechazar solicitud con motivo
|
||||
Given que hay una solicitud pendiente
|
||||
When hago click en "Rechazar"
|
||||
And selecciono motivo "<motivo>"
|
||||
And confirmo el rechazo
|
||||
Then la solicitud deberia desaparecer de mi lista
|
||||
And deberia seguir visible para otros veterinarios
|
||||
|
||||
Examples:
|
||||
| motivo |
|
||||
| No tengo disponibilidad |
|
||||
| Fuera de mi zona |
|
||||
| No realizo este servicio |
|
||||
| Otro |
|
||||
|
||||
# ============================================
|
||||
# RACE CONDITIONS
|
||||
# ============================================
|
||||
|
||||
Rule: Solo un veterinario puede aceptar cada solicitud
|
||||
|
||||
Scenario: Otro vet acepta mientras estoy viendo
|
||||
Given que estoy viendo el detalle de una solicitud
|
||||
And otro veterinario acepta la misma solicitud
|
||||
When intento aceptarla
|
||||
Then deberia ver error "Esta solicitud ya fue aceptada"
|
||||
And deberia ser redirigido al listado
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Solicitud urgente destacada
|
||||
Given que hay una solicitud marcada como "Urgente"
|
||||
When veo el listado de solicitudes
|
||||
Then deberia ver la solicitud con indicador de urgencia
|
||||
And deberia aparecer primero en la lista
|
||||
|
||||
Scenario: No puedo aceptar con agenda completa
|
||||
Given que tengo mi agenda completa para las fechas de la solicitud
|
||||
When intento aceptar la solicitud
|
||||
Then deberia ver advertencia "No tienes disponibilidad en las fechas preferidas"
|
||||
And deberia poder proponer una fecha alternativa
|
||||
@@ -0,0 +1,99 @@
|
||||
# Fuente: album/book/ops-templates/veterinarian/02-gestion-agenda.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_availability.py
|
||||
# Tests Frontend: npx playwright test vet-schedule.spec.ts
|
||||
|
||||
Feature: Gestion de agenda veterinaria
|
||||
|
||||
Como veterinario
|
||||
Quiero gestionar mi calendario y disponibilidad
|
||||
Para organizar mis visitas a domicilio eficientemente
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como veterinario
|
||||
And estoy en la seccion "Mi agenda"
|
||||
|
||||
# ============================================
|
||||
# VER AGENDA
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver calendario semanal
|
||||
When veo mi agenda en vista semanal
|
||||
Then deberia ver los 7 dias de la semana
|
||||
And deberia ver mis visitas programadas
|
||||
And cada visita deberia mostrar hora y nombre del paciente
|
||||
|
||||
Scenario: Ver visitas con codigo de color por estado
|
||||
Given que tengo visitas en diferentes estados
|
||||
When veo mi agenda
|
||||
Then las visitas deberian mostrarse con colores:
|
||||
| estado | color |
|
||||
| Coordinado | amarillo |
|
||||
| Pagado | verde |
|
||||
| En progreso | azul |
|
||||
| Completado | gris |
|
||||
|
||||
# ============================================
|
||||
# CONFIGURAR DISPONIBILIDAD SEMANAL
|
||||
# ============================================
|
||||
|
||||
Scenario: Configurar horario laboral
|
||||
When voy a "Configuracion" -> "Mi disponibilidad"
|
||||
And configuro mi horario:
|
||||
| dia | trabajo | desde | hasta | pausa_desde | pausa_hasta |
|
||||
| Lunes | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Martes | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Miercoles | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Jueves | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Viernes | Si | 09:00 | 15:00 | | |
|
||||
| Sabado | No | | | | |
|
||||
| Domingo | No | | | | |
|
||||
And guardo la configuracion
|
||||
Then mi disponibilidad deberia actualizarse
|
||||
And no deberia recibir solicitudes fuera de ese horario
|
||||
|
||||
# ============================================
|
||||
# BLOQUEAR DIAS ESPECIFICOS
|
||||
# ============================================
|
||||
|
||||
Scenario: Marcar dia como no disponible
|
||||
Given que necesito el 20 de enero libre
|
||||
When hago click en el dia 20 de enero en el calendario
|
||||
And selecciono "Marcar como no disponible"
|
||||
And ingreso motivo "Vacaciones"
|
||||
And confirmo
|
||||
Then el dia 20 deberia mostrarse como bloqueado
|
||||
And no deberia poder aceptar solicitudes para ese dia
|
||||
|
||||
Scenario: Bloquear rango de fechas
|
||||
Given que tomo vacaciones del 15 al 22 de enero
|
||||
When voy a "Agregar indisponibilidad"
|
||||
And selecciono fecha inicio "15 de enero"
|
||||
And selecciono fecha fin "22 de enero"
|
||||
And ingreso motivo "Vacaciones"
|
||||
And confirmo
|
||||
Then todos esos dias deberian mostrarse como bloqueados
|
||||
|
||||
# ============================================
|
||||
# IMPACTO EN SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Rule: Los bloqueos no afectan visitas ya aceptadas
|
||||
|
||||
Scenario: Bloquear dia con visita ya aceptada
|
||||
Given que tengo una visita aceptada para el 15 de enero
|
||||
When intento bloquear el 15 de enero
|
||||
Then deberia ver advertencia "Tienes una visita programada ese dia"
|
||||
And deberia poder elegir:
|
||||
| opcion |
|
||||
| Cancelar el bloqueo |
|
||||
| Bloquear y contactar al dueno |
|
||||
|
||||
Rule: Cambios de disponibilidad solo afectan solicitudes futuras
|
||||
|
||||
Scenario: Cambiar horario no afecta visitas existentes
|
||||
Given que tengo una visita a las 17:00 el lunes
|
||||
When cambio mi horario del lunes para terminar a las 16:00
|
||||
And guardo los cambios
|
||||
Then la visita de las 17:00 deberia mantenerse
|
||||
And deberia ver advertencia sobre la inconsistencia
|
||||
@@ -0,0 +1,127 @@
|
||||
# Fuente: album/book/ops-templates/veterinarian/03-realizar-visita.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_visits.py
|
||||
# Tests Frontend: npx playwright test vet-visit.spec.ts
|
||||
|
||||
Feature: Realizar visita y crear informe medico
|
||||
|
||||
Como veterinario
|
||||
Quiero registrar la atencion medica de mis visitas
|
||||
Para mantener el historial clinico del paciente
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como veterinario
|
||||
And tengo una visita programada para hoy con mascota "Luna"
|
||||
And la visita esta en estado "Pagado"
|
||||
|
||||
# ============================================
|
||||
# INICIAR VISITA
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver informacion antes de la visita
|
||||
When accedo al detalle de la visita
|
||||
Then deberia ver la direccion para llegar
|
||||
And deberia ver el telefono del dueno para confirmar
|
||||
And deberia ver los servicios a realizar
|
||||
And deberia ver el historial previo de Luna
|
||||
|
||||
Scenario: Iniciar visita al llegar
|
||||
Given que llegue al domicilio
|
||||
When hago click en "Iniciar visita"
|
||||
Then el estado deberia cambiar a "En progreso"
|
||||
And deberia registrarse la hora de inicio
|
||||
And deberia habilitarse el boton "Crear informe"
|
||||
|
||||
# ============================================
|
||||
# CREAR INFORME MEDICO
|
||||
# ============================================
|
||||
|
||||
Scenario: Completar informe con examen fisico
|
||||
Given que la visita esta en progreso
|
||||
When voy a "Crear informe"
|
||||
And completo el examen fisico:
|
||||
| campo | valor |
|
||||
| Peso | 4.5 kg |
|
||||
| Temperatura | 38.5 °C |
|
||||
| Frecuencia cardiaca | 120 lpm |
|
||||
| Frecuencia resp | 25 rpm |
|
||||
| Mucosas | Rosadas |
|
||||
| Hidratacion | Normal |
|
||||
And guardo el informe
|
||||
Then el examen fisico deberia guardarse
|
||||
|
||||
Scenario: Agregar diagnostico y tratamiento
|
||||
Given que complete el examen fisico
|
||||
When agrego el diagnostico:
|
||||
"""
|
||||
Otitis externa bilateral leve.
|
||||
Paciente presenta prurito y secrecion ceruminosa.
|
||||
Sin signos de infeccion secundaria.
|
||||
"""
|
||||
And agrego el tratamiento:
|
||||
"""
|
||||
Limpieza de oidos con solucion fisiologica.
|
||||
Aplicacion de gotas oticas antibioticas.
|
||||
Control en 7 dias.
|
||||
"""
|
||||
And guardo el informe
|
||||
Then el diagnostico y tratamiento deberian guardarse
|
||||
|
||||
Scenario: Recetar medicamentos
|
||||
Given que estoy creando el informe
|
||||
When agrego medicamentos:
|
||||
| nombre | dosis | frecuencia | duracion |
|
||||
| Otomax gotas | 5 gotas | cada 12 horas | 7 dias |
|
||||
| Meloxicam 1.5mg | 1 comp | cada 24 horas | 3 dias |
|
||||
And guardo el informe
|
||||
Then los medicamentos deberian aparecer en el informe
|
||||
And el dueno podra verlos desde su cuenta
|
||||
|
||||
# ============================================
|
||||
# COMPLETAR VISITA
|
||||
# ============================================
|
||||
|
||||
Scenario: Completar visita con informe
|
||||
Given que el informe esta completo
|
||||
When hago click en "Completar visita"
|
||||
And confirmo la finalizacion
|
||||
Then la visita deberia pasar a estado "Completado"
|
||||
And deberia generarse factura electronica (AFIP)
|
||||
And el dueno deberia recibir notificacion
|
||||
And el informe deberia ser visible para el dueno
|
||||
|
||||
Scenario: No puedo completar sin informe
|
||||
Given que la visita esta en progreso
|
||||
But no cree ningun informe
|
||||
When intento completar la visita
|
||||
Then deberia ver error "Debes crear el informe antes de completar"
|
||||
|
||||
# ============================================
|
||||
# GUARDADO AUTOMATICO
|
||||
# ============================================
|
||||
|
||||
Rule: El informe se guarda automaticamente
|
||||
|
||||
Scenario: Guardado automatico cada 30 segundos
|
||||
Given que estoy escribiendo el informe
|
||||
When pasan 30 segundos
|
||||
Then deberia ver indicador "Guardado automaticamente"
|
||||
|
||||
Scenario: Recuperar informe despues de desconexion
|
||||
Given que estaba escribiendo el informe
|
||||
And perdi conexion a internet
|
||||
When recupero la conexion
|
||||
Then deberia recuperar mi progreso
|
||||
And no deberia perder lo que escribi
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Cancelar visita in situ
|
||||
Given que llegue al domicilio
|
||||
But el dueno no esta
|
||||
When marco la visita como "Cancelada in situ"
|
||||
And selecciono motivo "Dueno ausente"
|
||||
Then la visita deberia marcarse como cancelada
|
||||
And deberia generarse cargo por visita fallida
|
||||
@@ -0,0 +1,83 @@
|
||||
# Fuente: album/book/ops-templates/veterinarian/04-zonas-cobertura.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_veterinarians.py
|
||||
# Tests Frontend: npx playwright test vet-coverage.spec.ts
|
||||
|
||||
Feature: Gestionar zonas de cobertura
|
||||
|
||||
Como veterinario
|
||||
Quiero definir en que zonas atiendo
|
||||
Para recibir solo solicitudes que puedo cubrir
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como veterinario
|
||||
And estoy en la seccion "Mi cobertura"
|
||||
|
||||
# ============================================
|
||||
# VER ZONAS ACTUALES
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver mapa con mis zonas de cobertura
|
||||
When cargo la pagina de cobertura
|
||||
Then deberia ver un mapa de la ciudad
|
||||
And mis zonas cubiertas deberian estar resaltadas
|
||||
And deberia ver un listado de barrios seleccionados
|
||||
|
||||
Scenario: Ver estadisticas por zona
|
||||
Given que tengo cobertura en "Palermo" y "Recoleta"
|
||||
When veo el detalle de mis zonas
|
||||
Then deberia ver para cada zona:
|
||||
| metrica |
|
||||
| Solicitudes este mes |
|
||||
| Visitas completadas |
|
||||
| Otros vets en la zona |
|
||||
|
||||
# ============================================
|
||||
# AGREGAR ZONAS
|
||||
# ============================================
|
||||
|
||||
Scenario: Agregar barrio desde el mapa
|
||||
Given que no tengo cobertura en "Belgrano"
|
||||
When hago click en "Belgrano" en el mapa
|
||||
And confirmo agregar la zona
|
||||
Then "Belgrano" deberia aparecer en mi lista de zonas
|
||||
And deberia empezar a ver solicitudes de Belgrano
|
||||
|
||||
Scenario: Agregar multiples zonas a la vez
|
||||
When selecciono los barrios:
|
||||
| barrio |
|
||||
| Colegiales |
|
||||
| Chacarita |
|
||||
| Villa Crespo |
|
||||
And hago click en "Agregar seleccionados"
|
||||
Then los 3 barrios deberian agregarse a mi cobertura
|
||||
|
||||
# ============================================
|
||||
# QUITAR ZONAS
|
||||
# ============================================
|
||||
|
||||
Scenario: Quitar zona de cobertura
|
||||
Given que tengo cobertura en "Recoleta"
|
||||
When hago click en "X" junto a "Recoleta"
|
||||
And confirmo quitar la zona
|
||||
Then "Recoleta" no deberia estar en mi lista
|
||||
And no deberia ver nuevas solicitudes de Recoleta
|
||||
|
||||
# ============================================
|
||||
# IMPACTO EN SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Rule: Los cambios de zona afectan solo solicitudes nuevas
|
||||
|
||||
Scenario: Agregar zona muestra solicitudes existentes
|
||||
Given que no tengo cobertura en "Belgrano"
|
||||
And hay 3 solicitudes pendientes en Belgrano
|
||||
When agrego "Belgrano" a mi cobertura
|
||||
Then deberia ver las 3 solicitudes pendientes de Belgrano
|
||||
|
||||
Scenario: Quitar zona no afecta visitas aceptadas
|
||||
Given que tengo cobertura en "Recoleta"
|
||||
And tengo una visita aceptada en Recoleta para mañana
|
||||
When quito "Recoleta" de mi cobertura
|
||||
Then la visita de mañana deberia mantenerse
|
||||
But no deberia ver nuevas solicitudes de Recoleta
|
||||
@@ -0,0 +1,100 @@
|
||||
# Fuente: album/book/ops-templates/veterinarian/05-historial-pacientes.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_visits.py
|
||||
# Tests Frontend: npx playwright test vet-history.spec.ts
|
||||
|
||||
Feature: Ver historial de pacientes
|
||||
|
||||
Como veterinario
|
||||
Quiero acceder al historial medico de pacientes
|
||||
Para tener contexto clinico en mis atenciones
|
||||
|
||||
Background:
|
||||
Given que estoy logueado como veterinario
|
||||
And estoy en la seccion "Historia clinica"
|
||||
|
||||
# ============================================
|
||||
# BUSCAR PACIENTES
|
||||
# ============================================
|
||||
|
||||
Scenario Outline: Buscar paciente por diferentes criterios
|
||||
When busco por <criterio> con valor "<valor>"
|
||||
Then deberia ver resultados que coincidan
|
||||
|
||||
Examples:
|
||||
| criterio | valor |
|
||||
| nombre dueno | Maria Garcia |
|
||||
| nombre mascota | Luna |
|
||||
| telefono | 1155551234 |
|
||||
| email | maria@ejemplo.com |
|
||||
|
||||
Scenario: Busqueda sin resultados
|
||||
When busco "ZZZZZ paciente inexistente"
|
||||
Then deberia ver mensaje "No se encontraron resultados"
|
||||
And deberia ver sugerencia "Verifica la ortografia"
|
||||
|
||||
# ============================================
|
||||
# VER FICHA DE MASCOTA
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver ficha completa de mascota
|
||||
Given que encontre a la mascota "Luna"
|
||||
When hago click en Luna
|
||||
Then deberia ver la ficha con:
|
||||
| seccion | contenido |
|
||||
| Datos basicos | Nombre, tipo, raza, edad, peso |
|
||||
| Foto | Foto de la mascota |
|
||||
| Dueno | Nombre y contacto del dueno |
|
||||
| Vacunacion | Estado de vacunas |
|
||||
| Historial | Lista de visitas |
|
||||
|
||||
# ============================================
|
||||
# VER HISTORIAL DE VISITAS
|
||||
# ============================================
|
||||
|
||||
Scenario: Ver listado de visitas
|
||||
Given que estoy viendo la ficha de "Luna"
|
||||
And Luna tiene 5 visitas completadas
|
||||
When veo la seccion "Historial de visitas"
|
||||
Then deberia ver las 5 visitas listadas
|
||||
And deberian estar ordenadas de mas reciente a mas antigua
|
||||
|
||||
Scenario: Ver informe de visita de otro veterinario
|
||||
Given que Luna fue atendida por "Dra. Rodriguez"
|
||||
And yo no la atendi en esa visita
|
||||
When hago click en esa visita
|
||||
Then deberia poder ver el informe completo
|
||||
# Para continuidad de atencion
|
||||
|
||||
# ============================================
|
||||
# FILTROS Y NAVEGACION
|
||||
# ============================================
|
||||
|
||||
Scenario: Filtrar historial por tipo de servicio
|
||||
Given que estoy viendo el historial de "Luna"
|
||||
When filtro por servicio "Vacunacion"
|
||||
Then solo deberia ver visitas de vacunacion
|
||||
|
||||
Scenario: Filtrar por mis atenciones
|
||||
Given que estoy viendo el historial de "Luna"
|
||||
And Luna fue atendida por varios veterinarios
|
||||
When marco "Solo mis atenciones"
|
||||
Then solo deberia ver las visitas que yo realice
|
||||
|
||||
# ============================================
|
||||
# PERMISOS
|
||||
# ============================================
|
||||
|
||||
Rule: Veterinarios pueden ver historial de pacientes que atendieron
|
||||
|
||||
Scenario: Puedo ver historial de paciente que atendi
|
||||
Given que yo atendi a "Luna" al menos una vez
|
||||
When busco a Luna
|
||||
Then deberia poder ver su historial completo
|
||||
|
||||
Scenario: Puedo ver historial de paciente con solicitud pendiente
|
||||
Given que hay una solicitud pendiente para "Rocky"
|
||||
And la solicitud esta en mi zona
|
||||
When busco a Rocky
|
||||
Then deberia poder ver su historial
|
||||
# Para evaluar si acepto la solicitud
|
||||
@@ -0,0 +1,158 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/backoffice/01-gestion-solicitudes.md
|
||||
# Drive: 05. ATC - Operaciones/Procedimientos
|
||||
# Tests Backend: pytest tests/contracts/solicitudes/test_service_requests.py
|
||||
# Tests Frontend: npx playwright test admin-requests.spec.ts
|
||||
|
||||
Característica: Gestion de solicitudes de servicio
|
||||
|
||||
Como administrador del backoffice
|
||||
Quiero gestionar todas las solicitudes del sistema
|
||||
Para asegurar que se atiendan correctamente
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como administrador
|
||||
Y estoy en la seccion "Solicitudes" del backoffice
|
||||
|
||||
# ============================================
|
||||
# VER Y FILTRAR SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver listado de todas las solicitudes
|
||||
Cuando cargo la pagina de solicitudes
|
||||
Entonces deberia ver un listado paginado
|
||||
Y cada solicitud deberia mostrar:
|
||||
| campo |
|
||||
| ID |
|
||||
| Dueno |
|
||||
| Mascota |
|
||||
| Servicios |
|
||||
| Estado |
|
||||
| Fecha |
|
||||
|
||||
Esquema del escenario: Filtrar solicitudes por estado
|
||||
Cuando filtro por estado "<estado>"
|
||||
Entonces solo deberia ver solicitudes con estado "<estado>"
|
||||
|
||||
Ejemplos:
|
||||
| estado |
|
||||
| Pendiente |
|
||||
| Coordinado |
|
||||
| Pagado |
|
||||
| Completado |
|
||||
| Cancelado |
|
||||
|
||||
Escenario: Buscar solicitud especifica
|
||||
Cuando busco por email "maria@ejemplo.com"
|
||||
Entonces deberia ver solo solicitudes de ese dueno
|
||||
|
||||
Escenario: Filtrar por rango de fechas
|
||||
Cuando filtro desde "01/01/2024" hasta "31/01/2024"
|
||||
Entonces solo deberia ver solicitudes de enero 2024
|
||||
|
||||
# ============================================
|
||||
# ASIGNAR VETERINARIO MANUALMENTE
|
||||
# ============================================
|
||||
|
||||
Escenario: Asignar veterinario a solicitud pendiente
|
||||
Dado que hay una solicitud pendiente en "Palermo"
|
||||
Y no fue aceptada por ningun veterinario
|
||||
Cuando abro el detalle de la solicitud
|
||||
Y hago click en "Asignar veterinario"
|
||||
Entonces deberia ver lista de veterinarios con cobertura en Palermo
|
||||
|
||||
Cuando selecciono "Dra. Garcia"
|
||||
Y selecciono fecha "15 de enero" hora "10:00"
|
||||
Y confirmo la asignacion
|
||||
Entonces la solicitud deberia pasar a estado "Coordinado"
|
||||
Y deberia estar asignada a Dra. Garcia
|
||||
Y el dueno deberia recibir notificacion
|
||||
Y el veterinario deberia recibir notificacion
|
||||
|
||||
Escenario: Ver disponibilidad de veterinarios antes de asignar
|
||||
Dado que estoy asignando un veterinario
|
||||
Cuando veo la lista de veterinarios disponibles
|
||||
Entonces deberia ver para cada uno:
|
||||
| informacion |
|
||||
| Nombre |
|
||||
| Disponibilidad |
|
||||
| Visitas del dia |
|
||||
| Distancia a destino |
|
||||
|
||||
# ============================================
|
||||
# CAMBIAR ESTADO MANUALMENTE
|
||||
# ============================================
|
||||
|
||||
Escenario: Cambiar estado de solicitud
|
||||
Dado que hay una solicitud en estado "Coordinado"
|
||||
Y el pago se proceso pero el webhook fallo
|
||||
Cuando abro la solicitud
|
||||
Y hago click en "Cambiar estado"
|
||||
Y selecciono "Pagado"
|
||||
Y ingreso motivo "Pago confirmado manualmente - ID MP: 12345"
|
||||
Y confirmo el cambio
|
||||
Entonces la solicitud deberia pasar a "Pagado"
|
||||
Y el cambio deberia registrarse en el historial
|
||||
Y deberia quedar mi usuario como responsable del cambio
|
||||
|
||||
Regla: Solo ciertos cambios de estado son validos
|
||||
|
||||
Escenario: No puedo volver a estado anterior
|
||||
Dado que hay una solicitud en estado "Completado"
|
||||
Cuando intento cambiar el estado a "Pagado"
|
||||
Entonces deberia ver error "No se puede volver a un estado anterior"
|
||||
|
||||
Escenario: Puedo cancelar desde cualquier estado
|
||||
Dado que hay una solicitud en estado "Coordinado"
|
||||
Cuando cambio el estado a "Cancelado"
|
||||
Y ingreso motivo de cancelacion
|
||||
Entonces la solicitud deberia cancelarse
|
||||
|
||||
# ============================================
|
||||
# VER DETALLE COMPLETO
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver historial de cambios de una solicitud
|
||||
Dado que hay una solicitud con varios cambios de estado
|
||||
Cuando abro el detalle de la solicitud
|
||||
Y voy a la pestaña "Historial"
|
||||
Entonces deberia ver todos los cambios con:
|
||||
| campo |
|
||||
| Fecha y hora |
|
||||
| Estado anterior |
|
||||
| Estado nuevo |
|
||||
| Usuario |
|
||||
| Motivo |
|
||||
|
||||
Escenario: Ver informacion de pago
|
||||
Dado que hay una solicitud pagada
|
||||
Cuando abro el detalle
|
||||
Entonces deberia ver seccion "Pago" con:
|
||||
| campo |
|
||||
| Monto |
|
||||
| Fecha de pago |
|
||||
| Metodo |
|
||||
| ID operacion MP |
|
||||
| Estado del pago |
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Reasignar veterinario
|
||||
Dado que hay una solicitud asignada a "Dr. Lopez"
|
||||
Y Dr. Lopez no puede asistir
|
||||
Cuando abro la solicitud
|
||||
Y hago click en "Reasignar"
|
||||
Y selecciono otro veterinario
|
||||
Y confirmo
|
||||
Entonces Dr. Lopez deberia ser notificado de la desasignacion
|
||||
Y el nuevo vet deberia ser notificado
|
||||
Y el dueno deberia ser notificado del cambio
|
||||
|
||||
Escenario: Solicitud sin veterinarios disponibles
|
||||
Dado que hay una solicitud pendiente en una zona remota
|
||||
Y no hay veterinarios con cobertura en esa zona
|
||||
Cuando intento asignar veterinario
|
||||
Entonces deberia ver mensaje "No hay veterinarios disponibles"
|
||||
Y deberia poder expandir la busqueda a zonas cercanas
|
||||
@@ -0,0 +1,131 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/backoffice/02-gestion-usuarios.md
|
||||
# Drive: 05. ATC - Operaciones/Procedimientos
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_pet_owners.py
|
||||
# Tests Frontend: npx playwright test admin-users.spec.ts
|
||||
|
||||
Característica: Gestion de usuarios
|
||||
|
||||
Como administrador del backoffice
|
||||
Quiero gestionar duenos y veterinarios
|
||||
Para mantener la base de usuarios actualizada
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como administrador
|
||||
Y estoy en el backoffice
|
||||
|
||||
# ============================================
|
||||
# GESTION DE DUENOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Buscar dueno de mascota
|
||||
Dado que estoy en la seccion "Duenos"
|
||||
Cuando busco "maria@ejemplo.com"
|
||||
Entonces deberia ver a Maria Garcia en los resultados
|
||||
Y deberia ver sus mascotas listadas
|
||||
Y deberia ver cantidad de turnos
|
||||
|
||||
Escenario: Ver perfil completo de dueno
|
||||
Dado que encontre a "Maria Garcia"
|
||||
Cuando hago click en su perfil
|
||||
Entonces deberia ver:
|
||||
| seccion | contenido |
|
||||
| Datos personales | Nombre, email, telefono |
|
||||
| Direcciones | Direcciones registradas |
|
||||
| Mascotas | Lista de mascotas |
|
||||
| Historial | Solicitudes anteriores |
|
||||
| Pagos | Historial de pagos |
|
||||
|
||||
Escenario: Editar datos de dueno
|
||||
Dado que estoy viendo el perfil de un dueno
|
||||
Cuando hago click en "Editar"
|
||||
Y cambio el telefono a "1155559999"
|
||||
Y guardo los cambios
|
||||
Entonces el telefono deberia actualizarse
|
||||
Y deberia registrarse quien hizo el cambio
|
||||
|
||||
Esquema del escenario: Filtrar duenos por tipo
|
||||
Dado que estoy en la lista de duenos
|
||||
Cuando filtro por tipo "<tipo>"
|
||||
Entonces solo deberia ver usuarios <tipo>
|
||||
|
||||
Ejemplos:
|
||||
| tipo |
|
||||
| Registrados |
|
||||
| Invitados |
|
||||
|
||||
# ============================================
|
||||
# GESTION DE VETERINARIOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver lista de veterinarios
|
||||
Cuando voy a la seccion "Veterinarios"
|
||||
Entonces deberia ver listado de todos los vets
|
||||
Y cada vet deberia mostrar:
|
||||
| campo |
|
||||
| Nombre |
|
||||
| Matricula |
|
||||
| Zonas |
|
||||
| Estado |
|
||||
| Visitas mes |
|
||||
|
||||
Escenario: Agregar nuevo veterinario
|
||||
Dado que estoy en la seccion "Veterinarios"
|
||||
Cuando hago click en "Agregar veterinario"
|
||||
Y completo los datos:
|
||||
| campo | valor |
|
||||
| Nombre | Dr. Juan Perez |
|
||||
| Matricula | MV-12345 |
|
||||
| Email | jperez@ejemplo.com |
|
||||
| Telefono | 1155551234 |
|
||||
| Especialidades | Clinica general |
|
||||
Y selecciono zonas de cobertura:
|
||||
| zona |
|
||||
| Palermo |
|
||||
| Recoleta |
|
||||
Y genero credenciales de acceso
|
||||
Y guardo
|
||||
Entonces deberia crearse el veterinario
|
||||
Y deberia poder loguearse con sus credenciales
|
||||
|
||||
Escenario: Desactivar veterinario sin citas pendientes
|
||||
Dado que el veterinario "Dr. Lopez" no tiene citas pendientes
|
||||
Cuando abro su perfil
|
||||
Y hago click en "Desactivar"
|
||||
Y confirmo la desactivacion
|
||||
Entonces Dr. Lopez deberia estar inactivo
|
||||
Y no deberia recibir nuevas solicitudes
|
||||
Y no deberia poder loguearse
|
||||
|
||||
Escenario: Intentar desactivar veterinario con citas pendientes
|
||||
Dado que el veterinario "Dra. Garcia" tiene 3 citas pendientes
|
||||
Cuando intento desactivarla
|
||||
Entonces deberia ver advertencia "Tiene 3 citas pendientes"
|
||||
Y deberia ver opciones:
|
||||
| opcion |
|
||||
| Reasignar citas y desactivar |
|
||||
| Cancelar |
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Fusionar cuentas duplicadas
|
||||
Dado que hay dos cuentas para el mismo dueno:
|
||||
| cuenta | email | mascotas |
|
||||
| Cuenta1 | maria@gmail.com | Luna |
|
||||
| Cuenta2 | maria@hotmail.com | Rocky |
|
||||
Cuando selecciono ambas cuentas
|
||||
Y hago click en "Fusionar"
|
||||
Y elijo Cuenta1 como principal
|
||||
Y confirmo
|
||||
Entonces deberia existir solo Cuenta1
|
||||
Y deberia tener ambas mascotas
|
||||
Y el historial deberia combinarse
|
||||
|
||||
Escenario: Convertir invitado a registrado
|
||||
Dado que hay un usuario invitado con email "invitado@test.com"
|
||||
Cuando abro su perfil
|
||||
Y hago click en "Enviar invitacion a registrarse"
|
||||
Entonces deberia enviarse email con link de registro
|
||||
Y el usuario podra crear contraseña y activar cuenta
|
||||
@@ -0,0 +1,131 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/backoffice/03-gestion-servicios.md
|
||||
# Drive: 08. IT y Producto/Catalogo Servicios
|
||||
# Tests Backend: pytest tests/contracts/productos/test_services.py
|
||||
# Tests Frontend: npx playwright test admin-services.spec.ts
|
||||
|
||||
Característica: Gestion de servicios y precios
|
||||
|
||||
Como administrador
|
||||
Quiero gestionar el catalogo de servicios
|
||||
Para mantener la oferta actualizada
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como administrador
|
||||
Y estoy en la seccion "Servicios"
|
||||
|
||||
# ============================================
|
||||
# VER CATALOGO DE SERVICIOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver listado de servicios
|
||||
Cuando cargo la pagina de servicios
|
||||
Entonces deberia ver todos los servicios organizados por categoria
|
||||
Y cada servicio deberia mostrar:
|
||||
| campo |
|
||||
| Nombre |
|
||||
| Categoria |
|
||||
| Tipo mascota |
|
||||
| Precio actual |
|
||||
| Estado |
|
||||
|
||||
Esquema del escenario: Filtrar servicios
|
||||
Cuando filtro por <filtro> "<valor>"
|
||||
Entonces solo deberia ver servicios que coincidan
|
||||
|
||||
Ejemplos:
|
||||
| filtro | valor |
|
||||
| categoria | Vacunacion |
|
||||
| tipo_mascota | Gato |
|
||||
| estado | Activo |
|
||||
|
||||
# ============================================
|
||||
# CREAR Y EDITAR SERVICIOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Agregar nuevo servicio
|
||||
Cuando hago click en "Agregar servicio"
|
||||
Y completo los datos:
|
||||
| campo | valor |
|
||||
| Nombre | Vacuna Quintuple Felina |
|
||||
| Descripcion | Protege contra 5 enfermedades |
|
||||
| Categoria | Vacunacion |
|
||||
| Tipo mascota | Gato |
|
||||
| Precio | 15000 |
|
||||
| Duracion | 30 minutos |
|
||||
Y guardo el servicio
|
||||
Entonces el servicio deberia crearse
|
||||
Y deberia aparecer en el turnero para gatos
|
||||
|
||||
Escenario: Editar servicio existente
|
||||
Dado que existe el servicio "Consulta clinica"
|
||||
Cuando abro el servicio
|
||||
Y cambio la descripcion
|
||||
Y guardo
|
||||
Entonces la descripcion deberia actualizarse
|
||||
Y las solicitudes existentes no deberian afectarse
|
||||
|
||||
Escenario: Desactivar servicio
|
||||
Dado que existe el servicio "Servicio Antiguo"
|
||||
Cuando abro el servicio
|
||||
Y hago click en "Desactivar"
|
||||
Y confirmo
|
||||
Entonces el servicio no deberia aparecer en el turnero
|
||||
Y las solicitudes existentes deberian mantenerse
|
||||
|
||||
# ============================================
|
||||
# GESTION DE PRECIOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Actualizar precio de servicio
|
||||
Dado que "Consulta clinica" tiene precio actual de 10000
|
||||
Cuando abro el servicio
|
||||
Y voy a la seccion "Precios"
|
||||
Y hago click en "Agregar precio"
|
||||
Y ingreso nuevo precio 12000
|
||||
Y selecciono fecha de vigencia "01/02/2024"
|
||||
Y guardo
|
||||
Entonces deberia crearse el nuevo precio
|
||||
Y el precio anterior deberia quedar en historial
|
||||
Y el nuevo precio deberia aplicar desde la fecha indicada
|
||||
|
||||
Escenario: Ver historial de precios
|
||||
Dado que un servicio tuvo varios cambios de precio
|
||||
Cuando veo la seccion "Precios"
|
||||
Entonces deberia ver historial con:
|
||||
| campo |
|
||||
| Precio |
|
||||
| Fecha desde |
|
||||
| Fecha hasta |
|
||||
| Usuario |
|
||||
|
||||
Regla: El precio se congela al crear la solicitud
|
||||
|
||||
Escenario: Cambio de precio no afecta solicitudes existentes
|
||||
Dado que hay una solicitud pendiente con "Consulta clinica" a 10000
|
||||
Cuando cambio el precio de "Consulta clinica" a 12000
|
||||
Entonces la solicitud deberia mantener el precio de 10000
|
||||
|
||||
# ============================================
|
||||
# CONFIGURAR COMBOS Y DEPENDENCIAS
|
||||
# ============================================
|
||||
|
||||
Escenario: Crear combo de servicios
|
||||
Cuando voy a "Combos"
|
||||
Y hago click en "Agregar combo"
|
||||
Y configuro:
|
||||
| campo | valor |
|
||||
| Nombre | Plan Preventivo Felino |
|
||||
| Servicios | Vacunacion, Desparasitacion, Antipulgas |
|
||||
| Precio combo | 25000 |
|
||||
| Descuento | 20% |
|
||||
Y guardo
|
||||
Entonces el combo deberia crearse
|
||||
Y deberia aplicarse automaticamente cuando se seleccionen esos servicios
|
||||
|
||||
Escenario: Configurar servicio dependiente
|
||||
Dado que "Vacunacion" requiere "Consulta clinica"
|
||||
Cuando configuro la dependencia
|
||||
Entonces al seleccionar "Vacunacion" en el turnero
|
||||
Y "Consulta clinica" deberia agregarse automaticamente
|
||||
Y no deberia poder quitarse
|
||||
141
atlas/book/gherkin-samples/es/backoffice/04-reembolsos.feature
Normal file
141
atlas/book/gherkin-samples/es/backoffice/04-reembolsos.feature
Normal file
@@ -0,0 +1,141 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/backoffice/04-reembolsos.md
|
||||
# Drive: 07. Finanzas y contabilidad/Reembolsos
|
||||
# Tests Backend: pytest tests/contracts/payments/test_refunds.py
|
||||
# Tests Frontend: npx playwright test admin-refunds.spec.ts
|
||||
|
||||
Característica: Proceso de reembolso
|
||||
|
||||
Como administrador
|
||||
Quiero procesar reembolsos de pagos
|
||||
Para resolver cancelaciones y problemas
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como administrador
|
||||
Y estoy en el backoffice
|
||||
|
||||
# ============================================
|
||||
# IDENTIFICAR PAGO A REEMBOLSAR
|
||||
# ============================================
|
||||
|
||||
Escenario: Buscar solicitud pagada por ID de operacion
|
||||
Cuando busco por ID de Mercado Pago "12345678"
|
||||
Entonces deberia encontrar la solicitud asociada
|
||||
Y deberia ver los detalles del pago
|
||||
|
||||
Escenario: Ver detalles de pago
|
||||
Dado que encontre una solicitud pagada
|
||||
Cuando veo la seccion de pago
|
||||
Entonces deberia ver:
|
||||
| campo |
|
||||
| Monto pagado |
|
||||
| Fecha de pago |
|
||||
| Metodo de pago |
|
||||
| ID operacion MP |
|
||||
| Estado en MP |
|
||||
|
||||
# ============================================
|
||||
# PROCESAR REEMBOLSO
|
||||
# ============================================
|
||||
|
||||
Escenario: Reembolso total exitoso
|
||||
Dado que hay una solicitud pagada por 15000
|
||||
Y la visita fue cancelada
|
||||
Cuando abro la solicitud
|
||||
Y hago click en "Procesar reembolso"
|
||||
Y selecciono "Reembolso total"
|
||||
Y ingreso motivo "Cancelacion por indisponibilidad del veterinario"
|
||||
Y confirmo el reembolso
|
||||
Entonces deberia enviarse la solicitud de reembolso a Mercado Pago
|
||||
Y deberia ver mensaje "Reembolso en proceso"
|
||||
Y la solicitud deberia registrar el reembolso
|
||||
Y el dueno deberia recibir email de confirmacion
|
||||
|
||||
Escenario: Reembolso parcial
|
||||
Dado que hay una solicitud pagada por 20000
|
||||
Y solo se realizo parte del servicio
|
||||
Cuando proceso reembolso parcial por 10000
|
||||
Y ingreso motivo "Servicio parcialmente completado"
|
||||
Y confirmo
|
||||
Entonces deberia procesarse reembolso por 10000
|
||||
Y deberia quedar registro del monto reembolsado
|
||||
Y deberia quedar registro del monto retenido
|
||||
|
||||
# ============================================
|
||||
# TIEMPOS DE ACREDITACION
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Informar tiempo de acreditacion segun metodo
|
||||
Dado que el pago original fue con "<metodo>"
|
||||
Cuando proceso el reembolso
|
||||
Entonces deberia informar al usuario:
|
||||
"""
|
||||
El reembolso se acreditara en <tiempo>
|
||||
"""
|
||||
|
||||
Ejemplos:
|
||||
| metodo | tiempo |
|
||||
| Tarjeta credito | 1-2 resumenes de cuenta |
|
||||
| Tarjeta debito | 5-10 dias habiles |
|
||||
| Dinero en cuenta MP | forma inmediata |
|
||||
| Transferencia | 5-10 dias habiles |
|
||||
|
||||
# ============================================
|
||||
# VALIDACIONES Y ERRORES
|
||||
# ============================================
|
||||
|
||||
Escenario: No puedo reembolsar mas del monto pagado
|
||||
Dado que hay una solicitud pagada por 15000
|
||||
Cuando intento reembolsar 20000
|
||||
Entonces deberia ver error "El monto supera el pago original"
|
||||
|
||||
Escenario: Reembolso duplicado
|
||||
Dado que ya procese un reembolso total para una solicitud
|
||||
Cuando intento procesar otro reembolso
|
||||
Entonces deberia ver error "Esta solicitud ya fue reembolsada"
|
||||
|
||||
Escenario: Pago fuera de plazo de reembolso
|
||||
Dado que hay un pago de hace 200 dias
|
||||
Cuando intento reembolsar
|
||||
Entonces deberia ver advertencia "Fuera de plazo de reembolso de MP (180 dias)"
|
||||
Y deberia sugerir "Contactar al dueno para solucion alternativa"
|
||||
|
||||
# ============================================
|
||||
# SEGUIMIENTO
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver estado del reembolso
|
||||
Dado que procese un reembolso hace 2 dias
|
||||
Cuando veo el detalle del reembolso
|
||||
Entonces deberia ver el estado actual en Mercado Pago
|
||||
Y deberia ver historial de estados:
|
||||
| estado | fecha |
|
||||
| Solicitado | 01/01/2024 |
|
||||
| En proceso | 01/01/2024 |
|
||||
| Completado | 03/01/2024 |
|
||||
|
||||
Escenario: Reembolso rechazado por MP
|
||||
Dado que Mercado Pago rechazo el reembolso
|
||||
Cuando veo el estado
|
||||
Entonces deberia ver "Reembolso rechazado"
|
||||
Y deberia ver el motivo del rechazo
|
||||
Y deberia poder contactar al dueno con alternativas
|
||||
|
||||
# ============================================
|
||||
# REGISTRO Y AUDITORIA
|
||||
# ============================================
|
||||
|
||||
Regla: Todos los reembolsos quedan registrados
|
||||
|
||||
Escenario: Ver historial de reembolsos
|
||||
Cuando voy a "Reportes" -> "Reembolsos"
|
||||
Entonces deberia ver lista de todos los reembolsos
|
||||
Y cada uno deberia mostrar:
|
||||
| campo |
|
||||
| Solicitud |
|
||||
| Monto original |
|
||||
| Monto reembolso |
|
||||
| Motivo |
|
||||
| Procesado por |
|
||||
| Fecha |
|
||||
| Estado |
|
||||
155
atlas/book/gherkin-samples/es/backoffice/05-reportes.feature
Normal file
155
atlas/book/gherkin-samples/es/backoffice/05-reportes.feature
Normal file
@@ -0,0 +1,155 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/backoffice/05-reportes.md
|
||||
# Drive: 07. Finanzas y contabilidad/Reportes
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_stats.py
|
||||
# Tests Frontend: npx playwright test admin-reports.spec.ts
|
||||
|
||||
Característica: Reportes y dashboard
|
||||
|
||||
Como administrador o gerente
|
||||
Quiero ver metricas y generar reportes
|
||||
Para tomar decisiones informadas
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como administrador
|
||||
Y estoy en el backoffice
|
||||
|
||||
# ============================================
|
||||
# DASHBOARD
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver dashboard principal
|
||||
Cuando accedo al dashboard
|
||||
Entonces deberia ver metricas resumidas:
|
||||
| metrica | periodo |
|
||||
| Solicitudes nuevas | Hoy |
|
||||
| Visitas completadas | Semana |
|
||||
| Ingresos | Mes |
|
||||
| Veterinarios activos | Actual |
|
||||
| Tasa de conversion | Mes |
|
||||
|
||||
Escenario: Dashboard actualiza periodicamente
|
||||
Dado que estoy en el dashboard
|
||||
Cuando pasan 5 minutos
|
||||
Entonces los datos deberian actualizarse
|
||||
Y deberia ver indicador de ultima actualizacion
|
||||
|
||||
Escenario: Ver grafico de tendencia
|
||||
Cuando veo el grafico de solicitudes
|
||||
Entonces deberia ver la evolucion de los ultimos 30 dias
|
||||
Y deberia poder comparar con periodo anterior
|
||||
|
||||
# ============================================
|
||||
# REPORTE DE SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Escenario: Generar reporte de solicitudes
|
||||
Cuando voy a "Reportes" -> "Solicitudes"
|
||||
Y selecciono periodo "Enero 2024"
|
||||
Y hago click en "Generar"
|
||||
Entonces deberia ver tabla con solicitudes del periodo
|
||||
Y deberia ver totales por estado:
|
||||
| estado | cantidad |
|
||||
| Pendiente | X |
|
||||
| Coordinado | X |
|
||||
| Pagado | X |
|
||||
| Completado | X |
|
||||
| Cancelado | X |
|
||||
|
||||
Escenario: Filtrar reporte por multiples criterios
|
||||
Cuando genero reporte con filtros:
|
||||
| filtro | valor |
|
||||
| Periodo | Enero 2024 |
|
||||
| Zona | Palermo |
|
||||
| Veterinario | Dra. Garcia |
|
||||
| Estado | Completado |
|
||||
Entonces deberia ver solo solicitudes que cumplan todos los criterios
|
||||
|
||||
# ============================================
|
||||
# REPORTE DE INGRESOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver ingresos por periodo
|
||||
Cuando voy a "Reportes" -> "Ingresos"
|
||||
Y selecciono "Ultimo trimestre"
|
||||
Entonces deberia ver:
|
||||
| dato |
|
||||
| Ingresos totales |
|
||||
| Cantidad de pagos |
|
||||
| Ticket promedio |
|
||||
| Reembolsos realizados |
|
||||
| Ingreso neto |
|
||||
|
||||
Escenario: Ingresos agrupados por veterinario
|
||||
Cuando agrupo el reporte por "Veterinario"
|
||||
Entonces deberia ver para cada vet:
|
||||
| dato |
|
||||
| Visitas completadas |
|
||||
| Ingresos generados |
|
||||
| Porcentaje del total |
|
||||
Y deberian estar ordenados de mayor a menor
|
||||
|
||||
Escenario: Ingresos agrupados por servicio
|
||||
Cuando agrupo el reporte por "Servicio"
|
||||
Entonces deberia ver los servicios mas vendidos
|
||||
Y deberia ver el ingreso de cada uno
|
||||
Y deberia ver la cantidad de veces realizado
|
||||
|
||||
# ============================================
|
||||
# REPORTE DE VETERINARIOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver performance de veterinarios
|
||||
Cuando voy a "Reportes" -> "Veterinarios"
|
||||
Entonces deberia ver para cada veterinario:
|
||||
| metrica |
|
||||
| Visitas completadas |
|
||||
| Calificacion promedio |
|
||||
| Tasa de aceptacion |
|
||||
| Tasa de cancelacion |
|
||||
| Ingresos generados |
|
||||
|
||||
Escenario: Identificar veterinarios con problemas
|
||||
Dado que el reporte muestra la tasa de cancelacion
|
||||
Cuando ordeno por "Tasa de cancelacion" descendente
|
||||
Entonces deberia ver primero los vets con mas cancelaciones
|
||||
Y deberia poder investigar los motivos
|
||||
|
||||
# ============================================
|
||||
# EXPORTACION
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Exportar reporte en diferentes formatos
|
||||
Dado que tengo un reporte generado
|
||||
Cuando hago click en "Exportar"
|
||||
Y selecciono formato "<formato>"
|
||||
Entonces deberia descargarse el archivo en formato <formato>
|
||||
|
||||
Ejemplos:
|
||||
| formato |
|
||||
| CSV |
|
||||
| Excel |
|
||||
| PDF |
|
||||
|
||||
Escenario: Exportar reporte grande de forma asincrona
|
||||
Dado que genere un reporte con mas de 10000 registros
|
||||
Cuando hago click en "Exportar"
|
||||
Entonces deberia ver mensaje "Generando exportacion..."
|
||||
Y deberia recibir notificacion cuando este listo
|
||||
Y deberia poder descargar desde el centro de notificaciones
|
||||
|
||||
# ============================================
|
||||
# REPORTES PROGRAMADOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Programar reporte semanal
|
||||
Cuando voy a "Reportes" -> "Programados"
|
||||
Y creo nuevo reporte programado:
|
||||
| campo | valor |
|
||||
| Tipo | Ingresos |
|
||||
| Frecuencia | Semanal |
|
||||
| Dia | Lunes |
|
||||
| Destinatarios | gerencia@ejemplo.com |
|
||||
Y guardo
|
||||
Entonces deberia enviarse el reporte cada lunes
|
||||
Y los destinatarios deberian recibirlo por email
|
||||
92
atlas/book/gherkin-samples/es/pet-owner/01-registro.feature
Normal file
92
atlas/book/gherkin-samples/es/pet-owner/01-registro.feature
Normal file
@@ -0,0 +1,92 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/pet-owner/01-registro.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/common/test_users.py
|
||||
# Tests Frontend: npx playwright test auth.spec.ts
|
||||
|
||||
Característica: Registro de usuario
|
||||
|
||||
Como visitante de la plataforma
|
||||
Quiero poder crear una cuenta
|
||||
Para gestionar mis mascotas y reservar turnos
|
||||
|
||||
# ============================================
|
||||
# CAMINO FELIZ
|
||||
# ============================================
|
||||
|
||||
Escenario: Registro exitoso con datos validos
|
||||
Dado que estoy en la pagina de registro
|
||||
Cuando ingreso email "nuevo@ejemplo.com"
|
||||
Y ingreso contraseña "Password123"
|
||||
Y confirmo contraseña "Password123"
|
||||
Y acepto los terminos y condiciones
|
||||
Y hago click en "Crear cuenta"
|
||||
Entonces deberia ver mensaje "Te enviamos un email de verificacion"
|
||||
Y deberia recibir email de verificacion
|
||||
|
||||
Escenario: Verificar email y activar cuenta
|
||||
Dado que me registre con email "nuevo@ejemplo.com"
|
||||
Y recibi el email de verificacion
|
||||
Cuando hago click en el link de verificacion
|
||||
Entonces mi cuenta deberia estar activa
|
||||
Y deberia ser redirigido al dashboard
|
||||
|
||||
# ============================================
|
||||
# VALIDACIONES
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Registro con datos invalidos
|
||||
Dado que estoy en la pagina de registro
|
||||
Cuando ingreso email "<email>"
|
||||
Y ingreso contraseña "<password>"
|
||||
Y confirmo contraseña "<confirmacion>"
|
||||
Y hago click en "Crear cuenta"
|
||||
Entonces deberia ver error "<mensaje_error>"
|
||||
|
||||
Ejemplos:
|
||||
| email | password | confirmacion | mensaje_error |
|
||||
| invalido | Password123 | Password123 | Email invalido |
|
||||
| test@test.com | 123 | 123 | Contraseña muy corta |
|
||||
| test@test.com | password | password | Debe contener al menos un numero |
|
||||
| test@test.com | Password123 | Diferente123 | Las contraseñas no coinciden |
|
||||
|
||||
Escenario: Registro con email ya existente
|
||||
Dado que existe un usuario con email "existente@ejemplo.com"
|
||||
Y estoy en la pagina de registro
|
||||
Cuando ingreso email "existente@ejemplo.com"
|
||||
Y completo el resto del formulario correctamente
|
||||
Y hago click en "Crear cuenta"
|
||||
Entonces deberia ver error "Este email ya esta registrado"
|
||||
Y deberia ver link "Recuperar contraseña"
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Registro linkea con cuenta invitado existente
|
||||
# Usuario que reservo turno como invitado y ahora quiere registrarse
|
||||
Dado que existe un usuario invitado con email "invitado@ejemplo.com"
|
||||
Y ese usuario tiene una mascota "Luna" registrada
|
||||
Y estoy en la pagina de registro
|
||||
Cuando me registro con email "invitado@ejemplo.com"
|
||||
Y verifico mi cuenta
|
||||
Entonces deberia ver mi mascota "Luna" en el dashboard
|
||||
Y deberia ver mis turnos anteriores
|
||||
|
||||
Escenario: Registro desde flujo de turnero
|
||||
# Usuario empezo a reservar turno y decide crear cuenta
|
||||
Dado que estoy en el paso final del turnero
|
||||
Y ingrese mis datos de contacto
|
||||
Cuando hago click en "Crear cuenta para guardar mis datos"
|
||||
Entonces deberia ver formulario simplificado
|
||||
Y mi email ya deberia estar pre-llenado
|
||||
Y solo deberia ingresar contraseña
|
||||
|
||||
Escenario: Reenviar email de verificacion
|
||||
Dado que me registre pero no verifique mi cuenta
|
||||
Y estoy en la pagina de login
|
||||
Cuando intento iniciar sesion
|
||||
Entonces deberia ver "Tu cuenta no esta verificada"
|
||||
Y deberia ver boton "Reenviar email"
|
||||
Cuando hago click en "Reenviar email"
|
||||
Entonces deberia recibir nuevo email de verificacion
|
||||
@@ -0,0 +1,141 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/pet-owner/02-reservar-turno.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/workflows/test_turnero_general.py
|
||||
# Tests Frontend: npx playwright test turnero.spec.ts
|
||||
# Relacionado: def/work_plan/10-flow-turnero.md
|
||||
|
||||
Característica: Reservar turno veterinario (Turnero)
|
||||
|
||||
Como dueno de mascota
|
||||
Quiero reservar un turno veterinario a domicilio
|
||||
Para que atiendan a mi mascota sin salir de casa
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy en la pagina del turnero
|
||||
|
||||
# ============================================
|
||||
# VERIFICACION DE COBERTURA
|
||||
# ============================================
|
||||
|
||||
Escenario: Verificar cobertura en zona disponible
|
||||
Cuando ingreso direccion "Av Santa Fe 1234, CABA"
|
||||
Entonces deberia ver mensaje "Tenemos cobertura en tu zona"
|
||||
Y deberia poder continuar al siguiente paso
|
||||
|
||||
Escenario: Zona sin cobertura
|
||||
Cuando ingreso direccion "Calle Principal 100, Ushuaia"
|
||||
Entonces deberia ver mensaje "Aun no tenemos cobertura en tu zona"
|
||||
Y deberia ver formulario "Avisame cuando lleguen"
|
||||
|
||||
# ============================================
|
||||
# FLUJO COMPLETO POR TIPO DE USUARIO
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Reservar turno como <tipo_usuario>
|
||||
Dado que soy un usuario <tipo_usuario>
|
||||
Y tengo cobertura en mi zona
|
||||
Cuando completo los datos de mi mascota:
|
||||
| campo | valor |
|
||||
| nombre | Luna |
|
||||
| tipo | Gato |
|
||||
| edad | 2 años |
|
||||
| castrada | Si |
|
||||
Y selecciono servicios:
|
||||
| servicio |
|
||||
| Vacunacion |
|
||||
Y selecciono fechas preferidas:
|
||||
| fecha | franja |
|
||||
| 2024-01-15 | Mañana |
|
||||
| 2024-01-16 | Tarde |
|
||||
Y completo datos de contacto con email "<email>"
|
||||
Y envio la solicitud
|
||||
Entonces deberia crearse una solicitud en estado "Pendiente"
|
||||
Y el dueno deberia ser <estado_dueno>
|
||||
Y deberia recibir email de confirmacion
|
||||
|
||||
Ejemplos:
|
||||
| tipo_usuario | email | estado_dueno |
|
||||
| invitado | nuevo@test.com | creado como invitado |
|
||||
| registrado | user@test.com | mi cuenta existente |
|
||||
| recurrente | conocido@test.com | identificado por email |
|
||||
|
||||
# ============================================
|
||||
# SELECCION DE SERVICIOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Servicios filtrados por tipo de mascota
|
||||
Dado que agregue una mascota tipo "Gato"
|
||||
Cuando veo los servicios disponibles
|
||||
Entonces deberia ver "Vacuna triple felina"
|
||||
Y deberia ver "Vacuna antirabica"
|
||||
Pero no deberia ver "Vacuna sextuple canina"
|
||||
|
||||
Escenario: Consulta clinica se agrega automaticamente con vacunacion
|
||||
Dado que estoy seleccionando servicios
|
||||
Cuando selecciono "Vacunacion"
|
||||
Entonces "Consulta clinica" deberia agregarse automaticamente
|
||||
Y deberia ver nota "Incluye revision general"
|
||||
Y no deberia poder quitar "Consulta clinica"
|
||||
|
||||
Escenario: Servicios combo con descuento
|
||||
Dado que estoy seleccionando servicios
|
||||
Cuando agrego los siguientes servicios:
|
||||
| servicio |
|
||||
| Vacunacion |
|
||||
| Desparasitacion |
|
||||
| Antipulgas |
|
||||
Entonces deberia ver "Plan preventivo completo"
|
||||
Y el total deberia incluir descuento de combo
|
||||
|
||||
Escenario: Castracion no disponible para mascota castrada
|
||||
Dado que mi mascota esta marcada como castrada
|
||||
Cuando veo los servicios disponibles
|
||||
Entonces no deberia ver "Castracion"
|
||||
|
||||
# ============================================
|
||||
# DATOS DE CONTACTO Y CUENTA
|
||||
# ============================================
|
||||
|
||||
Escenario: Pre-llenado de datos para usuario logueado
|
||||
Dado que estoy logueado como "maria@ejemplo.com"
|
||||
Y tengo registrada mascota "Firulais"
|
||||
Cuando inicio el flujo de turnero
|
||||
Entonces mi direccion deberia estar pre-llenada
|
||||
Y deberia poder seleccionar "Firulais" de mis mascotas
|
||||
Y mis datos de contacto ya deberian estar completos
|
||||
|
||||
Escenario: Detectar usuario existente por email
|
||||
Dado que soy usuario invitado
|
||||
Y existe una cuenta con email "existente@ejemplo.com"
|
||||
Cuando ingreso email "existente@ejemplo.com" en datos de contacto
|
||||
Entonces deberia ver "Ya tenes cuenta con este email"
|
||||
Y deberia ver opciones:
|
||||
| opcion |
|
||||
| Iniciar sesion |
|
||||
| Continuar como invitado |
|
||||
|
||||
# ============================================
|
||||
# EDGE CASES
|
||||
# ============================================
|
||||
|
||||
Escenario: Usuario abandona flujo a mitad
|
||||
Dado que complete los datos de mascota
|
||||
Y cerre el navegador sin enviar
|
||||
Cuando vuelvo a la pagina del turnero
|
||||
Entonces deberia poder recuperar mi progreso
|
||||
# Nota: datos guardados en localStorage o session
|
||||
|
||||
Escenario: Multiples mascotas en una solicitud
|
||||
Dado que quiero atender a 2 mascotas
|
||||
Cuando agrego mascota "Luna" tipo "Gato"
|
||||
Y agrego mascota "Rocky" tipo "Perro"
|
||||
Y selecciono servicios para cada una
|
||||
Entonces deberia crearse una solicitud con 2 mascotas
|
||||
Y el precio deberia reflejar ambas
|
||||
|
||||
Escenario: Franja horaria especifica
|
||||
Dado que solo puedo por la mañana
|
||||
Cuando selecciono franja "Mañana (9-12hs)"
|
||||
Entonces la solicitud deberia registrar esa preferencia
|
||||
# Nota: Es preferencia, no garantia
|
||||
@@ -0,0 +1,154 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/pet-owner/03-gestion-mascotas.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_pets.py
|
||||
# Tests Frontend: npx playwright test pets.spec.ts
|
||||
|
||||
Característica: Gestion de mascotas
|
||||
|
||||
Como dueno de mascota registrado
|
||||
Quiero gestionar la informacion de mis mascotas
|
||||
Para tenerla actualizada y acceder a su historial medico
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como dueno de mascota
|
||||
Y estoy en la seccion "Mis mascotas"
|
||||
|
||||
# ============================================
|
||||
# AGREGAR MASCOTA
|
||||
# ============================================
|
||||
|
||||
Escenario: Agregar mascota con datos minimos
|
||||
Cuando hago click en "Agregar mascota"
|
||||
Y completo el formulario:
|
||||
| campo | valor |
|
||||
| nombre | Luna |
|
||||
| tipo | Gato |
|
||||
Y hago click en "Guardar"
|
||||
Entonces deberia ver "Luna" en mi lista de mascotas
|
||||
Y Luna deberia aparecer disponible en el turnero
|
||||
|
||||
Escenario: Agregar mascota con datos completos
|
||||
Cuando hago click en "Agregar mascota"
|
||||
Y completo el formulario:
|
||||
| campo | valor |
|
||||
| nombre | Rocky |
|
||||
| tipo | Perro |
|
||||
| raza | Labrador |
|
||||
| fecha_nacimiento | 2020-03-15 |
|
||||
| peso | 25 |
|
||||
| sexo | Macho |
|
||||
| castrado | Si |
|
||||
Y subo una foto de Rocky
|
||||
Y hago click en "Guardar"
|
||||
Entonces deberia ver "Rocky" con su foto en mi lista
|
||||
Y deberia ver badge "Castrado"
|
||||
|
||||
Esquema del escenario: Validacion de datos de mascota
|
||||
Cuando intento agregar mascota con <campo> igual a "<valor>"
|
||||
Entonces deberia ver error "<mensaje>"
|
||||
|
||||
Ejemplos:
|
||||
| campo | valor | mensaje |
|
||||
| nombre | | El nombre es obligatorio |
|
||||
| nombre | A | Nombre muy corto |
|
||||
| tipo | | Selecciona el tipo |
|
||||
| peso | -5 | El peso debe ser positivo |
|
||||
| peso | 500 | Peso fuera de rango |
|
||||
|
||||
# ============================================
|
||||
# EDITAR MASCOTA
|
||||
# ============================================
|
||||
|
||||
Escenario: Editar peso de mascota
|
||||
Dado que tengo una mascota "Luna" con peso 4kg
|
||||
Cuando edito a Luna
|
||||
Y cambio el peso a 5kg
|
||||
Y guardo los cambios
|
||||
Entonces Luna deberia mostrar peso "5 kg"
|
||||
|
||||
Escenario: Marcar mascota como castrada
|
||||
Dado que tengo una mascota "Rocky" no castrado
|
||||
Cuando edito a Rocky
|
||||
Y marco "Esta castrado"
|
||||
Y guardo los cambios
|
||||
Entonces Rocky deberia mostrar badge "Castrado"
|
||||
Y el servicio "Castracion" no deberia aparecer para Rocky en el turnero
|
||||
|
||||
Escenario: Actualizar foto de mascota
|
||||
Dado que tengo una mascota "Luna" sin foto
|
||||
Cuando edito a Luna
|
||||
Y subo una nueva foto
|
||||
Y guardo los cambios
|
||||
Entonces deberia ver la foto de Luna en su tarjeta
|
||||
|
||||
# ============================================
|
||||
# ELIMINAR MASCOTA
|
||||
# ============================================
|
||||
|
||||
Escenario: Eliminar mascota sin historial
|
||||
Dado que tengo una mascota "Nuevo" sin visitas
|
||||
Cuando hago click en "Eliminar" para Nuevo
|
||||
Y confirmo la eliminacion
|
||||
Entonces Nuevo no deberia aparecer en mi lista
|
||||
# Nota: Es soft delete
|
||||
|
||||
Escenario: Eliminar mascota con historial medico
|
||||
Dado que tengo una mascota "Luna" con visitas anteriores
|
||||
Cuando hago click en "Eliminar" para Luna
|
||||
Entonces deberia ver advertencia "Luna tiene historial medico"
|
||||
Y deberia ver "El historial se conservara pero no podras verlo"
|
||||
Cuando confirmo la eliminacion
|
||||
Entonces Luna no deberia aparecer en mi lista
|
||||
|
||||
Escenario: No puedo eliminar mascota con turno pendiente
|
||||
Dado que tengo una mascota "Rocky" con turno pendiente
|
||||
Cuando intento eliminar a Rocky
|
||||
Entonces deberia ver error "Rocky tiene turnos pendientes"
|
||||
Y deberia ver sugerencia "Cancela los turnos primero"
|
||||
|
||||
# ============================================
|
||||
# VER HISTORIAL MEDICO
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver historial de visitas de mascota
|
||||
Dado que tengo una mascota "Luna" con 3 visitas completadas
|
||||
Cuando hago click en Luna
|
||||
Y voy a la seccion "Historial"
|
||||
Entonces deberia ver 3 visitas listadas
|
||||
Y deberian estar ordenadas por fecha descendente
|
||||
|
||||
Escenario: Ver detalle de visita
|
||||
Dado que tengo una mascota "Luna" con visitas
|
||||
Cuando veo el historial de Luna
|
||||
Y hago click en la primera visita
|
||||
Entonces deberia ver:
|
||||
| campo |
|
||||
| Fecha |
|
||||
| Veterinario |
|
||||
| Diagnostico |
|
||||
| Tratamiento |
|
||||
| Medicamentos |
|
||||
|
||||
Escenario: Mascota sin historial
|
||||
Dado que tengo una mascota "Nuevo" recien agregada
|
||||
Cuando veo el perfil de Nuevo
|
||||
Entonces la seccion "Historial" deberia estar vacia
|
||||
Y deberia ver mensaje "Aun no hay visitas registradas"
|
||||
Y deberia ver boton "Reservar primer turno"
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Mascota heredada de cuenta invitado
|
||||
Dado que me registre con email "juan@test.com"
|
||||
Y previamente reserve turno como invitado para "Firulais"
|
||||
Cuando voy a "Mis mascotas"
|
||||
Entonces deberia ver "Firulais" en mi lista
|
||||
Y deberia ver su historial de visitas previas
|
||||
|
||||
Escenario: Razas filtradas por tipo
|
||||
Cuando agrego una mascota tipo "Gato"
|
||||
Entonces las razas disponibles deberian ser razas de gato
|
||||
Y no deberia ver razas de perro como "Labrador"
|
||||
141
atlas/book/gherkin-samples/es/pet-owner/04-pago-turno.feature
Normal file
141
atlas/book/gherkin-samples/es/pet-owner/04-pago-turno.feature
Normal file
@@ -0,0 +1,141 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/pet-owner/04-pago-turno.md
|
||||
# Drive: 07. Finanzas y contabilidad/Mercado Pago
|
||||
# Tests Backend: pytest tests/contracts/payments/test_mercadopago.py
|
||||
# Tests Frontend: npx playwright test payment.spec.ts
|
||||
|
||||
Característica: Pago de turno
|
||||
|
||||
Como dueno de mascota con turno coordinado
|
||||
Quiero pagar mi turno online
|
||||
Para confirmar la visita del veterinario
|
||||
|
||||
Antecedentes:
|
||||
Dado que tengo un turno en estado "Coordinado"
|
||||
Y el turno tiene asignado veterinario "Dra. Garcia"
|
||||
Y la fecha asignada es "15 de enero a las 10:00"
|
||||
|
||||
# ============================================
|
||||
# FLUJO DE PAGO EXITOSO
|
||||
# ============================================
|
||||
|
||||
Escenario: Pagar turno con tarjeta de credito
|
||||
Dado que estoy en el detalle de mi turno coordinado
|
||||
Cuando hago click en "Pagar"
|
||||
Entonces deberia ser redirigido a Mercado Pago
|
||||
Y deberia ver el monto correcto
|
||||
|
||||
Cuando selecciono "Tarjeta de credito"
|
||||
Y completo los datos de la tarjeta
|
||||
Y confirmo el pago
|
||||
Entonces deberia volver a la plataforma
|
||||
Y deberia ver "Pago exitoso"
|
||||
Y el turno deberia estar en estado "Pagado"
|
||||
|
||||
Escenario: Recibir confirmacion de pago
|
||||
Dado que complete el pago exitosamente
|
||||
Entonces deberia recibir email de confirmacion
|
||||
Y el email deberia contener:
|
||||
| campo |
|
||||
| Fecha del turno |
|
||||
| Direccion |
|
||||
| Veterinario asignado |
|
||||
| Monto pagado |
|
||||
| Numero de operacion |
|
||||
|
||||
# ============================================
|
||||
# METODOS DE PAGO
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Pagar con diferentes metodos
|
||||
Dado que estoy en Mercado Pago
|
||||
Cuando selecciono metodo "<metodo>"
|
||||
Y completo el pago
|
||||
Entonces el pago deberia ser <resultado>
|
||||
Y el estado de acreditacion deberia ser "<acreditacion>"
|
||||
|
||||
Ejemplos:
|
||||
| metodo | resultado | acreditacion |
|
||||
| Tarjeta credito | exitoso | inmediata |
|
||||
| Tarjeta debito | exitoso | inmediata |
|
||||
| Dinero en cuenta | exitoso | inmediata |
|
||||
| Transferencia | pendiente | 1-2 dias |
|
||||
| Rapipago | pendiente | hasta 24hs |
|
||||
|
||||
# ============================================
|
||||
# MANEJO DE ERRORES
|
||||
# ============================================
|
||||
|
||||
Escenario: Pago rechazado por fondos insuficientes
|
||||
Dado que estoy en Mercado Pago
|
||||
Cuando intento pagar con tarjeta sin fondos
|
||||
Entonces deberia ver error "Fondos insuficientes"
|
||||
Y deberia poder reintentar con otra tarjeta
|
||||
Y el turno deberia seguir en estado "Coordinado"
|
||||
|
||||
Escenario: Usuario cancela el pago
|
||||
Dado que estoy en Mercado Pago
|
||||
Cuando hago click en "Volver al sitio"
|
||||
Entonces deberia volver a la plataforma
|
||||
Y deberia ver mensaje "El pago fue cancelado"
|
||||
Y deberia ver boton "Reintentar pago"
|
||||
Y el turno deberia seguir en estado "Coordinado"
|
||||
|
||||
Escenario: Cierre de browser durante pago
|
||||
Dado que estoy en Mercado Pago
|
||||
Y cierro el navegador accidentalmente
|
||||
Cuando vuelvo a la plataforma
|
||||
Y voy a "Mis turnos"
|
||||
Entonces deberia poder ver el estado real del pago
|
||||
# Si se proceso: Pagado. Si no: Coordinado con opcion de pagar
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Link de pago expirado
|
||||
Dado que recibi el link de pago hace mas de 24 horas
|
||||
Cuando hago click en el link
|
||||
Entonces deberia ver "Este link ha expirado"
|
||||
Y deberia ver "Contacta a soporte para generar uno nuevo"
|
||||
|
||||
Escenario: Intento pagar turno ya pagado
|
||||
Dado que mi turno ya esta en estado "Pagado"
|
||||
Cuando accedo al link de pago
|
||||
Entonces deberia ver "Este turno ya fue pagado"
|
||||
Y deberia ver boton "Ver detalle del turno"
|
||||
|
||||
Escenario: Precio cambio desde la coordinacion
|
||||
# Caso muy raro pero posible
|
||||
Dado que el precio del servicio aumento desde que se coordino
|
||||
Cuando voy a pagar
|
||||
Entonces deberia ver el precio original acordado
|
||||
# El precio se congela al momento de coordinacion
|
||||
|
||||
# ============================================
|
||||
# INTEGRACION CON WEBHOOK
|
||||
# ============================================
|
||||
|
||||
Regla: El estado del turno se actualiza via webhook de Mercado Pago
|
||||
|
||||
Escenario: Webhook confirma pago aprobado
|
||||
Dado que el usuario completo el pago en Mercado Pago
|
||||
Cuando Mercado Pago envia webhook con status "approved"
|
||||
Entonces el turno deberia cambiar a estado "Pagado"
|
||||
Y el veterinario deberia recibir notificacion
|
||||
Y el usuario deberia recibir email de confirmacion
|
||||
|
||||
Escenario: Webhook informa pago pendiente
|
||||
Dado que el usuario pago con transferencia bancaria
|
||||
Cuando Mercado Pago envia webhook con status "pending"
|
||||
Entonces el turno deberia quedarse en "Coordinado"
|
||||
Y deberia registrarse el pago pendiente
|
||||
Y el usuario deberia recibir email "Esperando acreditacion"
|
||||
|
||||
Escenario: Webhook falla pero pago se proceso
|
||||
# Caso de error que requiere intervencion manual
|
||||
Dado que el usuario pago exitosamente
|
||||
Pero el webhook fallo por error de red
|
||||
Entonces el turno seguira en "Coordinado"
|
||||
Y el equipo de ops deberia recibir alerta
|
||||
Y deberian poder actualizar manualmente
|
||||
@@ -0,0 +1,148 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/pet-owner/05-historial-medico.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_visits.py
|
||||
# Tests Frontend: npx playwright test medical-history.spec.ts
|
||||
|
||||
Característica: Ver historial medico
|
||||
|
||||
Como dueno de mascota
|
||||
Quiero ver el historial medico de mis mascotas
|
||||
Para tener registro de sus visitas y tratamientos
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como dueno de mascota
|
||||
Y tengo una mascota "Luna" con visitas completadas
|
||||
|
||||
# ============================================
|
||||
# ACCESO AL HISTORIAL
|
||||
# ============================================
|
||||
|
||||
Escenario: Acceder al historial desde mascotas
|
||||
Dado que estoy en "Mis mascotas"
|
||||
Cuando hago click en "Luna"
|
||||
Y voy a la seccion "Historial de visitas"
|
||||
Entonces deberia ver lista de visitas de Luna
|
||||
|
||||
Escenario: Acceder al historial desde turnos
|
||||
Dado que estoy en "Mis turnos"
|
||||
Y filtro por "Completados"
|
||||
Cuando hago click en un turno de Luna
|
||||
Y hago click en "Ver informe medico"
|
||||
Entonces deberia ver el informe de esa visita
|
||||
|
||||
# ============================================
|
||||
# VISUALIZACION DE VISITAS
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver lista de visitas ordenadas
|
||||
Dado que Luna tiene visitas en las siguientes fechas:
|
||||
| fecha |
|
||||
| 2024-01-15 |
|
||||
| 2023-11-20 |
|
||||
| 2023-06-10 |
|
||||
Cuando veo el historial de Luna
|
||||
Entonces deberia ver las visitas ordenadas de mas reciente a mas antigua
|
||||
Y la primera deberia ser del "15 de enero 2024"
|
||||
|
||||
Escenario: Ver detalle completo de informe
|
||||
Cuando hago click en una visita completada
|
||||
Entonces deberia ver los siguientes datos:
|
||||
| seccion | contenido |
|
||||
| Informacion | Fecha, veterinario, servicios |
|
||||
| Examen fisico | Peso, temperatura, FC, obs |
|
||||
| Diagnostico | Descripcion del diagnostico |
|
||||
| Tratamiento | Plan de tratamiento |
|
||||
| Medicamentos | Lista con dosis y frecuencia |
|
||||
| Estudios | Estudios solicitados y estado |
|
||||
| Seguimiento | Recomendaciones, proximo control |
|
||||
|
||||
Escenario: Ver informe con medicamentos recetados
|
||||
Dado que la visita incluye medicamentos:
|
||||
"""
|
||||
1. Amoxicilina 250mg - 1 comprimido cada 12hs por 7 dias
|
||||
2. Meloxicam 1.5mg - 1 comprimido por dia por 3 dias
|
||||
"""
|
||||
Cuando veo el informe
|
||||
Entonces deberia ver la lista de medicamentos
|
||||
Y cada medicamento deberia mostrar:
|
||||
| campo |
|
||||
| Nombre |
|
||||
| Dosis |
|
||||
| Frecuencia |
|
||||
| Duracion |
|
||||
|
||||
# ============================================
|
||||
# DESCARGA Y COMPARTIR
|
||||
# ============================================
|
||||
|
||||
Escenario: Descargar informe como PDF
|
||||
Dado que estoy viendo un informe medico
|
||||
Cuando hago click en "Descargar PDF"
|
||||
Entonces deberia descargarse un archivo PDF
|
||||
Y el PDF deberia contener toda la informacion del informe
|
||||
Y deberia tener el logo de Amar Mascotas
|
||||
|
||||
Escenario: Imprimir informe
|
||||
Dado que estoy viendo un informe medico
|
||||
Cuando hago click en "Imprimir"
|
||||
Entonces deberia abrirse el dialogo de impresion
|
||||
Y el formato deberia ser optimizado para impresion
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Visita completada sin informe cargado
|
||||
# El vet no cargo el informe todavia
|
||||
Dado que Luna tiene una visita marcada como "Completada"
|
||||
Pero el veterinario no cargo el informe aun
|
||||
Cuando intento ver el informe
|
||||
Entonces deberia ver mensaje "Informe pendiente"
|
||||
Y deberia ver "El veterinario esta completando el informe"
|
||||
|
||||
Escenario: Informe con estudios pendientes
|
||||
Dado que el informe solicita estudios de laboratorio
|
||||
Y los resultados aun no estan disponibles
|
||||
Cuando veo el informe
|
||||
Entonces deberia ver seccion "Estudios solicitados"
|
||||
Y deberia ver estado "Pendiente de resultados"
|
||||
Y deberia ver nota "Se notificara cuando esten listos"
|
||||
|
||||
Escenario: Mascota con multiples visitas el mismo dia
|
||||
# Raro pero posible: consulta de urgencia + seguimiento
|
||||
Dado que Luna tuvo 2 visitas el 15 de enero
|
||||
Cuando veo el historial
|
||||
Entonces deberia ver ambas visitas listadas
|
||||
Y deberian estar diferenciadas por hora
|
||||
|
||||
# ============================================
|
||||
# PERMISOS Y PRIVACIDAD
|
||||
# ============================================
|
||||
|
||||
Regla: Solo el dueno puede ver el historial de su mascota
|
||||
|
||||
Escenario: Dueno ve historial de su mascota
|
||||
Dado que soy dueno de Luna
|
||||
Cuando accedo al historial de Luna
|
||||
Entonces deberia poder verlo completo
|
||||
|
||||
Escenario: No puedo ver historial de mascota ajena
|
||||
Dado que existe mascota "Rocky" de otro dueno
|
||||
Cuando intento acceder al historial de Rocky
|
||||
Entonces deberia ver error "No tienes acceso"
|
||||
# 403 Forbidden
|
||||
|
||||
# ============================================
|
||||
# BUSQUEDA Y FILTROS
|
||||
# ============================================
|
||||
|
||||
Escenario: Filtrar historial por tipo de servicio
|
||||
Dado que Luna tiene visitas de vacunacion y consulta
|
||||
Cuando filtro por "Vacunacion"
|
||||
Entonces solo deberia ver visitas de vacunacion
|
||||
|
||||
Escenario: Buscar en historial por fecha
|
||||
Cuando busco visitas del "2023"
|
||||
Entonces solo deberia ver visitas del 2023
|
||||
Y no deberia ver visitas del 2024
|
||||
@@ -0,0 +1,129 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/veterinarian/01-aceptar-solicitud.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/solicitudes/test_service_requests.py
|
||||
# Tests Frontend: npx playwright test vet-requests.spec.ts
|
||||
|
||||
Característica: Aceptar o rechazar solicitudes de servicio
|
||||
|
||||
Como veterinario de la plataforma
|
||||
Quiero revisar y responder a solicitudes en mi zona
|
||||
Para gestionar mi agenda de visitas a domicilio
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como veterinario
|
||||
Y tengo cobertura en los barrios "Palermo" y "Recoleta"
|
||||
Y estoy en el dashboard de veterinario
|
||||
|
||||
# ============================================
|
||||
# VER SOLICITUDES PENDIENTES
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver lista de solicitudes en mi zona
|
||||
Cuando veo la seccion "Solicitudes pendientes"
|
||||
Entonces deberia ver solo solicitudes de "Palermo" y "Recoleta"
|
||||
Y no deberia ver solicitudes de otros barrios
|
||||
|
||||
Escenario: Ver detalle de solicitud
|
||||
Dado que hay una solicitud pendiente
|
||||
Cuando hago click en la solicitud
|
||||
Entonces deberia ver:
|
||||
| seccion | contenido |
|
||||
| Dueno | Nombre, telefono, direccion |
|
||||
| Mascota | Nombre, tipo, edad, foto |
|
||||
| Servicios | Lista de servicios solicitados |
|
||||
| Fechas | Fechas preferidas por el dueno |
|
||||
| Historial | Visitas anteriores si las hay |
|
||||
|
||||
# ============================================
|
||||
# ACEPTAR SOLICITUD
|
||||
# ============================================
|
||||
|
||||
Escenario: Aceptar solicitud con fecha disponible
|
||||
Dado que hay una solicitud para el barrio "Palermo"
|
||||
Y el dueno prefiere fechas:
|
||||
| fecha | franja |
|
||||
| 2024-01-15 | Mañana |
|
||||
| 2024-01-16 | Tarde |
|
||||
Y tengo disponibilidad el 15 de enero a las 10:00
|
||||
Cuando hago click en "Aceptar"
|
||||
Y selecciono fecha "15 de enero" hora "10:00"
|
||||
Y confirmo la aceptacion
|
||||
Entonces la solicitud deberia pasar a estado "Coordinado"
|
||||
Y deberia quedar asignada a mi
|
||||
Y el dueno deberia recibir notificacion con mis datos
|
||||
Y la visita deberia aparecer en mi agenda
|
||||
|
||||
Escenario: Aceptar solicitud con datos de mascota que ya atendi
|
||||
Dado que hay una solicitud para mascota "Luna"
|
||||
Y yo atendi a "Luna" anteriormente
|
||||
Cuando veo el detalle de la solicitud
|
||||
Entonces deberia ver badge "Paciente recurrente"
|
||||
Y deberia ver el historial de mis visitas anteriores a Luna
|
||||
|
||||
# ============================================
|
||||
# RECHAZAR SOLICITUD
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Rechazar solicitud con motivo
|
||||
Dado que hay una solicitud pendiente
|
||||
Cuando hago click en "Rechazar"
|
||||
Y selecciono motivo "<motivo>"
|
||||
Y confirmo el rechazo
|
||||
Entonces la solicitud deberia desaparecer de mi lista
|
||||
Y deberia seguir visible para otros veterinarios
|
||||
|
||||
Ejemplos:
|
||||
| motivo |
|
||||
| No tengo disponibilidad |
|
||||
| Fuera de mi zona |
|
||||
| No realizo este servicio |
|
||||
| Otro |
|
||||
|
||||
Escenario: Rechazar sin seleccionar motivo
|
||||
Dado que hay una solicitud pendiente
|
||||
Cuando hago click en "Rechazar"
|
||||
Y confirmo sin seleccionar motivo
|
||||
Entonces deberia poder rechazar igual
|
||||
# El motivo es opcional
|
||||
|
||||
# ============================================
|
||||
# RACE CONDITIONS
|
||||
# ============================================
|
||||
|
||||
Regla: Solo un veterinario puede aceptar cada solicitud
|
||||
|
||||
Escenario: Otro vet acepta mientras estoy viendo
|
||||
Dado que estoy viendo el detalle de una solicitud
|
||||
Y otro veterinario acepta la misma solicitud
|
||||
Cuando intento aceptarla
|
||||
Entonces deberia ver error "Esta solicitud ya fue aceptada"
|
||||
Y deberia ser redirigido al listado
|
||||
|
||||
Escenario: Solicitud desaparece cuando otro la acepta
|
||||
Dado que estoy viendo el listado de solicitudes
|
||||
Y otro veterinario acepta una solicitud
|
||||
Entonces esa solicitud deberia desaparecer de mi lista
|
||||
# Idealmente en tiempo real via websocket
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Solicitud urgente destacada
|
||||
Dado que hay una solicitud marcada como "Urgente"
|
||||
Cuando veo el listado de solicitudes
|
||||
Entonces deberia ver la solicitud con indicador de urgencia
|
||||
Y deberia aparecer primero en la lista
|
||||
|
||||
Escenario: Solicitud con multiples mascotas
|
||||
Dado que hay una solicitud para 2 mascotas
|
||||
Cuando veo el detalle
|
||||
Entonces deberia ver informacion de ambas mascotas
|
||||
Y deberia ver nota sobre tiempo estimado adicional
|
||||
|
||||
Escenario: No puedo aceptar con agenda completa
|
||||
Dado que tengo mi agenda completa para las fechas de la solicitud
|
||||
Cuando intento aceptar la solicitud
|
||||
Entonces deberia ver advertencia "No tienes disponibilidad en las fechas preferidas"
|
||||
Y deberia poder proponer una fecha alternativa
|
||||
@@ -0,0 +1,134 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/veterinarian/02-gestion-agenda.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_availability.py
|
||||
# Tests Frontend: npx playwright test vet-schedule.spec.ts
|
||||
|
||||
Característica: Gestion de agenda veterinaria
|
||||
|
||||
Como veterinario
|
||||
Quiero gestionar mi calendario y disponibilidad
|
||||
Para organizar mis visitas a domicilio eficientemente
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como veterinario
|
||||
Y estoy en la seccion "Mi agenda"
|
||||
|
||||
# ============================================
|
||||
# VER AGENDA
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver calendario semanal
|
||||
Cuando veo mi agenda en vista semanal
|
||||
Entonces deberia ver los 7 dias de la semana
|
||||
Y deberia ver mis visitas programadas
|
||||
Y cada visita deberia mostrar hora y nombre del paciente
|
||||
|
||||
Escenario: Ver visitas con codigo de color por estado
|
||||
Dado que tengo visitas en diferentes estados
|
||||
Cuando veo mi agenda
|
||||
Entonces las visitas deberian mostrarse con colores:
|
||||
| estado | color |
|
||||
| Coordinado | amarillo |
|
||||
| Pagado | verde |
|
||||
| En progreso | azul |
|
||||
| Completado | gris |
|
||||
|
||||
Escenario: Ver detalle de visita desde el calendario
|
||||
Dado que tengo una visita programada para hoy
|
||||
Cuando hago click en la visita
|
||||
Entonces deberia ver popup con:
|
||||
| campo | valor |
|
||||
| Hora | 10:00 - 11:00 |
|
||||
| Dueno | Maria Garcia |
|
||||
| Direccion | Av Santa Fe 1234 |
|
||||
| Mascota | Luna (Gato, 3 años) |
|
||||
| Servicios | Vacunacion, Consulta |
|
||||
| Estado pago | Pagado |
|
||||
Y deberia ver boton "Ver detalle completo"
|
||||
|
||||
# ============================================
|
||||
# CONFIGURAR DISPONIBILIDAD SEMANAL
|
||||
# ============================================
|
||||
|
||||
Escenario: Configurar horario laboral
|
||||
Cuando voy a "Configuracion" -> "Mi disponibilidad"
|
||||
Y configuro mi horario:
|
||||
| dia | trabajo | desde | hasta | pausa_desde | pausa_hasta |
|
||||
| Lunes | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Martes | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Miercoles | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Jueves | Si | 09:00 | 18:00 | 13:00 | 14:00 |
|
||||
| Viernes | Si | 09:00 | 15:00 | | |
|
||||
| Sabado | No | | | | |
|
||||
| Domingo | No | | | | |
|
||||
Y guardo la configuracion
|
||||
Entonces mi disponibilidad deberia actualizarse
|
||||
Y no deberia recibir solicitudes fuera de ese horario
|
||||
|
||||
# ============================================
|
||||
# BLOQUEAR DIAS ESPECIFICOS
|
||||
# ============================================
|
||||
|
||||
Escenario: Marcar dia como no disponible
|
||||
Dado que necesito el 20 de enero libre
|
||||
Cuando hago click en el dia 20 de enero en el calendario
|
||||
Y selecciono "Marcar como no disponible"
|
||||
Y ingreso motivo "Vacaciones"
|
||||
Y confirmo
|
||||
Entonces el dia 20 deberia mostrarse como bloqueado
|
||||
Y no deberia poder aceptar solicitudes para ese dia
|
||||
|
||||
Escenario: Bloquear rango de fechas
|
||||
Dado que tomo vacaciones del 15 al 22 de enero
|
||||
Cuando voy a "Agregar indisponibilidad"
|
||||
Y selecciono fecha inicio "15 de enero"
|
||||
Y selecciono fecha fin "22 de enero"
|
||||
Y ingreso motivo "Vacaciones"
|
||||
Y confirmo
|
||||
Entonces todos esos dias deberian mostrarse como bloqueados
|
||||
|
||||
Escenario: Desbloquear dia
|
||||
Dado que tengo el 20 de enero bloqueado
|
||||
Y ya no necesito ese dia libre
|
||||
Cuando hago click en el dia bloqueado
|
||||
Y selecciono "Quitar bloqueo"
|
||||
Entonces el dia deberia estar disponible nuevamente
|
||||
|
||||
# ============================================
|
||||
# IMPACTO EN SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Regla: Los bloqueos no afectan visitas ya aceptadas
|
||||
|
||||
Escenario: Bloquear dia con visita ya aceptada
|
||||
Dado que tengo una visita aceptada para el 15 de enero
|
||||
Cuando intento bloquear el 15 de enero
|
||||
Entonces deberia ver advertencia "Tienes una visita programada ese dia"
|
||||
Y deberia poder elegir:
|
||||
| opcion |
|
||||
| Cancelar el bloqueo |
|
||||
| Bloquear y contactar al dueno |
|
||||
|
||||
Regla: Cambios de disponibilidad solo afectan solicitudes futuras
|
||||
|
||||
Escenario: Cambiar horario no afecta visitas existentes
|
||||
Dado que tengo una visita a las 17:00 el lunes
|
||||
Cuando cambio mi horario del lunes para terminar a las 16:00
|
||||
Y guardo los cambios
|
||||
Entonces la visita de las 17:00 deberia mantenerse
|
||||
Y deberia ver advertencia sobre la inconsistencia
|
||||
|
||||
# ============================================
|
||||
# NAVEGACION
|
||||
# ============================================
|
||||
|
||||
Escenario: Navegar entre semanas
|
||||
Cuando hago click en "Semana siguiente"
|
||||
Entonces deberia ver el calendario de la proxima semana
|
||||
Y deberia poder volver con "Semana anterior"
|
||||
|
||||
Escenario: Ir a fecha especifica
|
||||
Cuando hago click en el selector de fecha
|
||||
Y selecciono "15 de marzo"
|
||||
Entonces deberia ver la semana que contiene el 15 de marzo
|
||||
@@ -0,0 +1,159 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/veterinarian/03-realizar-visita.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_visits.py
|
||||
# Tests Frontend: npx playwright test vet-visit.spec.ts
|
||||
|
||||
Característica: Realizar visita y crear informe medico
|
||||
|
||||
Como veterinario
|
||||
Quiero registrar la atencion medica de mis visitas
|
||||
Para mantener el historial clinico del paciente
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como veterinario
|
||||
Y tengo una visita programada para hoy con mascota "Luna"
|
||||
Y la visita esta en estado "Pagado"
|
||||
|
||||
# ============================================
|
||||
# INICIAR VISITA
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver informacion antes de la visita
|
||||
Cuando accedo al detalle de la visita
|
||||
Entonces deberia ver la direccion para llegar
|
||||
Y deberia ver el telefono del dueno para confirmar
|
||||
Y deberia ver los servicios a realizar
|
||||
Y deberia ver el historial previo de Luna
|
||||
|
||||
Escenario: Iniciar visita al llegar
|
||||
Dado que llegue al domicilio
|
||||
Cuando hago click en "Iniciar visita"
|
||||
Entonces el estado deberia cambiar a "En progreso"
|
||||
Y deberia registrarse la hora de inicio
|
||||
Y deberia habilitarse el boton "Crear informe"
|
||||
|
||||
# ============================================
|
||||
# CREAR INFORME MEDICO
|
||||
# ============================================
|
||||
|
||||
Escenario: Completar informe con examen fisico
|
||||
Dado que la visita esta en progreso
|
||||
Cuando voy a "Crear informe"
|
||||
Y completo el examen fisico:
|
||||
| campo | valor |
|
||||
| Peso | 4.5 kg |
|
||||
| Temperatura | 38.5 °C |
|
||||
| Frecuencia cardiaca | 120 lpm |
|
||||
| Frecuencia resp | 25 rpm |
|
||||
| Mucosas | Rosadas |
|
||||
| Hidratacion | Normal |
|
||||
Y guardo el informe
|
||||
Entonces el examen fisico deberia guardarse
|
||||
|
||||
Escenario: Agregar diagnostico y tratamiento
|
||||
Dado que complete el examen fisico
|
||||
Cuando agrego el diagnostico:
|
||||
"""
|
||||
Otitis externa bilateral leve.
|
||||
Paciente presenta prurito y secrecion ceruminosa.
|
||||
Sin signos de infeccion secundaria.
|
||||
"""
|
||||
Y agrego el tratamiento:
|
||||
"""
|
||||
Limpieza de oidos con solucion fisiologica.
|
||||
Aplicacion de gotas oticas antibioticas.
|
||||
Control en 7 dias.
|
||||
"""
|
||||
Y guardo el informe
|
||||
Entonces el diagnostico y tratamiento deberian guardarse
|
||||
|
||||
Escenario: Recetar medicamentos
|
||||
Dado que estoy creando el informe
|
||||
Cuando agrego medicamentos:
|
||||
| nombre | dosis | frecuencia | duracion |
|
||||
| Otomax gotas | 5 gotas | cada 12 horas | 7 dias |
|
||||
| Meloxicam 1.5mg | 1 comp | cada 24 horas | 3 dias |
|
||||
Y guardo el informe
|
||||
Entonces los medicamentos deberian aparecer en el informe
|
||||
Y el dueno podra verlos desde su cuenta
|
||||
|
||||
Escenario: Solicitar estudios complementarios
|
||||
Dado que necesito estudios de laboratorio
|
||||
Cuando agrego estudio:
|
||||
| tipo | indicaciones |
|
||||
| Hemograma completo | Ayuno de 8 horas |
|
||||
| Perfil hepatico | Para control pre-quirurgico |
|
||||
Y guardo el informe
|
||||
Entonces los estudios deberian quedar registrados
|
||||
Y deberian mostrarse como "Pendientes de resultado"
|
||||
|
||||
# ============================================
|
||||
# COMPLETAR VISITA
|
||||
# ============================================
|
||||
|
||||
Escenario: Completar visita con informe
|
||||
Dado que el informe esta completo
|
||||
Cuando hago click en "Completar visita"
|
||||
Y confirmo la finalizacion
|
||||
Entonces la visita deberia pasar a estado "Completado"
|
||||
Y deberia generarse factura electronica (AFIP)
|
||||
Y el dueno deberia recibir notificacion
|
||||
Y el informe deberia ser visible para el dueno
|
||||
|
||||
Escenario: No puedo completar sin informe
|
||||
Dado que la visita esta en progreso
|
||||
Pero no cree ningun informe
|
||||
Cuando intento completar la visita
|
||||
Entonces deberia ver error "Debes crear el informe antes de completar"
|
||||
|
||||
# ============================================
|
||||
# GUARDADO AUTOMATICO
|
||||
# ============================================
|
||||
|
||||
Regla: El informe se guarda automaticamente
|
||||
|
||||
Escenario: Guardado automatico cada 30 segundos
|
||||
Dado que estoy escribiendo el informe
|
||||
Cuando pasan 30 segundos
|
||||
Entonces deberia ver indicador "Guardado automaticamente"
|
||||
|
||||
Escenario: Recuperar informe despues de desconexion
|
||||
Dado que estaba escribiendo el informe
|
||||
Y perdi conexion a internet
|
||||
Cuando recupero la conexion
|
||||
Entonces deberia recuperar mi progreso
|
||||
Y no deberia perder lo que escribi
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Visita con multiples mascotas
|
||||
Dado que la visita incluye a "Luna" y "Rocky"
|
||||
Cuando creo el informe
|
||||
Entonces deberia crear un informe separado para cada mascota
|
||||
Y ambos informes deberian estar asociados a la misma visita
|
||||
|
||||
Escenario: Cancelar visita in situ
|
||||
Dado que llegue al domicilio
|
||||
Pero el dueno no esta
|
||||
Cuando marco la visita como "Cancelada in situ"
|
||||
Y selecciono motivo "Dueno ausente"
|
||||
Entonces la visita deberia marcarse como cancelada
|
||||
Y deberia generarse cargo por visita fallida
|
||||
# Segun politicas de la empresa
|
||||
|
||||
Escenario: Emergencia durante visita rutinaria
|
||||
Dado que estoy en una consulta de vacunacion
|
||||
Y detecto una condicion que requiere atencion urgente
|
||||
Cuando agrego servicio "Consulta de urgencia"
|
||||
Entonces deberia poder documentar ambos servicios
|
||||
Y el precio deberia ajustarse
|
||||
|
||||
Escenario: Derivar a clinica
|
||||
Dado que el paciente necesita atencion en clinica
|
||||
Cuando marco "Derivacion a clinica"
|
||||
Y especifico el motivo
|
||||
Entonces deberia quedar registrada la derivacion
|
||||
Y el dueno deberia recibir instrucciones
|
||||
@@ -0,0 +1,121 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/veterinarian/04-zonas-cobertura.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_veterinarians.py
|
||||
# Tests Frontend: npx playwright test vet-coverage.spec.ts
|
||||
|
||||
Característica: Gestionar zonas de cobertura
|
||||
|
||||
Como veterinario
|
||||
Quiero definir en que zonas atiendo
|
||||
Para recibir solo solicitudes que puedo cubrir
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como veterinario
|
||||
Y estoy en la seccion "Mi cobertura"
|
||||
|
||||
# ============================================
|
||||
# VER ZONAS ACTUALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver mapa con mis zonas de cobertura
|
||||
Cuando cargo la pagina de cobertura
|
||||
Entonces deberia ver un mapa de la ciudad
|
||||
Y mis zonas cubiertas deberian estar resaltadas
|
||||
Y deberia ver un listado de barrios seleccionados
|
||||
|
||||
Escenario: Ver estadisticas por zona
|
||||
Dado que tengo cobertura en "Palermo" y "Recoleta"
|
||||
Cuando veo el detalle de mis zonas
|
||||
Entonces deberia ver para cada zona:
|
||||
| metrica |
|
||||
| Solicitudes este mes |
|
||||
| Visitas completadas |
|
||||
| Otros vets en la zona |
|
||||
|
||||
# ============================================
|
||||
# AGREGAR ZONAS
|
||||
# ============================================
|
||||
|
||||
Escenario: Agregar barrio desde el mapa
|
||||
Dado que no tengo cobertura en "Belgrano"
|
||||
Cuando hago click en "Belgrano" en el mapa
|
||||
Y confirmo agregar la zona
|
||||
Entonces "Belgrano" deberia aparecer en mi lista de zonas
|
||||
Y deberia empezar a ver solicitudes de Belgrano
|
||||
|
||||
Escenario: Agregar barrio desde el buscador
|
||||
Cuando busco "Nuñez" en el buscador de barrios
|
||||
Y selecciono "Nuñez" de los resultados
|
||||
Y confirmo agregar la zona
|
||||
Entonces "Nuñez" deberia aparecer en mi lista
|
||||
|
||||
Escenario: Agregar multiples zonas a la vez
|
||||
Cuando selecciono los barrios:
|
||||
| barrio |
|
||||
| Colegiales |
|
||||
| Chacarita |
|
||||
| Villa Crespo |
|
||||
Y hago click en "Agregar seleccionados"
|
||||
Entonces los 3 barrios deberian agregarse a mi cobertura
|
||||
|
||||
# ============================================
|
||||
# QUITAR ZONAS
|
||||
# ============================================
|
||||
|
||||
Escenario: Quitar zona de cobertura
|
||||
Dado que tengo cobertura en "Recoleta"
|
||||
Cuando hago click en "X" junto a "Recoleta"
|
||||
Y confirmo quitar la zona
|
||||
Entonces "Recoleta" no deberia estar en mi lista
|
||||
Y no deberia ver nuevas solicitudes de Recoleta
|
||||
|
||||
Escenario: Quitar zona con solicitudes pendientes
|
||||
Dado que tengo cobertura en "Palermo"
|
||||
Y hay solicitudes pendientes en Palermo que no acepte
|
||||
Cuando intento quitar "Palermo"
|
||||
Entonces deberia ver advertencia "Hay X solicitudes pendientes en esta zona"
|
||||
Y deberia poder confirmar de todas formas
|
||||
# Las solicitudes pendientes siguen disponibles para otros vets
|
||||
|
||||
# ============================================
|
||||
# IMPACTO EN SOLICITUDES
|
||||
# ============================================
|
||||
|
||||
Regla: Los cambios de zona afectan solo solicitudes nuevas
|
||||
|
||||
Escenario: Agregar zona muestra solicitudes existentes
|
||||
Dado que no tengo cobertura en "Belgrano"
|
||||
Y hay 3 solicitudes pendientes en Belgrano
|
||||
Cuando agrego "Belgrano" a mi cobertura
|
||||
Entonces deberia ver las 3 solicitudes pendientes de Belgrano
|
||||
|
||||
Escenario: Quitar zona no afecta visitas aceptadas
|
||||
Dado que tengo cobertura en "Recoleta"
|
||||
Y tengo una visita aceptada en Recoleta para mañana
|
||||
Cuando quito "Recoleta" de mi cobertura
|
||||
Entonces la visita de mañana deberia mantenerse
|
||||
Pero no deberia ver nuevas solicitudes de Recoleta
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Zona no disponible en el sistema
|
||||
Dado que quiero cubrir "Barrio Nuevo"
|
||||
Pero ese barrio no esta en el sistema
|
||||
Cuando busco "Barrio Nuevo"
|
||||
Entonces deberia ver mensaje "Zona no encontrada"
|
||||
Y deberia ver opcion "Solicitar que agreguen esta zona"
|
||||
|
||||
Escenario: Zona con baja demanda
|
||||
Dado que agrego una zona con poca actividad
|
||||
Cuando pasan 30 dias sin solicitudes
|
||||
Entonces deberia recibir sugerencia "Considera expandir tu cobertura"
|
||||
|
||||
Escenario: Zona saturada de veterinarios
|
||||
Dado que quiero agregar "Palermo"
|
||||
Y hay muchos veterinarios cubriendo Palermo
|
||||
Cuando agrego la zona
|
||||
Entonces deberia ver nota "Esta zona tiene alta cobertura de veterinarios"
|
||||
# Informativo, no bloquea
|
||||
@@ -0,0 +1,157 @@
|
||||
# language: es
|
||||
# Fuente: album/book/ops-templates/veterinarian/05-historial-pacientes.md
|
||||
# Drive: 08. IT y Producto/Funcionalidades Plataforma Veterinario
|
||||
# Tests Backend: pytest tests/contracts/mascotas/test_vet_visits.py
|
||||
# Tests Frontend: npx playwright test vet-history.spec.ts
|
||||
|
||||
Característica: Ver historial de pacientes
|
||||
|
||||
Como veterinario
|
||||
Quiero acceder al historial medico de pacientes
|
||||
Para tener contexto clinico en mis atenciones
|
||||
|
||||
Antecedentes:
|
||||
Dado que estoy logueado como veterinario
|
||||
Y estoy en la seccion "Historia clinica"
|
||||
|
||||
# ============================================
|
||||
# BUSCAR PACIENTES
|
||||
# ============================================
|
||||
|
||||
Esquema del escenario: Buscar paciente por diferentes criterios
|
||||
Cuando busco por <criterio> con valor "<valor>"
|
||||
Entonces deberia ver resultados que coincidan
|
||||
|
||||
Ejemplos:
|
||||
| criterio | valor |
|
||||
| nombre dueno | Maria Garcia |
|
||||
| nombre mascota | Luna |
|
||||
| telefono | 1155551234 |
|
||||
| email | maria@ejemplo.com |
|
||||
|
||||
Escenario: Busqueda sin resultados
|
||||
Cuando busco "ZZZZZ paciente inexistente"
|
||||
Entonces deberia ver mensaje "No se encontraron resultados"
|
||||
Y deberia ver sugerencia "Verifica la ortografia"
|
||||
|
||||
Escenario: Busqueda con multiples resultados
|
||||
Cuando busco "Garcia"
|
||||
Y hay varios duenos con apellido Garcia
|
||||
Entonces deberia ver lista de coincidencias
|
||||
Y cada resultado deberia mostrar:
|
||||
| campo |
|
||||
| Nombre completo |
|
||||
| Email |
|
||||
| Mascotas |
|
||||
|
||||
# ============================================
|
||||
# VER FICHA DE MASCOTA
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver ficha completa de mascota
|
||||
Dado que encontre a la mascota "Luna"
|
||||
Cuando hago click en Luna
|
||||
Entonces deberia ver la ficha con:
|
||||
| seccion | contenido |
|
||||
| Datos basicos | Nombre, tipo, raza, edad, peso |
|
||||
| Foto | Foto de la mascota |
|
||||
| Dueno | Nombre y contacto del dueno |
|
||||
| Vacunacion | Estado de vacunas |
|
||||
| Historial | Lista de visitas |
|
||||
|
||||
Escenario: Ver grafico de peso historico
|
||||
Dado que estoy viendo la ficha de "Luna"
|
||||
Y Luna tuvo multiples visitas con peso registrado
|
||||
Cuando veo la seccion "Evolucion"
|
||||
Entonces deberia ver grafico de peso en el tiempo
|
||||
Y deberia poder detectar tendencias
|
||||
|
||||
# ============================================
|
||||
# VER HISTORIAL DE VISITAS
|
||||
# ============================================
|
||||
|
||||
Escenario: Ver listado de visitas
|
||||
Dado que estoy viendo la ficha de "Luna"
|
||||
Y Luna tiene 5 visitas completadas
|
||||
Cuando veo la seccion "Historial de visitas"
|
||||
Entonces deberia ver las 5 visitas listadas
|
||||
Y deberian estar ordenadas de mas reciente a mas antigua
|
||||
Y cada visita deberia mostrar:
|
||||
| campo |
|
||||
| Fecha |
|
||||
| Veterinario |
|
||||
| Servicios |
|
||||
| Diagnostico |
|
||||
|
||||
Escenario: Ver informe de visita de otro veterinario
|
||||
Dado que Luna fue atendida por "Dra. Rodriguez"
|
||||
Y yo no la atendi en esa visita
|
||||
Cuando hago click en esa visita
|
||||
Entonces deberia poder ver el informe completo
|
||||
# Para continuidad de atencion
|
||||
|
||||
Escenario: Ver informe detallado
|
||||
Cuando hago click en una visita
|
||||
Entonces deberia ver el informe con:
|
||||
| seccion | contenido |
|
||||
| Examen fisico | Peso, temperatura, signos vitales |
|
||||
| Diagnostico | Descripcion del diagnostico |
|
||||
| Tratamiento | Plan de tratamiento indicado |
|
||||
| Medicamentos | Lista con dosis y duracion |
|
||||
| Estudios | Estudios solicitados y resultados |
|
||||
| Observaciones | Notas adicionales |
|
||||
|
||||
# ============================================
|
||||
# FILTROS Y NAVEGACION
|
||||
# ============================================
|
||||
|
||||
Escenario: Filtrar historial por tipo de servicio
|
||||
Dado que estoy viendo el historial de "Luna"
|
||||
Cuando filtro por servicio "Vacunacion"
|
||||
Entonces solo deberia ver visitas de vacunacion
|
||||
|
||||
Escenario: Filtrar historial por fecha
|
||||
Dado que estoy viendo el historial de "Luna"
|
||||
Cuando filtro por año "2023"
|
||||
Entonces solo deberia ver visitas del 2023
|
||||
|
||||
Escenario: Filtrar por mis atenciones
|
||||
Dado que estoy viendo el historial de "Luna"
|
||||
Y Luna fue atendida por varios veterinarios
|
||||
Cuando marco "Solo mis atenciones"
|
||||
Entonces solo deberia ver las visitas que yo realice
|
||||
|
||||
# ============================================
|
||||
# PERMISOS
|
||||
# ============================================
|
||||
|
||||
Regla: Veterinarios pueden ver historial de pacientes que atendieron
|
||||
|
||||
Escenario: Puedo ver historial de paciente que atendi
|
||||
Dado que yo atendi a "Luna" al menos una vez
|
||||
Cuando busco a Luna
|
||||
Entonces deberia poder ver su historial completo
|
||||
|
||||
Escenario: Puedo ver historial de paciente con solicitud pendiente
|
||||
Dado que hay una solicitud pendiente para "Rocky"
|
||||
Y la solicitud esta en mi zona
|
||||
Cuando busco a Rocky
|
||||
Entonces deberia poder ver su historial
|
||||
# Para evaluar si acepto la solicitud
|
||||
|
||||
# ============================================
|
||||
# CASOS ESPECIALES
|
||||
# ============================================
|
||||
|
||||
Escenario: Paciente sin historial previo
|
||||
Dado que busco a "Nuevo" que nunca fue atendido
|
||||
Cuando veo su ficha
|
||||
Entonces el historial deberia estar vacio
|
||||
Y deberia ver mensaje "Sin visitas registradas"
|
||||
|
||||
Escenario: Dueno con multiples mascotas
|
||||
Dado que busque al dueno "Maria Garcia"
|
||||
Y Maria tiene 3 mascotas
|
||||
Cuando veo su perfil
|
||||
Entonces deberia ver las 3 mascotas listadas
|
||||
Y deberia poder navegar al historial de cada una
|
||||
248
atlas/book/gherkin-samples/index.html
Normal file
248
atlas/book/gherkin-samples/index.html
Normal file
@@ -0,0 +1,248 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Gherkin Samples - Album</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: #f8fafc;
|
||||
color: #1e293b;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container { max-width: 1000px; margin: 0 auto; padding: 2rem 1rem; }
|
||||
header { margin-bottom: 2rem; }
|
||||
.breadcrumb { font-size: 0.9rem; color: #64748b; margin-bottom: 0.5rem; }
|
||||
.breadcrumb a { color: #6366f1; text-decoration: none; }
|
||||
.breadcrumb a:hover { text-decoration: underline; }
|
||||
h1 { font-size: 2rem; color: #6366f1; }
|
||||
.subtitle { color: #64748b; margin-top: 0.5rem; }
|
||||
.sample-badge {
|
||||
display: inline-block;
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.lang-tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.lang-tab {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
background: white;
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
}
|
||||
.lang-tab.active {
|
||||
border-color: #6366f1;
|
||||
color: #6366f1;
|
||||
background: #eef2ff;
|
||||
}
|
||||
.lang-section { display: none; }
|
||||
.lang-section.active { display: block; }
|
||||
.user-type {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.user-type h2 {
|
||||
font-size: 1.25rem;
|
||||
color: #6366f1;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.user-type h2::before {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #818cf8;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.features-list { list-style: none; }
|
||||
.features-list li {
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
.features-list li:last-child { border-bottom: none; }
|
||||
.features-list a {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0;
|
||||
text-decoration: none;
|
||||
color: #1e293b;
|
||||
}
|
||||
.features-list a:hover { color: #6366f1; }
|
||||
.features-list .name { font-weight: 500; }
|
||||
.features-list .ext { color: #94a3b8; font-size: 0.85rem; }
|
||||
.features-list .arrow { color: #94a3b8; }
|
||||
.info-box {
|
||||
background: #eef2ff;
|
||||
border: 1px solid #c7d2fe;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-top: 2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.info-box h3 { font-size: 0.9rem; color: #6366f1; margin-bottom: 0.5rem; }
|
||||
.keywords {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.keyword {
|
||||
background: white;
|
||||
border: 1px solid #c7d2fe;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
footer {
|
||||
margin-top: 3rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
footer a { color: #6366f1; text-decoration: none; }
|
||||
footer a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="breadcrumb"><a href="/">Album</a> / Gherkin Samples</div>
|
||||
<h1>Gherkin Samples</h1>
|
||||
<p class="subtitle">BDD feature files - Given/When/Then specifications derived from ops templates</p>
|
||||
<span class="sample-badge">Sample Data</span>
|
||||
</header>
|
||||
|
||||
<div class="lang-tabs">
|
||||
<button class="lang-tab active" onclick="showLang('es')">Espanol (Dado/Cuando/Entonces)</button>
|
||||
<button class="lang-tab" onclick="showLang('en')">English (Given/When/Then)</button>
|
||||
</div>
|
||||
|
||||
<!-- Spanish -->
|
||||
<div id="lang-es" class="lang-section active">
|
||||
<div class="user-type">
|
||||
<h2>Pet Owner</h2>
|
||||
<ul class="features-list">
|
||||
<li><a href="/book/gherkin/es/pet-owner/01-registro.feature"><span class="name">01. Registro de Usuario</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/pet-owner/02-reservar-turno.feature"><span class="name">02. Reservar Turno</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/pet-owner/03-gestion-mascotas.feature"><span class="name">03. Gestion de Mascotas</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/pet-owner/04-pago-turno.feature"><span class="name">04. Pago de Turno</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/pet-owner/05-historial-medico.feature"><span class="name">05. Historial Medico</span><span class="ext">.feature</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="user-type">
|
||||
<h2>Veterinarian</h2>
|
||||
<ul class="features-list">
|
||||
<li><a href="/book/gherkin/es/veterinarian/01-aceptar-solicitud.feature"><span class="name">01. Aceptar/Rechazar Solicitud</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/veterinarian/02-gestion-agenda.feature"><span class="name">02. Gestion de Agenda</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/veterinarian/03-realizar-visita.feature"><span class="name">03. Realizar Visita</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/veterinarian/04-zonas-cobertura.feature"><span class="name">04. Zonas de Cobertura</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/veterinarian/05-historial-pacientes.feature"><span class="name">05. Historial de Pacientes</span><span class="ext">.feature</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="user-type">
|
||||
<h2>Backoffice</h2>
|
||||
<ul class="features-list">
|
||||
<li><a href="/book/gherkin/es/backoffice/01-gestion-solicitudes.feature"><span class="name">01. Gestion de Solicitudes</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/backoffice/02-gestion-usuarios.feature"><span class="name">02. Gestion de Usuarios</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/backoffice/03-gestion-servicios.feature"><span class="name">03. Gestion de Servicios</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/backoffice/04-reembolsos.feature"><span class="name">04. Reembolsos</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/es/backoffice/05-reportes.feature"><span class="name">05. Reportes</span><span class="ext">.feature</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<h3>Spanish Keywords</h3>
|
||||
<div class="keywords">
|
||||
<span class="keyword">Caracteristica</span>
|
||||
<span class="keyword">Escenario</span>
|
||||
<span class="keyword">Dado</span>
|
||||
<span class="keyword">Cuando</span>
|
||||
<span class="keyword">Entonces</span>
|
||||
<span class="keyword">Y</span>
|
||||
<span class="keyword">Pero</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- English -->
|
||||
<div id="lang-en" class="lang-section">
|
||||
<div class="user-type">
|
||||
<h2>Pet Owner</h2>
|
||||
<ul class="features-list">
|
||||
<li><a href="/book/gherkin/en/pet-owner/01-registro.feature"><span class="name">01. User Registration</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/pet-owner/02-reservar-turno.feature"><span class="name">02. Book Appointment</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/pet-owner/03-gestion-mascotas.feature"><span class="name">03. Pet Management</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/pet-owner/04-pago-turno.feature"><span class="name">04. Payment</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/pet-owner/05-historial-medico.feature"><span class="name">05. Medical History</span><span class="ext">.feature</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="user-type">
|
||||
<h2>Veterinarian</h2>
|
||||
<ul class="features-list">
|
||||
<li><a href="/book/gherkin/en/veterinarian/01-aceptar-solicitud.feature"><span class="name">01. Accept/Reject Request</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/veterinarian/02-gestion-agenda.feature"><span class="name">02. Schedule Management</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/veterinarian/03-realizar-visita.feature"><span class="name">03. Conduct Visit</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/veterinarian/04-zonas-cobertura.feature"><span class="name">04. Coverage Zones</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/veterinarian/05-historial-pacientes.feature"><span class="name">05. Patient History</span><span class="ext">.feature</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="user-type">
|
||||
<h2>Backoffice</h2>
|
||||
<ul class="features-list">
|
||||
<li><a href="/book/gherkin/en/backoffice/01-gestion-solicitudes.feature"><span class="name">01. Request Management</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/backoffice/02-gestion-usuarios.feature"><span class="name">02. User Management</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/backoffice/03-gestion-servicios.feature"><span class="name">03. Service Management</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/backoffice/04-reembolsos.feature"><span class="name">04. Refunds</span><span class="ext">.feature</span></a></li>
|
||||
<li><a href="/book/gherkin/en/backoffice/05-reportes.feature"><span class="name">05. Reports</span><span class="ext">.feature</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<h3>English Keywords</h3>
|
||||
<div class="keywords">
|
||||
<span class="keyword">Feature</span>
|
||||
<span class="keyword">Scenario</span>
|
||||
<span class="keyword">Given</span>
|
||||
<span class="keyword">When</span>
|
||||
<span class="keyword">Then</span>
|
||||
<span class="keyword">And</span>
|
||||
<span class="keyword">But</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<a href="/">← Album</a> |
|
||||
<a href="/book/ops-templates/">Source Templates</a> |
|
||||
<a href="/book/feature-flow/">Feature Flow Pipeline</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showLang(lang) {
|
||||
document.querySelectorAll('.lang-section').forEach(s => s.classList.remove('active'));
|
||||
document.querySelectorAll('.lang-tab').forEach(t => t.classList.remove('active'));
|
||||
document.getElementById('lang-' + lang).classList.add('active');
|
||||
event.target.classList.add('active');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user