Compare commits
50 Commits
27b32deba4
...
rig-fold
| Author | SHA1 | Date | |
|---|---|---|---|
| 83b6cbebe3 | |||
| a65c92257d | |||
| 78595f1bd9 | |||
| 9bcd439266 | |||
| 74f03566f1 | |||
| 2d9bc9289c | |||
| 910927993e | |||
| 9a6337e493 | |||
| 0b04516cbb | |||
| ef63b02554 | |||
| 33f0559268 | |||
| dfb1991ae3 | |||
| 9fc4c23143 | |||
| 973f0a01c9 | |||
| 38c2cfe50f | |||
| 5f9cac1947 | |||
| b886455431 | |||
| ec3391fe72 | |||
| a80b72a9b1 | |||
| 2e5a304181 | |||
| 54e7cfebd9 | |||
| def1b95bab | |||
| 3e625c2a27 | |||
| 85a856b7ac | |||
| af06309dad | |||
| a1141759a5 | |||
| b2bd2245cb | |||
| 0d2862c633 | |||
|
|
b233cd049a | ||
|
|
d783bb3a61 | ||
|
|
9eb720f90b | ||
|
|
fbe10a96d3 | ||
|
|
69121eb0c1 | ||
|
|
55d68ed371 | ||
|
|
88c286029c | ||
|
|
5603979d5c | ||
|
|
2babd47835 | ||
|
|
027f73794d | ||
|
|
8c5deb74e8 | ||
|
|
dd47f9c66f | ||
|
|
e1f81889fc | ||
|
|
fecb978a5f | ||
|
|
cae5a913ca | ||
|
|
c4e702eae3 | ||
|
|
6e18324a43 | ||
|
|
423909d391 | ||
|
|
d7d3c90152 | ||
|
|
6b9a228d9a | ||
|
|
a9d1e135cb | ||
|
|
e4052374db |
27
.gitattributes
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copied verbatim from rig/.gitattributes, and deliberately duplicated rather
|
||||
# than shared: rig/ must carry its own so it survives being handed over on its
|
||||
# own, and spr had none at all despite shipping ctrl/*.sh and generating
|
||||
# gen/<room>/ctrl/*.sh.
|
||||
#
|
||||
# Line endings are normalised to LF in the repository and on checkout, on every
|
||||
# platform. Without this, a checkout on Windows/WSL rewrites files to CRLF and
|
||||
# every one of them shows up as modified without anyone having touched it.
|
||||
#
|
||||
# For the scripts it is not cosmetic: a shell script with CRLF fails on Linux
|
||||
# with `bad interpreter: /usr/bin/env bash^M`, which reads as a broken installer
|
||||
# rather than a line-ending problem — the worst possible first impression on a
|
||||
# machine where nothing has been proven yet.
|
||||
* text=auto eol=lf
|
||||
|
||||
*.sh text eol=lf
|
||||
*.py text eol=lf
|
||||
*.env text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.yml text eol=lf
|
||||
|
||||
# Never touch binaries.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.zip binary
|
||||
*.tar binary
|
||||
*.gz binary
|
||||
34
.gitignore
vendored
@@ -9,8 +9,38 @@ __pycache__/
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Secrets. There was no rule here, which is how a real API key ended up tracked
|
||||
# in station/tools/tester/.env. Templates still ship.
|
||||
.env
|
||||
!.env.example
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
|
||||
# Built library bundles (regenerate with `pnpm build` in the package).
|
||||
# The dist is what a container boots; it is an artifact, not source.
|
||||
dist/
|
||||
|
||||
# Generated runnable instance (entirely gitignored - regenerate with build.py)
|
||||
gen/
|
||||
|
||||
# Database dumps (sensitive data)
|
||||
cfg/*/dumps/*.sql
|
||||
# Specs and sheets uploaded through shuntgen's UI. Inputs someone dropped in a
|
||||
# browser, not source — and often a client's real data.
|
||||
soleprint/station/tools/shuntgen/uploads/
|
||||
|
||||
# Room configurations (separate repo - contains credentials and room-specific data)
|
||||
# Keep cfg/standalone/ and cfg/sample/ as templates, ignore actual rooms
|
||||
cfg/amar/
|
||||
cfg/dlt/
|
||||
# Add new rooms here as they are created
|
||||
# cfg/<room>/
|
||||
|
||||
# Client rigs. A rig is a copy of rig/ renamed after the environment it models,
|
||||
# so its k8s files spell out a real architecture — exactly the thing that must
|
||||
# not land here. They are versioned in their own repo.
|
||||
#
|
||||
# Anchored at the ROOT on purpose: a copy is a SIBLING of rig/, so a rule inside
|
||||
# rig/.gitignore cannot see it. The negation must name the full path for the same
|
||||
# reason — `*-rig/` is unanchored and matches at any depth, including rig/sample-rig.
|
||||
*-rig/
|
||||
!rig/sample-rig/
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Soleprint Pipeline
|
||||
|
||||
when:
|
||||
- event: push
|
||||
- event: manual
|
||||
|
||||
steps:
|
||||
- name: notify
|
||||
image: alpine
|
||||
commands:
|
||||
- echo "=== Soleprint ==="
|
||||
- "echo Branch: $CI_COMMIT_BRANCH"
|
||||
- "echo Commit: $CI_COMMIT_SHA"
|
||||
- "echo Build locally: ./ctrl/deploy-domains.sh standalone --build"
|
||||
264
CLAUDE.md
@@ -17,51 +17,57 @@ spr/
|
||||
├── schema.json # Source of truth for models
|
||||
├── build.py # Build tool
|
||||
│
|
||||
├── cfg/ # Room configurations
|
||||
│ ├── standalone/ # Base soleprint config
|
||||
│ │ ├── config.json # Framework branding/terminology
|
||||
│ │ └── data/ # Data files (veins.json, shunts.json, etc.)
|
||||
│ └── amar/ # AMAR room config
|
||||
│ ├── config.json # Can rebrand (e.g., "pawprint")
|
||||
│ ├── data/ # Room-specific data files
|
||||
│ ├── .env.example
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── soleprint/ # Soleprint Docker config for this room
|
||||
│ ├── databrowse/depot/
|
||||
│ ├── tester/tests/
|
||||
│ ├── monitors/
|
||||
│ └── models/
|
||||
│
|
||||
├── ctrl/ # Build/run scripts
|
||||
│ ├── build.sh # ./build.sh [room]
|
||||
│ ├── start.sh # ./start.sh [room] [-d]
|
||||
│ ├── stop.sh # ./stop.sh [room]
|
||||
│ └── logs.sh # ./logs.sh [room]
|
||||
│
|
||||
├── artery/ # Vital connections
|
||||
│ ├── veins/ # Stateless API connectors (jira, slack, google)
|
||||
│ ├── shunts/ # Fake connectors for testing
|
||||
│ ├── pulses/ # Composed: Vein + Room + Depot
|
||||
│ └── plexus/ # Full apps: backend + frontend + DB
|
||||
│
|
||||
├── atlas/ # Documentation system
|
||||
│ └── books/ # Soleprint docs (external via depots)
|
||||
│
|
||||
├── station/ # Tools & execution
|
||||
│ ├── tools/ # modelgen, datagen, tester, sbwrapper
|
||||
│ └── monitors/ # databrowse
|
||||
│
|
||||
├── soleprint/ # Core entry points (versioned)
|
||||
│ ├── main.py
|
||||
│ ├── run.py
|
||||
├── soleprint/ # Core framework (all systems inside)
|
||||
│ ├── main.py # Hub entry point
|
||||
│ ├── run.py # Bare-metal dev server
|
||||
│ ├── index.html
|
||||
│ ├── requirements.txt
|
||||
│ ├── Dockerfile
|
||||
│ └── dataloader/
|
||||
│ ├── dataloader/
|
||||
│ │
|
||||
│ ├── artery/ # Connectors
|
||||
│ │ ├── veins/ # Real API connectors (jira, slack, google)
|
||||
│ │ ├── shunts/ # Fake connectors for testing
|
||||
│ │ ├── pulses/ # Composed: Vein + Room + Depot
|
||||
│ │ └── depots/
|
||||
│ │
|
||||
│ ├── atlas/ # Documentation
|
||||
│ │ ├── books/ # Documentation (soleprint-only)
|
||||
│ │ ├── templates/
|
||||
│ │ ├── depots/
|
||||
│ │ └── static/
|
||||
│ │
|
||||
│ └── station/ # Tools & execution
|
||||
│ ├── tools/ # modelgen, datagen, tester, sbwrapper
|
||||
│ ├── monitors/ # databrowse
|
||||
│ └── desks/
|
||||
│
|
||||
├── cfg/ # Room configurations
|
||||
│ ├── standalone/ # Base soleprint config
|
||||
│ │ ├── config.json
|
||||
│ │ ├── data/
|
||||
│ │ └── soleprint/
|
||||
│ │
|
||||
│ └── amar/ # Amar room config
|
||||
│ ├── config.json
|
||||
│ ├── data/
|
||||
│ ├── soleprint/ # Room overlay — merged over soleprint/ on build
|
||||
│ │ ├── artery/ # room shunts, pulses
|
||||
│ │ │ └── shunts/amar/
|
||||
│ │ ├── atlas/ # room books
|
||||
│ │ │ └── books/
|
||||
│ │ ├── station/ # room tool configs
|
||||
│ │ │ └── tools/datagen/
|
||||
│ │ └── nginx/
|
||||
│ ├── ctrl/ # Room lifecycle scripts (copied into gen/<room>/)
|
||||
│ ├── link/ # Bridge to managed app
|
||||
│ └── amar/ # The managed app itself
|
||||
│
|
||||
├── ctrl/ # Build/run scripts (see Build & Run)
|
||||
│
|
||||
└── gen/ # Built instances (gitignored)
|
||||
├── standalone/ # python build.py dev
|
||||
└── amar/ # python build.py dev --cfg amar
|
||||
├── standalone/
|
||||
└── amar/
|
||||
```
|
||||
|
||||
## The Four Systems
|
||||
@@ -87,63 +93,59 @@ Vein ──────► Pulse ──────► Plexus
|
||||
Shunt ─── Fake connector for testing
|
||||
```
|
||||
|
||||
| Type | State | Frontend | Deploy |
|
||||
|------|-------|----------|--------|
|
||||
| Vein | None (or OAuth) | Optional test UI | With soleprint |
|
||||
| Shunt | Configurable responses | Config UI | With soleprint |
|
||||
| Pulse | Vein + config | Uses vein's | With soleprint |
|
||||
| Plexus | Full app state | Required | Self-contained |
|
||||
|
||||
## Room Configuration
|
||||
|
||||
Each room in `cfg/` has:
|
||||
- `config.json` - Framework branding/terminology (can rebrand soleprint)
|
||||
- `data/` - Data files (veins.json, shunts.json, depots.json, etc.)
|
||||
- Room-specific: databrowse depot, tester tests, monitors, models
|
||||
- `config.json` - Framework branding/terminology
|
||||
- `data/` - Data files (veins.json, shunts.json, etc.)
|
||||
|
||||
Managed rooms (like amar) also have:
|
||||
- `docker-compose.yml` - Room's own services
|
||||
- `soleprint/` - Soleprint Docker config for this room
|
||||
- `.env.example` - Environment template
|
||||
Room-specific system configs live under `cfg/<room>/soleprint/` and are merged over
|
||||
the core `soleprint/` tree at build time:
|
||||
- `soleprint/artery/` - Room-specific shunts, pulses
|
||||
- `soleprint/atlas/` - Room-specific books
|
||||
- `soleprint/station/` - Room-specific tool configs (datagen generators, tester tests)
|
||||
|
||||
A room's own lifecycle scripts live in `cfg/<room>/ctrl/` and land in
|
||||
`gen/<room>/ctrl/` — that is what `make start <room>` dispatches to.
|
||||
|
||||
## Build & Run
|
||||
|
||||
### Commands
|
||||
`make` is the front door — one target per `ctrl/` script, with the subcommand as an
|
||||
argument (`make cluster up`, not `make cluster-up`). The logic lives in the scripts,
|
||||
never in the Makefile. `make help` lists every target.
|
||||
|
||||
```bash
|
||||
# Build
|
||||
python build.py dev # -> gen/standalone/
|
||||
python build.py dev --cfg amar # -> gen/amar/
|
||||
python build.py dev --all # -> both
|
||||
|
||||
# Using ctrl scripts
|
||||
./ctrl/build.sh # Build standalone
|
||||
./ctrl/build.sh amar # Build amar
|
||||
./ctrl/build.sh --all # Build all
|
||||
|
||||
./ctrl/start.sh # Start standalone
|
||||
./ctrl/start.sh amar # Start amar
|
||||
./ctrl/start.sh amar -d # Detached
|
||||
./ctrl/stop.sh amar # Stop
|
||||
./ctrl/logs.sh amar # View logs
|
||||
|
||||
# Bare-metal dev
|
||||
cd gen/standalone && .venv/bin/python run.py
|
||||
make # = make help
|
||||
make build [room|all|models] # -> gen/<room>/ (default: standalone)
|
||||
make start [room] [-d] # dispatches to gen/<room>/ctrl/start.sh
|
||||
make stop [room]
|
||||
make cluster [up|down|status] # the shared `spr` kind cluster
|
||||
make component [list|sync|watch|publish|diff]
|
||||
make deploy [--build|--sync-only]
|
||||
```
|
||||
|
||||
### Adding a New Managed Room
|
||||
Every script stays runnable on its own — the standalone rule holds:
|
||||
|
||||
```bash
|
||||
# 1. Create room config
|
||||
mkdir -p cfg/clientx/data
|
||||
python build.py --cfg amar # -> gen/amar/
|
||||
cd gen/standalone && python run.py # bare-metal
|
||||
./ctrl/kind-up.sh # still works directly
|
||||
cd gen/<room> && ./ctrl/start.sh # each room owns its lifecycle scripts
|
||||
```
|
||||
|
||||
## Adding a New Room
|
||||
|
||||
# 2. Copy base config
|
||||
cp cfg/standalone/config.json cfg/clientx/
|
||||
cp -r cfg/standalone/data/* cfg/clientx/data/
|
||||
```bash
|
||||
mkdir -p cfg/newroom/data
|
||||
cp cfg/standalone/config.json cfg/newroom/
|
||||
cp -r cfg/standalone/data/* cfg/newroom/data/
|
||||
|
||||
# 3. Customize as needed (shunts, depots, branding)
|
||||
# Add room-specific configs as needed (note the soleprint/ overlay level):
|
||||
# cfg/newroom/soleprint/artery/shunts/...
|
||||
# cfg/newroom/soleprint/atlas/books/...
|
||||
# cfg/newroom/soleprint/station/tools/datagen/<name>.py
|
||||
|
||||
# 4. Build and run
|
||||
python build.py dev --cfg clientx
|
||||
./ctrl/start.sh clientx
|
||||
make build newroom
|
||||
```
|
||||
|
||||
## Ports
|
||||
@@ -151,45 +153,95 @@ python build.py dev --cfg clientx
|
||||
| Service | Port |
|
||||
|---------|------|
|
||||
| Soleprint | 12000 |
|
||||
| Amar Backend | 8000 |
|
||||
| Amar Frontend | 3000 |
|
||||
|
||||
## Tools
|
||||
|
||||
| Tool | Location | Purpose |
|
||||
|------|----------|---------|
|
||||
| modelgen | station/tools/modelgen | Model generation |
|
||||
| datagen | station/tools/datagen | Test data generation |
|
||||
| tester | station/tools/tester | BDD/playwright test runner |
|
||||
| sbwrapper | station/tools/sbwrapper | Sidebar wrapper UI |
|
||||
| databrowse | station/monitors/databrowse | SQL data browser |
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| modelgen | Generate models from config |
|
||||
| datagen | Generate test data (uses faker) |
|
||||
| tester | HTTP contract test runner |
|
||||
| graphgen | Generate navigable model graphs |
|
||||
| databrowse | SQL data browser |
|
||||
|
||||
## Shared UI (`soleprint/common/ui`)
|
||||
|
||||
## Integration with ppl/ (Infrastructure)
|
||||
Upstream copy of the `soleprint-ui` package. `mpr/ui/framework` and
|
||||
`meetus/ui/framework` are **downstream copies** synced by `ctrl/spr.py` — edit here,
|
||||
never there, or the next sync overwrites the change.
|
||||
|
||||
```bash
|
||||
cd soleprint/common/ui
|
||||
pnpm install && pnpm typecheck && pnpm test
|
||||
pnpm build # -> dist/soleprint-ui.js + dist/style.css
|
||||
```
|
||||
wdir/
|
||||
├── spr/ # This repo (soleprint)
|
||||
├── ppl/ # Pipelines & infrastructure
|
||||
│ ├── pipelines/spr-standalone/ # CI/CD for standalone
|
||||
│ ├── pipelines/spr-managed/ # Manual deploy for rooms
|
||||
│ └── gateway/ # Nginx configs
|
||||
└── ama/ # Amar source code
|
||||
|
||||
**The theme ships with the bundle.** Every component styles itself with
|
||||
`var(--surface-0)` and friends from `src/tokens.css`, so `src/index.ts` imports
|
||||
`src/theme.css` (tokens + `base.css`). A bundle without it renders broken, not merely
|
||||
unbranded. Retheme by overriding the variables in `tokens.css`; don't inline colours
|
||||
in components.
|
||||
|
||||
`dist/` and `node_modules/` are gitignored — the dist is an artifact a container
|
||||
boots, not source. To hand someone a running artifact without the sources:
|
||||
|
||||
```bash
|
||||
python ctrl/spr.py publish soleprint-ui <dest> --dist # bundle + manifest only
|
||||
```
|
||||
|
||||
### Pipeline (standalone only)
|
||||
- git push -> woodpecker -> build gen/standalone/ -> docker push -> deploy
|
||||
- Managed rooms deploy manually (no pipeline for client code)
|
||||
## Tests
|
||||
|
||||
**No test bodies are committed to core.** `soleprint/station/tools/tester/` ships the
|
||||
base class, runner and UI; tests belong to a room at
|
||||
`cfg/<room>/soleprint/station/tools/tester/tests/`.
|
||||
|
||||
Two rules, both in `tester/tests/test_template.py`:
|
||||
|
||||
1. **One suite, any environment** — a test states what the API promises, never who
|
||||
implements it or where it runs.
|
||||
2. **No helper framework tools** — stdlib `unittest` + `httpx`. No pytest fixtures,
|
||||
no framework test client, no factories, no ORM or database access.
|
||||
|
||||
Django, where it appears, is an optional private DB editor via its admin — never the
|
||||
framework, and never something a test reaches into.
|
||||
|
||||
## External Paths
|
||||
|
||||
| What | Path |
|
||||
|------|------|
|
||||
| Amar Backend | /home/mariano/wdir/ama/amar_django_back |
|
||||
| Amar Frontend | /home/mariano/wdir/ama/amar_frontend |
|
||||
| Pipelines | /home/mariano/wdir/ppl |
|
||||
|
||||
## Files Ignored
|
||||
|
||||
- `gen/` - Regenerate with `python build.py dev`
|
||||
- `gen/` - Regenerate with `make build [room]`
|
||||
- `dist/` - Build artifact; regenerate with `pnpm build`
|
||||
- `node_modules/`
|
||||
- `fails/`, `def/` - Drafts
|
||||
- `__pycache__/`, `.venv/`
|
||||
|
||||
## Known Broken (found, not yet fixed)
|
||||
|
||||
- `tester/tests/example/test_health.py` imports `pytest`, which is neither a
|
||||
dependency nor allowed here — `tests/README.md` says stdlib `unittest` and
|
||||
`httpx`, no pytest. It fails to import, so `python -m tester discover` reports
|
||||
it as `_FailedTest`. It also duplicates `test_template.py`, which is the
|
||||
sanctioned example. Removing it is probably the fix; core ships no tests.
|
||||
- Old vocabulary survives in prose — `album`, `larder`, `ward`, `nest` and
|
||||
`pawprint` still appear in READMEs, `docs/`, and this file's history. Live code
|
||||
is clean; the docs lag.
|
||||
|
||||
## Fixed
|
||||
|
||||
- **A real API key was tracked** in `station/tools/tester/.env`. Untracked, `.env`
|
||||
added to `.gitignore`, `.env.example` added. **The key is still in git history
|
||||
and must be rotated** — untracking does not unpublish it.
|
||||
- Client vocabulary is out of live code. `atlas/main.py` was written entirely as
|
||||
`Album`/`larder`/`PAWPRINT_URL`; `databrowse` docs described a `larder/` the
|
||||
code stopped using; `get-api-key.sh` defaulted to the client's database name.
|
||||
- Two dead back-links, found while renaming: `atlas/main.py` passed
|
||||
`pawprint_url` and `artery/index.html` read `pawprint_url`, while `run.py`
|
||||
passes `soleprint_url`. Neither "← Soleprint" link had ever rendered.
|
||||
- `atlas/main.py` fetched `/api/data/album`; `main.py` serves `/api/data/atlas`.
|
||||
`get_data()` had been failing and returning empty lists.
|
||||
- `tester/tests/_dev/test_health.py` imported a non-existent `..endpoints`. The
|
||||
import was unused; removing it makes the module discoverable (2 tests).
|
||||
|
||||
80
Makefile
Normal file
@@ -0,0 +1,80 @@
|
||||
# Thin control Makefile — one target per ctrl/ script, and the subcommand is an
|
||||
# argument rather than a second target: `make cluster down`, not `make cluster-down`.
|
||||
#
|
||||
# The logic lives in the scripts, never here. Each target maps to exactly one
|
||||
# file, and that file holds the variants:
|
||||
#
|
||||
# make cluster up -> ctrl/cluster.sh up
|
||||
# make build amar -> ctrl/build.sh amar
|
||||
#
|
||||
# Bare words pass straight through. Anything starting with a dash would be
|
||||
# swallowed by make itself, so pass those via ARGS instead:
|
||||
#
|
||||
# make component ARGS="publish soleprint-ui /tmp/out --dist"
|
||||
# make deploy ARGS="--build"
|
||||
#
|
||||
# Every script stays runnable on its own (./ctrl/kind-up.sh still works, and each
|
||||
# built room keeps its own gen/<room>/ctrl/*.sh) — the standalone rule holds, and
|
||||
# this only saves typing.
|
||||
#
|
||||
# Start with: make build && make start
|
||||
|
||||
# The room to act on when none is named. Rooms live in cfg/ and build into gen/.
|
||||
ROOM ?= standalone
|
||||
PYTHON ?= python3
|
||||
export PYTHON
|
||||
|
||||
# Words after the target become the script's subcommand. Make would otherwise
|
||||
# treat them as goals of their own, so each gets a no-op rule.
|
||||
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
|
||||
ifneq ($(ARGS),)
|
||||
# Declares each extra word as a target that does nothing: `:` is an empty rule
|
||||
# body and `@` silences it. Without this, `make build sample` runs the build and
|
||||
# then fails with "No rule to make target 'sample'", because make reads every
|
||||
# word on the line as something it has been asked to build.
|
||||
$(eval $(ARGS):;@:)
|
||||
# ...and as PHONY, because some of those words name real directories. `cfg`,
|
||||
# `ctrl`, `docs`, `gen` and `init` all exist at this level, and make considers a
|
||||
# target that is an existing directory already built — so `make build ctrl` ran
|
||||
# the build and then printed "make: 'ctrl' is up to date". The empty rule above
|
||||
# is not enough on its own; only .PHONY stops make consulting the filesystem.
|
||||
.PHONY: $(ARGS)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: help build start stop dist docs cluster deploy component
|
||||
|
||||
help: ## list targets
|
||||
@grep -hE '^[a-z]+:.*?##' $(MAKEFILE_LIST) | sed 's/:.*##/\t/' | expand -t16
|
||||
|
||||
# ── rooms ──────────────────────────────────────────────────────────────────
|
||||
|
||||
build: ## build a room into gen/ [<room>|all|models]
|
||||
bash ctrl/build.sh $(or $(ARGS),$(ROOM))
|
||||
|
||||
start: ## run a built room [<room>] [-d] [--build]
|
||||
bash ctrl/start.sh $(or $(ARGS),$(ROOM))
|
||||
|
||||
stop: ## stop a running room [<room>]
|
||||
bash ctrl/stop.sh $(or $(ARGS),$(ROOM))
|
||||
|
||||
dist: ## compile the plexus UIs to single files [<room>]
|
||||
bash ctrl/dist.sh $(or $(ARGS),$(ROOM))
|
||||
|
||||
# ── docs ───────────────────────────────────────────────────────────────────
|
||||
|
||||
docs: ## documentation [serve [port]|graphs [theme]] (default serve)
|
||||
bash ctrl/docs.sh $(or $(ARGS),serve)
|
||||
|
||||
# ── cluster ────────────────────────────────────────────────────────────────
|
||||
|
||||
cluster: ## shared kind cluster [up|down|status] (default status)
|
||||
bash ctrl/cluster.sh $(or $(ARGS),status)
|
||||
|
||||
# ── distribution ───────────────────────────────────────────────────────────
|
||||
|
||||
component: ## publish components [list|sync|watch|publish|diff]
|
||||
$(PYTHON) ctrl/spr.py $(or $(ARGS),list)
|
||||
|
||||
deploy: ## push standalone to the server [--build|--sync-only]
|
||||
bash ctrl/deploy.sh $(ARGS)
|
||||
149
README.md
@@ -1,150 +1,15 @@
|
||||
# Soleprint
|
||||
# soleprint
|
||||
|
||||
> Cada paso deja huella / Each step leaves a mark
|
||||
Development workflow platform. Wraps existing applications with tools, testing, and documentation — without touching source code.
|
||||
|
||||
Development workflow platform. Run, test, and document everything in one place.
|
||||
*Cada paso deja huella.*
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Build standalone
|
||||
python build.py dev
|
||||
cd gen/standalone && .venv/bin/python run.py
|
||||
|
||||
# Build with room config
|
||||
python build.py dev --cfg amar
|
||||
cd gen/amar && .venv/bin/python run.py
|
||||
|
||||
# Visit http://localhost:12000
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Build
|
||||
python build.py dev # → gen/standalone/
|
||||
python build.py dev --cfg amar # → gen/amar/
|
||||
python build.py dev --all # → both
|
||||
|
||||
# ctrl scripts
|
||||
./ctrl/build.sh # Build standalone
|
||||
./ctrl/build.sh amar # Build amar
|
||||
./ctrl/build.sh --all # Build all
|
||||
|
||||
./ctrl/start.sh # Start standalone (Docker)
|
||||
./ctrl/start.sh amar # Start amar
|
||||
./ctrl/start.sh -d # Detached
|
||||
./ctrl/stop.sh # Stop
|
||||
./ctrl/logs.sh # View logs
|
||||
```
|
||||
|
||||
## Adding a New Managed Room
|
||||
|
||||
1. Create room config directory:
|
||||
```bash
|
||||
mkdir -p cfg/clientx
|
||||
```
|
||||
---
|
||||
|
||||
2. Add required files:
|
||||
```
|
||||
cfg/clientx/
|
||||
├── .env.example # Environment template
|
||||
├── docker-compose.yml # Room services (optional)
|
||||
├── databrowse/depot/ # Database schemas (optional)
|
||||
├── tester/tests/ # Room-specific tests (optional)
|
||||
├── monitors/ # Room-specific monitors (optional)
|
||||
├── models/ # Room-specific models (optional)
|
||||
└── data/ # Room-specific data files (optional)
|
||||
├── depots.json # Doc depots for Atlas
|
||||
└── shunts.json # Mock connectors
|
||||
```
|
||||
## Docs
|
||||
|
||||
3. Build and run:
|
||||
```bash
|
||||
python build.py dev --cfg clientx
|
||||
./ctrl/start.sh clientx
|
||||
cd docs && python -m http.server 8080
|
||||
```
|
||||
|
||||
## Systems
|
||||
|
||||
| | System | What it does |
|
||||
|---|--------|--------------|
|
||||
| 👣 | **Soleprint** | Core coordinator, routing, landing page |
|
||||
| 💉 | **Artery** | Connectors to external services (Jira, Slack, Google) |
|
||||
| 🗺️ | **Atlas** | Actionable documentation (BDD, Gherkin, specs) |
|
||||
| 🎛️ | **Station** | Tools (modelgen, tester) and monitors (databrowse) |
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
spr/
|
||||
├── build.py # Build tool
|
||||
├── cfg/ # Room configurations
|
||||
│ ├── soleprint.config.json
|
||||
│ └── amar/ # AMAR room config
|
||||
├── ctrl/ # Docker scripts
|
||||
│
|
||||
├── artery/ # Connectors
|
||||
│ ├── veins/ # Jira, Slack, Google
|
||||
│ ├── shunts/ # Fake connectors for testing
|
||||
│ └── plexus/ # Full apps (backend + frontend)
|
||||
│
|
||||
├── atlas/ # Documentation
|
||||
│ └── books/ # Soleprint docs only
|
||||
│
|
||||
├── station/ # Tools & monitors
|
||||
│ ├── tools/ # modelgen, tester, datagen
|
||||
│ └── monitors/ # databrowse
|
||||
│
|
||||
├── soleprint/ # Core (versioned)
|
||||
│
|
||||
├── gen/ # Built instances (gitignored)
|
||||
│ ├── standalone/ # Base soleprint
|
||||
│ └── amar/ # With amar config
|
||||
│
|
||||
└── mainroom/ # Orchestration with managed room
|
||||
├── amar -> cfg/amar
|
||||
├── soleprint/ # Soleprint Docker config
|
||||
└── ctrl/ # start, stop, deploy scripts
|
||||
```
|
||||
|
||||
## Artery Hierarchy
|
||||
|
||||
```
|
||||
Vein ──► Pulse ──► Plexus
|
||||
│ │ │
|
||||
│ │ └── Full app (backend + frontend + DB)
|
||||
│ └── Composed (Vein + Room + Depot)
|
||||
└── Stateless API connector
|
||||
|
||||
Shunt ── Fake connector for testing
|
||||
```
|
||||
|
||||
## Ports
|
||||
|
||||
| Service | Port |
|
||||
|---------|------|
|
||||
| Soleprint | 12000 |
|
||||
| Amar Backend | 8000 |
|
||||
| Amar Frontend | 3000 |
|
||||
|
||||
## Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| modelgen | Generate models from config |
|
||||
| tester | BDD/playwright test runner |
|
||||
| datagen | Test data generation |
|
||||
| databrowse | SQL data browser |
|
||||
|
||||
## Veins
|
||||
|
||||
| Vein | Auth |
|
||||
|------|------|
|
||||
| jira | Token |
|
||||
| slack | Token |
|
||||
| google | OAuth2 |
|
||||
|
||||
---
|
||||
|
||||
*Built for small teams who need to move fast without breaking things.*
|
||||
Visit `http://localhost:8080`. Start with [Concepts](docs/data/en/concepts.md) for the mental model, then [Quickstart](docs/data/en/quickstart.md) to run your first room.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Amar (MOCK) Vein Configuration
|
||||
API_PORT=8005
|
||||
|
||||
# Mock data settings
|
||||
MOCK_DATA_PATH=./mock_data
|
||||
|
||||
# Mock behavior
|
||||
ENABLE_RANDOM_DELAYS=true
|
||||
MIN_DELAY_MS=100
|
||||
MAX_DELAY_MS=500
|
||||
|
||||
# Simulate errors
|
||||
ERROR_RATE=0.0 # 0.0 to 1.0 (0% to 100%)
|
||||
@@ -1,173 +0,0 @@
|
||||
# Amar (MOCK) Vein
|
||||
|
||||
Mock Amar API for testing - returns predictable test data for turnero flow testing.
|
||||
|
||||
## Purpose
|
||||
|
||||
Enables testing of the turnero flow (VET-535-540) without hitting the real Amar backend:
|
||||
- Guest petowner creation (VET-536)
|
||||
- Pet creation (VET-537)
|
||||
- Cart creation and price calculation (VET-538)
|
||||
- Service/category filtering (VET-539, VET-540)
|
||||
- Service request creation
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Start the mock server
|
||||
python run.py
|
||||
|
||||
# API docs: http://localhost:8005/docs
|
||||
# Health check: http://localhost:8005/health
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy `.env.example` to `.env` and adjust:
|
||||
|
||||
```bash
|
||||
API_PORT=8005 # Server port
|
||||
ENABLE_RANDOM_DELAYS=true # Add realistic delays
|
||||
MIN_DELAY_MS=100 # Minimum delay
|
||||
MAX_DELAY_MS=500 # Maximum delay
|
||||
ERROR_RATE=0.0 # Error rate (0.0 to 1.0)
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Turnero Flow (VET-536-540)
|
||||
|
||||
```bash
|
||||
# Create guest petowner (VET-536)
|
||||
POST /api/v1/pet-owners/
|
||||
{
|
||||
"address": "Av. Santa Fe 1234, Palermo"
|
||||
}
|
||||
|
||||
# Create pet (VET-537)
|
||||
POST /api/v1/pets/
|
||||
{
|
||||
"owner_id": 1234,
|
||||
"name": "Luna",
|
||||
"species": "DOG",
|
||||
"age": 3,
|
||||
"age_unit": "years"
|
||||
}
|
||||
|
||||
# Create cart (VET-538)
|
||||
POST /api/v1/cart/
|
||||
{
|
||||
"owner_id": 1234
|
||||
}
|
||||
|
||||
# Add item to cart
|
||||
POST /api/v1/cart/{cart_id}/items/
|
||||
{
|
||||
"service_id": 1,
|
||||
"pet_id": 5678,
|
||||
"quantity": 1
|
||||
}
|
||||
|
||||
# List services (VET-540)
|
||||
GET /api/v1/services/?species=DOG&neighborhood_id=1
|
||||
|
||||
# List categories (VET-539)
|
||||
GET /api/v1/categories/?species=DOG&neighborhood_id=1
|
||||
|
||||
# Create service request
|
||||
POST /solicitudes/service-requests/?cart_id=12345
|
||||
```
|
||||
|
||||
### Mock Control
|
||||
|
||||
```bash
|
||||
# Get mock database stats
|
||||
GET /mock/stats
|
||||
|
||||
# Reset mock database
|
||||
GET /mock/reset
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
All responses include `_mock: true` to identify mock data:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1234,
|
||||
"address": "Av. Santa Fe 1234, Palermo",
|
||||
"is_guest": true,
|
||||
"_mock": true
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### Complete Turnero Flow
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
BASE_URL = "http://localhost:8005"
|
||||
|
||||
# Step 1: Create guest petowner
|
||||
owner_resp = requests.post(f"{BASE_URL}/api/v1/pet-owners/", json={
|
||||
"address": "Av. Santa Fe 1234, Palermo"
|
||||
})
|
||||
owner = owner_resp.json()
|
||||
|
||||
# Step 2: Create pet
|
||||
pet_resp = requests.post(f"{BASE_URL}/api/v1/pets/", json={
|
||||
"owner_id": owner["id"],
|
||||
"name": "Luna",
|
||||
"species": "DOG",
|
||||
"age": 3,
|
||||
"age_unit": "years"
|
||||
})
|
||||
pet = pet_resp.json()
|
||||
|
||||
# Step 3: Create cart
|
||||
cart_resp = requests.post(f"{BASE_URL}/api/v1/cart/", json={
|
||||
"owner_id": owner["id"]
|
||||
})
|
||||
cart = cart_resp.json()
|
||||
|
||||
# Step 4: Get available services
|
||||
services_resp = requests.get(
|
||||
f"{BASE_URL}/api/v1/services/",
|
||||
params={"species": "DOG", "neighborhood_id": owner["neighborhood"]["id"]}
|
||||
)
|
||||
services = services_resp.json()
|
||||
|
||||
# Step 5: Add service to cart
|
||||
cart_resp = requests.post(f"{BASE_URL}/api/v1/cart/{cart['id']}/items/", json={
|
||||
"service_id": services[0]["id"],
|
||||
"pet_id": pet["id"],
|
||||
"quantity": 1
|
||||
})
|
||||
cart = cart_resp.json()
|
||||
print(f"Cart total: ${cart['resume']['total']}")
|
||||
|
||||
# Step 6: Create service request
|
||||
request_resp = requests.post(
|
||||
f"{BASE_URL}/solicitudes/service-requests/",
|
||||
params={"cart_id": cart["id"]}
|
||||
)
|
||||
service_request = request_resp.json()
|
||||
print(f"Service request created: {service_request['id']}")
|
||||
```
|
||||
|
||||
## Data Generator
|
||||
|
||||
This vein uses the independent `datagen` tool from `ward/tools/datagen/amar.py`.
|
||||
See `ward/tools/datagen/README.md` for data generation details.
|
||||
|
||||
## Notes
|
||||
|
||||
- Mock database is in-memory (resets on server restart)
|
||||
- Use `/mock/reset` to clear data during testing
|
||||
- All IDs are randomly generated on creation
|
||||
- Multi-pet discounts are automatically calculated
|
||||
@@ -1 +0,0 @@
|
||||
"""Amar (MOCK) vein - Mock Amar API for testing."""
|
||||
@@ -1 +0,0 @@
|
||||
"""API routes for Amar mock vein."""
|
||||
@@ -1,302 +0,0 @@
|
||||
"""API routes for Amar (MOCK) vein - Mock Amar API for testing."""
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from fastapi.responses import JSONResponse
|
||||
from typing import Optional, List, Dict, Any
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Import datagen from ward/tools
|
||||
import sys
|
||||
from pathlib import Path
|
||||
ward_tools_path = Path(__file__).parent.parent.parent.parent.parent / "ward" / "tools"
|
||||
sys.path.insert(0, str(ward_tools_path))
|
||||
|
||||
from datagen.amar import AmarDataGenerator
|
||||
|
||||
from ..core.config import settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# In-memory storage for mock data (reset on restart)
|
||||
MOCK_DB = {
|
||||
"petowners": {},
|
||||
"pets": {},
|
||||
"carts": {},
|
||||
"service_requests": {},
|
||||
}
|
||||
|
||||
|
||||
# Request/Response Models
|
||||
class CreatePetOwnerRequest(BaseModel):
|
||||
address: str
|
||||
email: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
|
||||
|
||||
class CreatePetRequest(BaseModel):
|
||||
owner_id: int
|
||||
name: str
|
||||
species: str # DOG, CAT
|
||||
age: Optional[int] = None
|
||||
age_unit: str = "years" # years, months
|
||||
|
||||
|
||||
class CreateCartRequest(BaseModel):
|
||||
owner_id: int
|
||||
|
||||
|
||||
class AddCartItemRequest(BaseModel):
|
||||
service_id: int
|
||||
pet_id: int
|
||||
quantity: int = 1
|
||||
|
||||
|
||||
async def _mock_delay():
|
||||
"""Add realistic delay if enabled."""
|
||||
if settings.enable_random_delays:
|
||||
delay_ms = random.randint(settings.min_delay_ms, settings.max_delay_ms)
|
||||
await asyncio.sleep(delay_ms / 1000)
|
||||
|
||||
|
||||
def _maybe_error():
|
||||
"""Randomly raise an error based on error_rate."""
|
||||
if random.random() < settings.error_rate:
|
||||
raise HTTPException(500, "Mock error: Simulated failure")
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def health():
|
||||
"""Health check endpoint."""
|
||||
return {
|
||||
"status": "ok",
|
||||
"vein": "Amar\n(MOCK)",
|
||||
"message": "Mock Amar API for testing",
|
||||
"_mock": True,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/api/v1/pet-owners/")
|
||||
async def create_petowner(request: CreatePetOwnerRequest):
|
||||
"""Create a guest petowner (VET-536)."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
owner = AmarDataGenerator.petowner(
|
||||
address=request.address,
|
||||
is_guest=True,
|
||||
email=request.email,
|
||||
phone=request.phone,
|
||||
)
|
||||
|
||||
# Store in mock DB
|
||||
MOCK_DB["petowners"][owner["id"]] = owner
|
||||
|
||||
owner["_mock"] = True
|
||||
return owner
|
||||
|
||||
|
||||
@router.get("/api/v1/pet-owners/{owner_id}")
|
||||
async def get_petowner(owner_id: int):
|
||||
"""Get petowner by ID."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
owner = MOCK_DB["petowners"].get(owner_id)
|
||||
if not owner:
|
||||
raise HTTPException(404, f"PetOwner {owner_id} not found")
|
||||
|
||||
return owner
|
||||
|
||||
|
||||
@router.post("/api/v1/pets/")
|
||||
async def create_pet(request: CreatePetRequest):
|
||||
"""Create a pet (VET-537)."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
# Verify owner exists
|
||||
if request.owner_id not in MOCK_DB["petowners"]:
|
||||
raise HTTPException(404, f"Owner {request.owner_id} not found")
|
||||
|
||||
pet = AmarDataGenerator.pet(
|
||||
owner_id=request.owner_id,
|
||||
name=request.name,
|
||||
species=request.species,
|
||||
age_value=request.age,
|
||||
age_unit=request.age_unit,
|
||||
)
|
||||
|
||||
# Store in mock DB
|
||||
MOCK_DB["pets"][pet["id"]] = pet
|
||||
|
||||
pet["_mock"] = True
|
||||
return pet
|
||||
|
||||
|
||||
@router.get("/api/v1/pets/{pet_id}")
|
||||
async def get_pet(pet_id: int):
|
||||
"""Get pet by ID."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
pet = MOCK_DB["pets"].get(pet_id)
|
||||
if not pet:
|
||||
raise HTTPException(404, f"Pet {pet_id} not found")
|
||||
|
||||
return pet
|
||||
|
||||
|
||||
@router.post("/api/v1/cart/")
|
||||
async def create_cart(request: CreateCartRequest):
|
||||
"""Create a cart (VET-538)."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
# Verify owner exists
|
||||
if request.owner_id not in MOCK_DB["petowners"]:
|
||||
raise HTTPException(404, f"Owner {request.owner_id} not found")
|
||||
|
||||
cart = AmarDataGenerator.cart(owner_id=request.owner_id)
|
||||
|
||||
# Store in mock DB
|
||||
MOCK_DB["carts"][cart["id"]] = cart
|
||||
|
||||
cart["_mock"] = True
|
||||
return cart
|
||||
|
||||
|
||||
@router.post("/api/v1/cart/{cart_id}/items/")
|
||||
async def add_cart_item(cart_id: int, request: AddCartItemRequest):
|
||||
"""Add item to cart and recalculate summary."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
cart = MOCK_DB["carts"].get(cart_id)
|
||||
if not cart:
|
||||
raise HTTPException(404, f"Cart {cart_id} not found")
|
||||
|
||||
# Get service price
|
||||
services = AmarDataGenerator.SERVICES
|
||||
service = next((s for s in services if s["id"] == request.service_id), None)
|
||||
if not service:
|
||||
raise HTTPException(404, f"Service {request.service_id} not found")
|
||||
|
||||
# Add item
|
||||
item = {
|
||||
"service_id": request.service_id,
|
||||
"service_name": service["name"],
|
||||
"pet_id": request.pet_id,
|
||||
"quantity": request.quantity,
|
||||
"price": service["price"],
|
||||
}
|
||||
|
||||
cart["items"].append(item)
|
||||
|
||||
# Recalculate summary
|
||||
cart = AmarDataGenerator.calculate_cart_summary(cart, cart["items"])
|
||||
MOCK_DB["carts"][cart_id] = cart
|
||||
|
||||
cart["_mock"] = True
|
||||
return cart
|
||||
|
||||
|
||||
@router.get("/api/v1/cart/{cart_id}")
|
||||
async def get_cart(cart_id: int):
|
||||
"""Get cart by ID."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
cart = MOCK_DB["carts"].get(cart_id)
|
||||
if not cart:
|
||||
raise HTTPException(404, f"Cart {cart_id} not found")
|
||||
|
||||
return cart
|
||||
|
||||
|
||||
@router.get("/api/v1/services/")
|
||||
async def list_services(
|
||||
species: Optional[str] = Query(None),
|
||||
neighborhood_id: Optional[int] = Query(None),
|
||||
):
|
||||
"""List available services filtered by species and neighborhood (VET-540)."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
services = AmarDataGenerator.filter_services(
|
||||
species=species,
|
||||
neighborhood_id=neighborhood_id,
|
||||
)
|
||||
|
||||
for service in services:
|
||||
service["_mock"] = True
|
||||
|
||||
return services
|
||||
|
||||
|
||||
@router.get("/api/v1/categories/")
|
||||
async def list_categories(
|
||||
species: Optional[str] = Query(None),
|
||||
neighborhood_id: Optional[int] = Query(None),
|
||||
):
|
||||
"""List categories with available services (VET-539)."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
categories = AmarDataGenerator.filter_categories(
|
||||
species=species,
|
||||
neighborhood_id=neighborhood_id,
|
||||
)
|
||||
|
||||
for category in categories:
|
||||
category["_mock"] = True
|
||||
|
||||
return categories
|
||||
|
||||
|
||||
@router.post("/solicitudes/service-requests/")
|
||||
async def create_service_request(cart_id: int, requested_date: Optional[str] = None):
|
||||
"""Create a service request."""
|
||||
await _mock_delay()
|
||||
_maybe_error()
|
||||
|
||||
cart = MOCK_DB["carts"].get(cart_id)
|
||||
if not cart:
|
||||
raise HTTPException(404, f"Cart {cart_id} not found")
|
||||
|
||||
request = AmarDataGenerator.service_request(
|
||||
cart_id=cart_id,
|
||||
requested_date=requested_date,
|
||||
)
|
||||
|
||||
MOCK_DB["service_requests"][request["id"]] = request
|
||||
|
||||
request["_mock"] = True
|
||||
return request
|
||||
|
||||
|
||||
@router.get("/mock/reset")
|
||||
async def reset_mock_db():
|
||||
"""Reset the mock database (useful for testing)."""
|
||||
MOCK_DB["petowners"].clear()
|
||||
MOCK_DB["pets"].clear()
|
||||
MOCK_DB["carts"].clear()
|
||||
MOCK_DB["service_requests"].clear()
|
||||
|
||||
return {
|
||||
"message": "Mock database reset",
|
||||
"_mock": True,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/mock/stats")
|
||||
async def mock_stats():
|
||||
"""Get mock database statistics."""
|
||||
return {
|
||||
"petowners": len(MOCK_DB["petowners"]),
|
||||
"pets": len(MOCK_DB["pets"]),
|
||||
"carts": len(MOCK_DB["carts"]),
|
||||
"service_requests": len(MOCK_DB["service_requests"]),
|
||||
"_mock": True,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
"""Core logic for Amar mock API."""
|
||||
@@ -1,27 +0,0 @@
|
||||
"""Configuration for Amar mock vein."""
|
||||
|
||||
from pathlib import Path
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
ENV_FILE = Path(__file__).parent.parent / ".env"
|
||||
|
||||
|
||||
class AmarMockConfig(BaseSettings):
|
||||
"""Configuration for Amar (MOCK) vein."""
|
||||
|
||||
api_port: int = 8005
|
||||
mock_data_path: str = "./mock_data"
|
||||
|
||||
# Mock behavior
|
||||
enable_random_delays: bool = True
|
||||
min_delay_ms: int = 100
|
||||
max_delay_ms: int = 500
|
||||
error_rate: float = 0.0 # 0.0 to 1.0
|
||||
|
||||
model_config = {
|
||||
"env_file": ENV_FILE if ENV_FILE.exists() else None,
|
||||
"env_file_encoding": "utf-8",
|
||||
}
|
||||
|
||||
|
||||
settings = AmarMockConfig()
|
||||
@@ -1,40 +0,0 @@
|
||||
"""Amar (MOCK) Vein - FastAPI app."""
|
||||
|
||||
from pathlib import Path
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from .api.routes import router
|
||||
from .core.config import settings
|
||||
|
||||
app = FastAPI(
|
||||
title="Amar API (MOCK)",
|
||||
description="Mock Amar API for testing - returns predictable test data",
|
||||
version="0.1.0",
|
||||
)
|
||||
|
||||
# Enable CORS for testing from frontend
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # In production, specify exact origins
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Templates for configuration UI
|
||||
templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
def index(request: Request):
|
||||
"""Mock configuration UI."""
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
# Include router at root (matches real Amar API structure)
|
||||
app.include_router(router)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=settings.api_port)
|
||||
@@ -1,18 +0,0 @@
|
||||
"""Standalone runner for Amar mock vein."""
|
||||
|
||||
import logging
|
||||
import uvicorn
|
||||
from main import app
|
||||
from core.config import settings
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info(f"Starting Amar (MOCK) vein on port {settings.api_port}")
|
||||
logger.info(f"API docs: http://localhost:{settings.api_port}/docs")
|
||||
logger.info(f"Health check: http://localhost:{settings.api_port}/health")
|
||||
uvicorn.run(app, host="0.0.0.0", port=settings.api_port, reload=True)
|
||||
@@ -1,298 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Amar API (MOCK) - Configuration</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #111827;
|
||||
color: #e5e7eb;
|
||||
padding: 20px;
|
||||
}
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
header {
|
||||
background: #f59e0b;
|
||||
color: #111827;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 8px; }
|
||||
.subtitle { opacity: 0.8; font-size: 0.875rem; }
|
||||
.mock-badge {
|
||||
display: inline-block;
|
||||
background: #111827;
|
||||
color: #f59e0b;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-left: 12px;
|
||||
}
|
||||
.section {
|
||||
background: #1f2937;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.section-header {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
color: #f9fafb;
|
||||
}
|
||||
.endpoint-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.endpoint-card {
|
||||
background: #374151;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.endpoint-card:hover { border-color: #f59e0b; background: #4b5563; }
|
||||
.endpoint-card.active { border-color: #f59e0b; background: #4b5563; }
|
||||
.endpoint-method {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.method-post { background: #10b981; color: white; }
|
||||
.method-get { background: #3b82f6; color: white; }
|
||||
.endpoint-path { font-family: monospace; font-size: 0.875rem; }
|
||||
.endpoint-desc { font-size: 0.75rem; color: #9ca3af; margin-top: 6px; }
|
||||
.form-group { margin-bottom: 16px; }
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
color: #f9fafb;
|
||||
}
|
||||
.form-input, .form-textarea {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
background: #374151;
|
||||
border: 1px solid #4b5563;
|
||||
border-radius: 6px;
|
||||
color: #e5e7eb;
|
||||
font-size: 0.875rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
.form-textarea { min-height: 200px; font-family: monospace; }
|
||||
.form-input:focus, .form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #f59e0b;
|
||||
}
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.btn-primary {
|
||||
background: #f59e0b;
|
||||
color: #111827;
|
||||
}
|
||||
.btn-primary:hover { background: #d97706; }
|
||||
.btn-secondary {
|
||||
background: #4b5563;
|
||||
color: #e5e7eb;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.btn-secondary:hover { background: #6b7280; }
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.stat-card {
|
||||
background: #374151;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.stat-value { font-size: 1.5rem; font-weight: 600; color: #f59e0b; }
|
||||
.stat-label { font-size: 0.75rem; color: #9ca3af; margin-top: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Amar API <span class="mock-badge">MOCK</span></h1>
|
||||
<div class="subtitle">Configure mock responses for Amar backend endpoints</div>
|
||||
</header>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="section">
|
||||
<div class="section-header">Mock Database Stats</div>
|
||||
<div class="stats" id="stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">-</div>
|
||||
<div class="stat-label">Pet Owners</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">-</div>
|
||||
<div class="stat-label">Pets</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">-</div>
|
||||
<div class="stat-label">Carts</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">-</div>
|
||||
<div class="stat-label">Requests</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 16px;">
|
||||
<button class="btn btn-secondary" onclick="resetMock()">Reset Database</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Endpoint Configuration -->
|
||||
<div class="section">
|
||||
<div class="section-header">Configure Endpoint Responses</div>
|
||||
<div class="endpoint-list">
|
||||
<div class="endpoint-card" onclick="selectEndpoint('POST', '/api/v1/pet-owners/', 'petowner')">
|
||||
<div>
|
||||
<span class="endpoint-method method-post">POST</span>
|
||||
<span class="endpoint-path">/api/v1/pet-owners/</span>
|
||||
</div>
|
||||
<div class="endpoint-desc">Create guest pet owner (VET-536)</div>
|
||||
</div>
|
||||
<div class="endpoint-card" onclick="selectEndpoint('POST', '/api/v1/pets/', 'pet')">
|
||||
<div>
|
||||
<span class="endpoint-method method-post">POST</span>
|
||||
<span class="endpoint-path">/api/v1/pets/</span>
|
||||
</div>
|
||||
<div class="endpoint-desc">Create pet (VET-537)</div>
|
||||
</div>
|
||||
<div class="endpoint-card" onclick="selectEndpoint('POST', '/api/v1/cart/', 'cart')">
|
||||
<div>
|
||||
<span class="endpoint-method method-post">POST</span>
|
||||
<span class="endpoint-path">/api/v1/cart/</span>
|
||||
</div>
|
||||
<div class="endpoint-desc">Create cart (VET-538)</div>
|
||||
</div>
|
||||
<div class="endpoint-card" onclick="selectEndpoint('GET', '/api/v1/services/', 'services')">
|
||||
<div>
|
||||
<span class="endpoint-method method-get">GET</span>
|
||||
<span class="endpoint-path">/api/v1/services/</span>
|
||||
</div>
|
||||
<div class="endpoint-desc">List services (VET-540)</div>
|
||||
</div>
|
||||
<div class="endpoint-card" onclick="selectEndpoint('GET', '/api/v1/categories/', 'categories')">
|
||||
<div>
|
||||
<span class="endpoint-method method-get">GET</span>
|
||||
<span class="endpoint-path">/api/v1/categories/</span>
|
||||
</div>
|
||||
<div class="endpoint-desc">List categories (VET-539)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Response Editor -->
|
||||
<div class="section" id="responseEditor" style="display: none;">
|
||||
<div class="section-header">Edit Response</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Endpoint</label>
|
||||
<input class="form-input" id="endpointDisplay" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Mock Response (JSON)</label>
|
||||
<textarea class="form-textarea" id="responseJson" placeholder='{"id": 123, "name": "Luna", "_mock": true}'></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">HTTP Status Code</label>
|
||||
<input type="number" class="form-input" id="statusCode" value="200">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Delay (ms)</label>
|
||||
<input type="number" class="form-input" id="delay" value="0">
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary" onclick="saveResponse()">Save Response</button>
|
||||
<button class="btn btn-secondary" onclick="closeEditor()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Test -->
|
||||
<div class="section">
|
||||
<div class="section-header">Quick Test</div>
|
||||
<p style="color: #9ca3af; margin-bottom: 12px;">Test endpoint URL to hit for configured responses:</p>
|
||||
<div class="form-input" style="background: #374151; user-select: all;">
|
||||
http://localhost:8005/api/v1/pet-owners/
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let selectedEndpoint = null;
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const resp = await fetch('/mock/stats');
|
||||
const data = await resp.json();
|
||||
document.getElementById('stats').innerHTML = `
|
||||
<div class="stat-card"><div class="stat-value">${data.pet_owners || 0}</div><div class="stat-label">Pet Owners</div></div>
|
||||
<div class="stat-card"><div class="stat-value">${data.pets || 0}</div><div class="stat-label">Pets</div></div>
|
||||
<div class="stat-card"><div class="stat-value">${data.carts || 0}</div><div class="stat-label">Carts</div></div>
|
||||
<div class="stat-card"><div class="stat-value">${data.service_requests || 0}</div><div class="stat-label">Requests</div></div>
|
||||
`;
|
||||
} catch (e) {
|
||||
console.error('Failed to load stats:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function resetMock() {
|
||||
if (confirm('Reset all mock data?')) {
|
||||
await fetch('/mock/reset');
|
||||
loadStats();
|
||||
alert('Mock database reset');
|
||||
}
|
||||
}
|
||||
|
||||
function selectEndpoint(method, path, type) {
|
||||
selectedEndpoint = {method, path, type};
|
||||
document.querySelectorAll('.endpoint-card').forEach(c => c.classList.remove('active'));
|
||||
event.currentTarget.classList.add('active');
|
||||
document.getElementById('responseEditor').style.display = 'block';
|
||||
document.getElementById('endpointDisplay').value = `${method} ${path}`;
|
||||
document.getElementById('responseJson').value = getDefaultResponse(type);
|
||||
}
|
||||
|
||||
function getDefaultResponse(type) {
|
||||
const defaults = {
|
||||
petowner: JSON.stringify({"id": 1234, "address": "Av Santa Fe 1234", "is_guest": true, "_mock": true}, null, 2),
|
||||
pet: JSON.stringify({"id": 5678, "name": "Luna", "species": "DOG", "age": 3, "_mock": true}, null, 2),
|
||||
cart: JSON.stringify({"id": 9012, "items": [], "total": 0, "_mock": true}, null, 2),
|
||||
services: JSON.stringify([{"id": 1, "name": "Vacunación", "price": 1500, "_mock": true}], null, 2),
|
||||
categories: JSON.stringify([{"id": 1, "name": "Salud", "services": 5, "_mock": true}], null, 2)
|
||||
};
|
||||
return defaults[type] || '{}';
|
||||
}
|
||||
|
||||
function saveResponse() {
|
||||
alert('Mock response saved (feature pending implementation)');
|
||||
}
|
||||
|
||||
function closeEditor() {
|
||||
document.getElementById('responseEditor').style.display = 'none';
|
||||
selectedEndpoint = null;
|
||||
document.querySelectorAll('.endpoint-card').forEach(c => c.classList.remove('active'));
|
||||
}
|
||||
|
||||
loadStats();
|
||||
setInterval(loadStats, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,194 +0,0 @@
|
||||
"""
|
||||
API routes for Google vein.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from fastapi.responses import PlainTextResponse, RedirectResponse
|
||||
from typing import Optional
|
||||
|
||||
from core.oauth import GoogleOAuth
|
||||
from core.sheets import GoogleSheetsClient, GoogleSheetsError
|
||||
from models.spreadsheet import SpreadsheetMetadata, SheetValues
|
||||
from models.formatter import format_spreadsheet_metadata, format_sheet_values
|
||||
|
||||
# Import from parent vein module
|
||||
import sys
|
||||
from pathlib import Path
|
||||
vein_path = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(vein_path))
|
||||
from oauth import TokenStorage
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# OAuth client and token storage
|
||||
oauth_client = GoogleOAuth()
|
||||
token_storage = TokenStorage(vein_name="google")
|
||||
|
||||
# For demo/development, use a default user_id
|
||||
# In production, this would come from session/auth
|
||||
DEFAULT_USER_ID = "demo_user"
|
||||
|
||||
|
||||
def _get_sheets_client(user_id: str = DEFAULT_USER_ID) -> GoogleSheetsClient:
|
||||
"""Get authenticated Sheets client for user."""
|
||||
tokens = token_storage.load_tokens(user_id)
|
||||
if not tokens:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Not authenticated. Visit /google/oauth/start to login.",
|
||||
)
|
||||
|
||||
# Check if expired and refresh if needed
|
||||
if token_storage.is_expired(tokens):
|
||||
if "refresh_token" not in tokens:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Token expired and no refresh token. Re-authenticate at /google/oauth/start",
|
||||
)
|
||||
try:
|
||||
new_tokens = oauth_client.refresh_access_token(tokens["refresh_token"])
|
||||
token_storage.save_tokens(user_id, new_tokens)
|
||||
tokens = new_tokens
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=f"Failed to refresh token: {e}. Re-authenticate at /google/oauth/start",
|
||||
)
|
||||
|
||||
credentials = oauth_client.get_credentials(
|
||||
access_token=tokens["access_token"],
|
||||
refresh_token=tokens.get("refresh_token"),
|
||||
)
|
||||
return GoogleSheetsClient(credentials)
|
||||
|
||||
|
||||
def _maybe_text(data, text: bool, formatter):
|
||||
"""Return text or JSON based on query param."""
|
||||
if not text:
|
||||
return data
|
||||
return PlainTextResponse(formatter(data))
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def health():
|
||||
"""Check if user is authenticated."""
|
||||
try:
|
||||
tokens = token_storage.load_tokens(DEFAULT_USER_ID)
|
||||
if not tokens:
|
||||
return {
|
||||
"status": "not_authenticated",
|
||||
"message": "Visit /google/oauth/start to login",
|
||||
}
|
||||
|
||||
expired = token_storage.is_expired(tokens)
|
||||
return {
|
||||
"status": "ok" if not expired else "token_expired",
|
||||
"has_refresh_token": "refresh_token" in tokens,
|
||||
"user": DEFAULT_USER_ID,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@router.get("/oauth/start")
|
||||
async def start_oauth(state: Optional[str] = None):
|
||||
"""Start OAuth flow - redirect to Google authorization."""
|
||||
auth_url = oauth_client.get_authorization_url(state=state)
|
||||
return RedirectResponse(auth_url)
|
||||
|
||||
|
||||
@router.get("/oauth/callback")
|
||||
async def oauth_callback(
|
||||
code: Optional[str] = None,
|
||||
state: Optional[str] = None,
|
||||
error: Optional[str] = None,
|
||||
):
|
||||
"""Handle OAuth callback from Google."""
|
||||
if error:
|
||||
raise HTTPException(400, f"OAuth error: {error}")
|
||||
|
||||
if not code:
|
||||
raise HTTPException(400, "Missing authorization code")
|
||||
|
||||
try:
|
||||
tokens = oauth_client.exchange_code_for_tokens(code)
|
||||
token_storage.save_tokens(DEFAULT_USER_ID, tokens)
|
||||
return {
|
||||
"status": "ok",
|
||||
"message": "Successfully authenticated with Google",
|
||||
"user": DEFAULT_USER_ID,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(500, f"Failed to exchange code: {e}")
|
||||
|
||||
|
||||
@router.get("/oauth/logout")
|
||||
async def logout():
|
||||
"""Clear stored tokens."""
|
||||
token_storage.delete_tokens(DEFAULT_USER_ID)
|
||||
return {"status": "ok", "message": "Logged out"}
|
||||
|
||||
|
||||
@router.get("/spreadsheets/{spreadsheet_id}")
|
||||
async def get_spreadsheet(
|
||||
spreadsheet_id: str,
|
||||
text: bool = False,
|
||||
):
|
||||
"""Get spreadsheet metadata (title, sheets list, etc.)."""
|
||||
try:
|
||||
client = _get_sheets_client()
|
||||
metadata = client.get_spreadsheet_metadata(spreadsheet_id)
|
||||
result = SpreadsheetMetadata.from_google(metadata)
|
||||
return _maybe_text(result, text, format_spreadsheet_metadata)
|
||||
except GoogleSheetsError as e:
|
||||
raise HTTPException(404, str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@router.get("/spreadsheets/{spreadsheet_id}/values")
|
||||
async def get_sheet_values(
|
||||
spreadsheet_id: str,
|
||||
range: str = Query(..., description="A1 notation range (e.g., 'Sheet1!A1:D10')"),
|
||||
text: bool = False,
|
||||
max_rows: int = Query(100, ge=1, le=10000),
|
||||
):
|
||||
"""Get values from a sheet range."""
|
||||
try:
|
||||
client = _get_sheets_client()
|
||||
values = client.get_sheet_values(spreadsheet_id, range)
|
||||
result = SheetValues.from_google(spreadsheet_id, range, values)
|
||||
|
||||
if text:
|
||||
return PlainTextResponse(format_sheet_values(result, max_rows=max_rows))
|
||||
return result
|
||||
except GoogleSheetsError as e:
|
||||
raise HTTPException(404, str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@router.get("/spreadsheets/{spreadsheet_id}/sheets")
|
||||
async def list_sheets(
|
||||
spreadsheet_id: str,
|
||||
text: bool = False,
|
||||
):
|
||||
"""List all sheets in a spreadsheet."""
|
||||
try:
|
||||
client = _get_sheets_client()
|
||||
sheets = client.get_all_sheets(spreadsheet_id)
|
||||
|
||||
if text:
|
||||
lines = [f"Sheets in {spreadsheet_id}:", ""]
|
||||
for sheet in sheets:
|
||||
lines.append(
|
||||
f" [{sheet['index']}] {sheet['title']} "
|
||||
f"({sheet['row_count']} rows x {sheet['column_count']} cols)"
|
||||
)
|
||||
return PlainTextResponse("\n".join(lines))
|
||||
|
||||
return {"spreadsheet_id": spreadsheet_id, "sheets": sheets}
|
||||
except GoogleSheetsError as e:
|
||||
raise HTTPException(404, str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(500, str(e))
|
||||
@@ -1,24 +0,0 @@
|
||||
"""
|
||||
Google OAuth2 configuration loaded from .env file.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
ENV_FILE = Path(__file__).parent.parent / ".env"
|
||||
|
||||
|
||||
class GoogleConfig(BaseSettings):
|
||||
google_client_id: str
|
||||
google_client_secret: str
|
||||
google_redirect_uri: str # e.g., https://artery.mcrn.ar/google/oauth/callback
|
||||
google_scopes: str = "https://www.googleapis.com/auth/spreadsheets.readonly https://www.googleapis.com/auth/drive.readonly"
|
||||
api_port: int = 8003
|
||||
|
||||
model_config = {
|
||||
"env_file": ENV_FILE,
|
||||
"env_file_encoding": "utf-8",
|
||||
}
|
||||
|
||||
|
||||
settings = GoogleConfig()
|
||||
@@ -1,184 +0,0 @@
|
||||
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"]
|
||||
}
|
||||
@@ -1,585 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,184 +0,0 @@
|
||||
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]
|
||||
}
|
||||
@@ -1,595 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 51 KiB |
@@ -1,234 +0,0 @@
|
||||
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"]
|
||||
}
|
||||
@@ -1,976 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 84 KiB |
@@ -1,195 +0,0 @@
|
||||
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]
|
||||
}
|
||||
@@ -1,540 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,120 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,245 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,565 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,217 +0,0 @@
|
||||
# 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
|
||||
@@ -1,79 +0,0 @@
|
||||
# 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** | |
|
||||
@@ -1,335 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,92 +0,0 @@
|
||||
# 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
|
||||
@@ -1,106 +0,0 @@
|
||||
# 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
|
||||
@@ -1,98 +0,0 @@
|
||||
# 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
|
||||
@@ -1,82 +0,0 @@
|
||||
# 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
|
||||
@@ -1,99 +0,0 @@
|
||||
# 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
|
||||
@@ -1,55 +0,0 @@
|
||||
# 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
|
||||
@@ -1,77 +0,0 @@
|
||||
# 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
|
||||
@@ -1,79 +0,0 @@
|
||||
# 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
|
||||
@@ -1,66 +0,0 @@
|
||||
# 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
|
||||
@@ -1,79 +0,0 @@
|
||||
# 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
|
||||
@@ -1,77 +0,0 @@
|
||||
# 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
|
||||
@@ -1,87 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,97 +0,0 @@
|
||||
# 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
|
||||
@@ -1,66 +0,0 @@
|
||||
# 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
|
||||
@@ -1,81 +0,0 @@
|
||||
# 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
|
||||
@@ -1,143 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,91 +0,0 @@
|
||||
# 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?
|
||||
@@ -1,114 +0,0 @@
|
||||
# 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/`
|
||||
@@ -1,161 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,104 +0,0 @@
|
||||
# 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
|
||||
@@ -1,88 +0,0 @@
|
||||
# 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
|
||||
@@ -1,91 +0,0 @@
|
||||
# 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
|
||||
@@ -1,86 +0,0 @@
|
||||
# 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
|
||||
@@ -1,97 +0,0 @@
|
||||
# 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
|
||||
@@ -1,91 +0,0 @@
|
||||
# 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
|
||||
@@ -1,140 +0,0 @@
|
||||
# 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
|
||||
@@ -1,153 +0,0 @@
|
||||
# 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"
|
||||
@@ -1,140 +0,0 @@
|
||||
# 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
|
||||
@@ -1,147 +0,0 @@
|
||||
# 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
|
||||
@@ -1,109 +0,0 @@
|
||||
# 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
|
||||
@@ -1,99 +0,0 @@
|
||||
# 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
|
||||
@@ -1,127 +0,0 @@
|
||||
# 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
|
||||
@@ -1,83 +0,0 @@
|
||||
# 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
|
||||
@@ -1,100 +0,0 @@
|
||||
# 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
|
||||
@@ -1,158 +0,0 @@
|
||||
# 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
|
||||
@@ -1,131 +0,0 @@
|
||||
# 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
|
||||
@@ -1,131 +0,0 @@
|
||||
# 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
|
||||
@@ -1,141 +0,0 @@
|
||||
# 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 |
|
||||
@@ -1,155 +0,0 @@
|
||||
# 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
|
||||
@@ -1,92 +0,0 @@
|
||||
# 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
|
||||
@@ -1,141 +0,0 @@
|
||||
# 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
|
||||
@@ -1,154 +0,0 @@
|
||||
# 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"
|
||||
@@ -1,141 +0,0 @@
|
||||
# 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
|
||||
@@ -1,148 +0,0 @@
|
||||
# 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
|
||||
@@ -1,129 +0,0 @@
|
||||
# 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
|
||||
@@ -1,134 +0,0 @@
|
||||
# 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
|
||||
@@ -1,159 +0,0 @@
|
||||
# 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
|
||||
@@ -1,121 +0,0 @@
|
||||
# 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
|
||||
@@ -1,157 +0,0 @@
|
||||
# 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
|
||||
@@ -1,248 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,184 +0,0 @@
|
||||
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"]
|
||||
}
|
||||
@@ -1,585 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,184 +0,0 @@
|
||||
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]
|
||||
}
|
||||
@@ -1,595 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 51 KiB |
@@ -1,234 +0,0 @@
|
||||
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"]
|
||||
}
|
||||
@@ -1,976 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 84 KiB |
@@ -1,195 +0,0 @@
|
||||
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]
|
||||
}
|
||||
@@ -1,540 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,120 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,245 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,565 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,645 +0,0 @@
|
||||
<?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 2.42.4 (0)
|
||||
-->
|
||||
<!-- Title: MediaAnalyzer Pages: 1 -->
|
||||
<svg width="1979pt" height="2160pt"
|
||||
viewBox="0.00 0.00 1979.34 2160.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 2165.46)">
|
||||
<title>MediaAnalyzer</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-2165.46 1984,-2165.46 1984,4 -4,4"/>
|
||||
<g id="clust1" class="cluster">
|
||||
<title>cluster_legend</title>
|
||||
<polygon fill="lightgray" stroke="lightgray" points="8.47,-1739.35 8.47,-2138.17 249.75,-2138.17 249.75,-1739.35 8.47,-1739.35"/>
|
||||
<text text-anchor="middle" x="129.11" y="-2124.57" font-family="Arial" font-size="12.00">Legend</text>
|
||||
</g>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_sources</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="258.21,-1925.66 258.21,-2138.17 522.78,-2138.17 522.78,-1925.66 258.21,-1925.66"/>
|
||||
<text text-anchor="middle" x="390.5" y="-2124.57" font-family="Arial" font-size="12.00">Video Sources</text>
|
||||
</g>
|
||||
<g id="clust3" class="cluster">
|
||||
<title>cluster_adapters</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="531.25,-1739.35 531.25,-2138.17 837.08,-2138.17 837.08,-1739.35 531.25,-1739.35"/>
|
||||
<text text-anchor="middle" x="684.16" y="-2124.57" font-family="Arial" font-size="12.00">Source Adapters Pattern</text>
|
||||
</g>
|
||||
<g id="clust4" class="cluster">
|
||||
<title>cluster_core</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="641.3,-1158.6 641.3,-1689.87 846.61,-1689.87 846.61,-1158.6 641.3,-1158.6"/>
|
||||
<text text-anchor="middle" x="743.96" y="-1676.27" font-family="Arial" font-size="12.00">Core Platform</text>
|
||||
</g>
|
||||
<g id="clust5" class="cluster">
|
||||
<title>cluster_execution</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1665.7,-570.57 1665.7,-1052.35 1971.53,-1052.35 1971.53,-570.57 1665.7,-570.57"/>
|
||||
<text text-anchor="middle" x="1818.62" y="-1038.75" font-family="Arial" font-size="12.00">Execution Strategies Pattern</text>
|
||||
</g>
|
||||
<g id="clust6" class="cluster">
|
||||
<title>cluster_event_sources</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1200.06,-839.84 1200.06,-1371.11 1657.23,-1371.11 1657.23,-839.84 1200.06,-839.84"/>
|
||||
<text text-anchor="middle" x="1428.65" y="-1357.51" font-family="Arial" font-size="12.00">Event Source Pattern</text>
|
||||
</g>
|
||||
<g id="clust7" class="cluster">
|
||||
<title>cluster_events</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="1164.08,-23.29 1164.08,-783.07 1321.76,-783.07 1321.76,-23.29 1164.08,-23.29"/>
|
||||
<text text-anchor="middle" x="1242.92" y="-769.47" font-family="Arial" font-size="12.00">Event-Driven Processing</text>
|
||||
</g>
|
||||
<g id="clust8" class="cluster">
|
||||
<title>cluster_workers</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="386.26,-832.56 386.26,-1059.62 846.61,-1059.62 846.61,-832.56 386.26,-832.56"/>
|
||||
<text text-anchor="middle" x="616.44" y="-1046.02" font-family="Arial" font-size="12.00">Celery Workers</text>
|
||||
</g>
|
||||
<g id="clust9" class="cluster">
|
||||
<title>cluster_ai_adapters</title>
|
||||
<polygon fill="#8e24aa" stroke="#8e24aa" points="1330.23,-334.77 1330.23,-783.07 1657.23,-783.07 1657.23,-334.77 1330.23,-334.77"/>
|
||||
<text text-anchor="middle" x="1493.73" y="-769.47" font-family="Arial" font-size="12.00">Analysis Adapters Pattern</text>
|
||||
</g>
|
||||
<g id="clust10" class="cluster">
|
||||
<title>cluster_storage</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="988.41,-1158.6 988.41,-1371.11 1191.6,-1371.11 1191.6,-1158.6 988.41,-1158.6"/>
|
||||
<text text-anchor="middle" x="1090.01" y="-1357.51" font-family="Arial" font-size="12.00">Media Storage</text>
|
||||
</g>
|
||||
<g id="clust11" class="cluster">
|
||||
<title>cluster_frontend</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="855.07,-839.84 855.07,-1378.38 979.95,-1378.38 979.95,-839.84 855.07,-839.84"/>
|
||||
<text text-anchor="middle" x="917.51" y="-1364.78" font-family="Arial" font-size="12.00">Frontend</text>
|
||||
</g>
|
||||
<g id="clust12" class="cluster">
|
||||
<title>cluster_cloud</title>
|
||||
<polygon fill="#1976d2" stroke="#1976d2" points="1569.4,-23.29 1569.4,-235.8 1813.85,-235.8 1813.85,-23.29 1569.4,-23.29"/>
|
||||
<text text-anchor="middle" x="1691.62" y="-222.2" font-family="Arial" font-size="12.00">GCP Services</text>
|
||||
</g>
|
||||
<!-- implemented -->
|
||||
<g id="node1" class="node">
|
||||
<title>implemented</title>
|
||||
<polygon fill="#28a745" stroke="transparent" points="239.18,-2019.29 150.18,-2019.29 150.18,-1983.29 239.18,-1983.29 239.18,-2019.29"/>
|
||||
<text text-anchor="middle" x="194.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">✅ Implemented</text>
|
||||
</g>
|
||||
<!-- planned -->
|
||||
<g id="node2" class="node">
|
||||
<title>planned</title>
|
||||
<polygon fill="#ffc107" stroke="transparent" points="239.68,-1833.29 171.68,-1833.29 171.68,-1797.29 239.68,-1797.29 239.68,-1833.29"/>
|
||||
<text text-anchor="middle" x="205.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#212529">⏳ Planned</text>
|
||||
</g>
|
||||
<!-- implemented->planned -->
|
||||
<!-- code_pattern -->
|
||||
<g id="node3" class="node">
|
||||
<title>code_pattern</title>
|
||||
<polygon fill="#8e24aa" stroke="transparent" points="121.68,-2019.29 31.68,-2019.29 31.68,-1983.29 121.68,-1983.29 121.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="76.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">📐 Code Pattern</text>
|
||||
</g>
|
||||
<!-- service_arch -->
|
||||
<g id="node4" class="node">
|
||||
<title>service_arch</title>
|
||||
<polygon fill="#1976d2" stroke="transparent" points="142.18,-1833.29 21.18,-1833.29 21.18,-1797.29 142.18,-1797.29 142.18,-1833.29"/>
|
||||
<text text-anchor="middle" x="81.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">⚡ Service/Architecture</text>
|
||||
</g>
|
||||
<!-- code_pattern->service_arch -->
|
||||
<!-- webcam -->
|
||||
<g id="node5" class="node">
|
||||
<title>webcam</title>
|
||||
<polygon fill="#28a745" stroke="black" points="324.18,-2019.29 269.18,-2019.29 269.18,-1983.29 324.18,-1983.29 324.18,-2019.29"/>
|
||||
<text text-anchor="middle" x="296.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">Webcam</text>
|
||||
</g>
|
||||
<!-- webcam_adapter -->
|
||||
<g id="node9" class="node">
|
||||
<title>webcam_adapter</title>
|
||||
<polygon fill="#28a745" stroke="black" points="631.18,-1833.29 542.18,-1833.29 542.18,-1797.29 631.18,-1797.29 631.18,-1833.29"/>
|
||||
<text text-anchor="middle" x="586.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">WebcamAdapter</text>
|
||||
</g>
|
||||
<!-- webcam->webcam_adapter -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>webcam->webcam_adapter</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M318.21,-1983.19C344.52,-1962.61 390.72,-1927.49 432.68,-1900.79 469.09,-1877.62 512.31,-1854.24 543.69,-1837.97"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="545.33,-1841.06 552.61,-1833.36 542.12,-1834.83 545.33,-1841.06"/>
|
||||
</g>
|
||||
<!-- rtmp -->
|
||||
<g id="node6" class="node">
|
||||
<title>rtmp</title>
|
||||
<polygon fill="#28a745" stroke="black" points="512.68,-2019.29 444.68,-2019.29 444.68,-1983.29 512.68,-1983.29 512.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="478.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#ffffff">RTMP/OBS</text>
|
||||
</g>
|
||||
<!-- rtmp_adapter -->
|
||||
<g id="node10" class="node">
|
||||
<title>rtmp_adapter</title>
|
||||
<polygon fill="#28a745" stroke="black" points="826.68,-1833.29 752.68,-1833.29 752.68,-1797.29 826.68,-1797.29 826.68,-1833.29"/>
|
||||
<text text-anchor="middle" x="789.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#ffffff">RtmpAdapter</text>
|
||||
</g>
|
||||
<!-- rtmp->rtmp_adapter -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>rtmp->rtmp_adapter</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M507.66,-1983.14C564.14,-1949.73 688.93,-1875.9 751.84,-1838.67"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="753.84,-1841.56 760.66,-1833.46 750.27,-1835.54 753.84,-1841.56"/>
|
||||
</g>
|
||||
<!-- files -->
|
||||
<g id="node7" class="node">
|
||||
<title>files</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="416.68,-2019.29 350.68,-2019.29 350.68,-1983.29 416.68,-1983.29 416.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="383.68" y="-1998.79" font-family="Arial" font-size="10.00" fill="#212529">File Upload</text>
|
||||
</g>
|
||||
<!-- file_adapter -->
|
||||
<g id="node11" class="node">
|
||||
<title>file_adapter</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="724.68,-1833.29 658.68,-1833.29 658.68,-1797.29 724.68,-1797.29 724.68,-1833.29"/>
|
||||
<text text-anchor="middle" x="691.68" y="-1812.79" font-family="Arial" font-size="10.00" fill="#212529">FileAdapter</text>
|
||||
</g>
|
||||
<!-- files->file_adapter -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>files->file_adapter</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M406.89,-1983.22C435.24,-1962.66 484.94,-1927.58 529.68,-1900.79 569.19,-1877.13 615.98,-1853.13 649.1,-1836.79"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="651.05,-1839.73 658.48,-1832.18 647.96,-1833.44 651.05,-1839.73"/>
|
||||
</g>
|
||||
<!-- base_adapter -->
|
||||
<g id="node8" class="node">
|
||||
<title>base_adapter</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="743.68,-2019.29 639.68,-2019.29 639.68,-1983.29 743.68,-1983.29 743.68,-2019.29"/>
|
||||
<text text-anchor="middle" x="691.68" y="-2004.29" font-family="Arial" font-size="10.00">BaseSourceAdapter</text>
|
||||
<text text-anchor="middle" x="691.68" y="-1993.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- base_adapter->webcam_adapter -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>base_adapter->webcam_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M681.8,-1982.98C663.31,-1950.57 623.36,-1880.56 601.58,-1842.39"/>
|
||||
<polygon fill="gray" stroke="gray" points="604.45,-1840.37 596.46,-1833.42 598.37,-1843.84 604.45,-1840.37"/>
|
||||
</g>
|
||||
<!-- base_adapter->rtmp_adapter -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>base_adapter->rtmp_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M700.9,-1982.98C718.16,-1950.57 755.45,-1880.56 775.78,-1842.39"/>
|
||||
<polygon fill="gray" stroke="gray" points="778.95,-1843.89 780.56,-1833.42 772.77,-1840.6 778.95,-1843.89"/>
|
||||
</g>
|
||||
<!-- base_adapter->file_adapter -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>base_adapter->file_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M691.68,-1982.98C691.68,-1950.98 691.68,-1882.33 691.68,-1843.86"/>
|
||||
<polygon fill="gray" stroke="gray" points="695.18,-1843.42 691.68,-1833.42 688.18,-1843.42 695.18,-1843.42"/>
|
||||
</g>
|
||||
<!-- django -->
|
||||
<g id="node12" class="node">
|
||||
<title>django</title>
|
||||
<polygon fill="#28a745" stroke="black" points="725.18,-1565.79 658.18,-1565.79 658.18,-1524.79 725.18,-1524.79 725.18,-1565.79"/>
|
||||
<text text-anchor="middle" x="691.68" y="-1553.79" font-family="Arial" font-size="10.00" fill="#ffffff">Django API</text>
|
||||
<text text-anchor="middle" x="691.68" y="-1542.79" font-family="Arial" font-size="10.00" fill="#ffffff">+ Channels</text>
|
||||
<text text-anchor="middle" x="691.68" y="-1531.79" font-family="Arial" font-size="10.00" fill="#ffffff">:8000</text>
|
||||
</g>
|
||||
<!-- webcam_adapter->django -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>webcam_adapter->django</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M593.41,-1797.12C611,-1752.21 658.51,-1630.94 680.38,-1575.12"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="683.64,-1576.4 684.03,-1565.81 677.13,-1573.84 683.64,-1576.4"/>
|
||||
</g>
|
||||
<!-- rtmp_adapter->django -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>rtmp_adapter->django</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M783.4,-1797.12C767.02,-1752.31 722.82,-1631.45 702.35,-1575.47"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="705.54,-1574 698.82,-1565.81 698.97,-1576.4 705.54,-1574"/>
|
||||
</g>
|
||||
<!-- file_adapter->django -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>file_adapter->django</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M691.68,-1797.12C691.68,-1752.4 691.68,-1631.95 691.68,-1575.82"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="695.18,-1575.81 691.68,-1565.81 688.18,-1575.81 695.18,-1575.81"/>
|
||||
</g>
|
||||
<!-- postgres -->
|
||||
<g id="node13" class="node">
|
||||
<title>postgres</title>
|
||||
<polygon fill="#28a745" stroke="black" points="835.68,-1252.29 765.68,-1252.29 765.68,-1216.29 835.68,-1216.29 835.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="800.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">PostgreSQL</text>
|
||||
<text text-anchor="middle" x="800.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">Database</text>
|
||||
</g>
|
||||
<!-- django->postgres -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>django->postgres</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M698.67,-1524.49C717.54,-1470.97 769.61,-1323.37 791.15,-1262.29"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="794.55,-1263.19 794.58,-1252.6 787.95,-1260.86 794.55,-1263.19"/>
|
||||
</g>
|
||||
<!-- redis -->
|
||||
<g id="node14" class="node">
|
||||
<title>redis</title>
|
||||
<polygon fill="#28a745" stroke="black" points="738.68,-1252.29 652.68,-1252.29 652.68,-1216.29 738.68,-1216.29 738.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="695.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">Redis</text>
|
||||
<text text-anchor="middle" x="695.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">Cache & Broker</text>
|
||||
</g>
|
||||
<!-- django->redis -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>django->redis</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M691.94,-1524.49C692.63,-1471.08 694.53,-1323.98 695.33,-1262.67"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="698.83,-1262.64 695.46,-1252.6 691.83,-1262.55 698.83,-1262.64"/>
|
||||
</g>
|
||||
<!-- local_storage -->
|
||||
<g id="node35" class="node">
|
||||
<title>local_storage</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1076.68,-1252.29 998.68,-1252.29 998.68,-1216.29 1076.68,-1216.29 1076.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="1037.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#ffffff">Local Files</text>
|
||||
<text text-anchor="middle" x="1037.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#ffffff">(nginx-served)</text>
|
||||
</g>
|
||||
<!-- django->local_storage -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>django->local_storage</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M716.06,-1524.54C742.04,-1503.33 784.24,-1468.48 819.68,-1437.29 890.85,-1374.64 972.16,-1297.86 1012.4,-1259.51"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1015.06,-1261.8 1019.88,-1252.36 1010.23,-1256.74 1015.06,-1261.8"/>
|
||||
</g>
|
||||
<!-- gcs_storage -->
|
||||
<g id="node36" class="node">
|
||||
<title>gcs_storage</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1180.68,-1252.29 1104.68,-1252.29 1104.68,-1216.29 1180.68,-1216.29 1180.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="1142.68" y="-1237.29" font-family="Arial" font-size="10.00" fill="#212529">Google Cloud</text>
|
||||
<text text-anchor="middle" x="1142.68" y="-1226.29" font-family="Arial" font-size="10.00" fill="#212529">Storage</text>
|
||||
</g>
|
||||
<!-- django->gcs_storage -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>django->gcs_storage</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M724.82,-1524.68C794.21,-1483.15 959.01,-1382.07 1087.68,-1283.79 1097.54,-1276.26 1107.86,-1267.43 1116.87,-1259.39"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1119.33,-1261.88 1124.4,-1252.57 1114.64,-1256.69 1119.33,-1261.88"/>
|
||||
</g>
|
||||
<!-- angular -->
|
||||
<g id="node37" class="node">
|
||||
<title>angular</title>
|
||||
<polygon fill="#28a745" stroke="black" points="951.68,-1254.79 865.68,-1254.79 865.68,-1213.79 951.68,-1213.79 951.68,-1254.79"/>
|
||||
<text text-anchor="middle" x="908.68" y="-1242.79" font-family="Arial" font-size="10.00" fill="#ffffff">Angular 17 SPA</text>
|
||||
<text text-anchor="middle" x="908.68" y="-1231.79" font-family="Arial" font-size="10.00" fill="#ffffff">+ WebSocket</text>
|
||||
<text text-anchor="middle" x="908.68" y="-1220.79" font-family="Arial" font-size="10.00" fill="#ffffff">:4200</text>
|
||||
</g>
|
||||
<!-- django->angular -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>django->angular</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M705.59,-1524.49C742.94,-1471.29 845.59,-1325.13 888.94,-1263.4"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="892.01,-1265.12 894.89,-1254.92 886.28,-1261.1 892.01,-1265.12"/>
|
||||
<text text-anchor="middle" x="794.18" y="-1430.89" font-family="Arial" font-size="8.00">WebSocket</text>
|
||||
<text text-anchor="middle" x="794.18" y="-1421.89" font-family="Arial" font-size="8.00">API</text>
|
||||
</g>
|
||||
<!-- logo_worker -->
|
||||
<g id="node27" class="node">
|
||||
<title>logo_worker</title>
|
||||
<polygon fill="#28a745" stroke="black" points="480.18,-935.79 397.18,-935.79 397.18,-894.79 480.18,-894.79 480.18,-935.79"/>
|
||||
<text text-anchor="middle" x="438.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#ffffff">Logo Detection</text>
|
||||
<text text-anchor="middle" x="438.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#ffffff">Worker</text>
|
||||
<text text-anchor="middle" x="438.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#ffffff">(logo_queue)</text>
|
||||
</g>
|
||||
<!-- redis->logo_worker -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>redis->logo_worker</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M681.86,-1216.25C639.76,-1164.31 512.77,-1007.67 461.01,-943.83"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="463.52,-941.37 454.5,-935.8 458.08,-945.78 463.52,-941.37"/>
|
||||
</g>
|
||||
<!-- visual_worker -->
|
||||
<g id="node28" class="node">
|
||||
<title>visual_worker</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="600.18,-935.79 509.18,-935.79 509.18,-894.79 600.18,-894.79 600.18,-935.79"/>
|
||||
<text text-anchor="middle" x="554.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Visual Properties</text>
|
||||
<text text-anchor="middle" x="554.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
||||
<text text-anchor="middle" x="554.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(visual_queue)</text>
|
||||
</g>
|
||||
<!-- redis->visual_worker -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>redis->visual_worker</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M688.1,-1216.25C665.14,-1164.63 596.18,-1009.58 567.46,-945.01"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="570.62,-943.52 563.36,-935.8 564.23,-946.36 570.62,-943.52"/>
|
||||
</g>
|
||||
<!-- audio_worker -->
|
||||
<g id="node29" class="node">
|
||||
<title>audio_worker</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="835.18,-935.79 748.18,-935.79 748.18,-894.79 835.18,-894.79 835.18,-935.79"/>
|
||||
<text text-anchor="middle" x="791.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Audio Transcript</text>
|
||||
<text text-anchor="middle" x="791.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
||||
<text text-anchor="middle" x="791.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(audio_queue)</text>
|
||||
</g>
|
||||
<!-- redis->audio_worker -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>redis->audio_worker</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M700.84,-1216.25C716.44,-1164.73 763.23,-1010.22 782.86,-945.41"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="786.22,-946.39 785.77,-935.8 779.52,-944.36 786.22,-946.39"/>
|
||||
</g>
|
||||
<!-- text_worker -->
|
||||
<g id="node30" class="node">
|
||||
<title>text_worker</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="718.68,-935.79 628.68,-935.79 628.68,-894.79 718.68,-894.79 718.68,-935.79"/>
|
||||
<text text-anchor="middle" x="673.68" y="-923.79" font-family="Arial" font-size="10.00" fill="#212529">Text Recognition</text>
|
||||
<text text-anchor="middle" x="673.68" y="-912.79" font-family="Arial" font-size="10.00" fill="#212529">Worker</text>
|
||||
<text text-anchor="middle" x="673.68" y="-901.79" font-family="Arial" font-size="10.00" fill="#212529">(text_queue)</text>
|
||||
</g>
|
||||
<!-- redis->text_worker -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>redis->text_worker</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M694.5,-1216.25C690.93,-1164.84 680.24,-1010.86 675.73,-945.81"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="679.22,-945.54 675.04,-935.8 672.24,-946.02 679.22,-945.54"/>
|
||||
</g>
|
||||
<!-- nginx -->
|
||||
<g id="node15" class="node">
|
||||
<title>nginx</title>
|
||||
<polygon fill="#28a745" stroke="black" points="835.68,-1565.79 753.68,-1565.79 753.68,-1524.79 835.68,-1524.79 835.68,-1565.79"/>
|
||||
<text text-anchor="middle" x="794.68" y="-1553.79" font-family="Arial" font-size="10.00" fill="#ffffff">NGINX</text>
|
||||
<text text-anchor="middle" x="794.68" y="-1542.79" font-family="Arial" font-size="10.00" fill="#ffffff">Reverse Proxy</text>
|
||||
<text text-anchor="middle" x="794.68" y="-1531.79" font-family="Arial" font-size="10.00" fill="#ffffff">:80</text>
|
||||
</g>
|
||||
<!-- nginx->angular -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>nginx->angular</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M801.99,-1524.49C821.49,-1471.62 874.87,-1326.93 897.89,-1264.55"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="901.26,-1265.52 901.44,-1254.92 894.69,-1263.09 901.26,-1265.52"/>
|
||||
</g>
|
||||
<!-- base_strategy -->
|
||||
<g id="node16" class="node">
|
||||
<title>base_strategy</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="1877.18,-933.29 1758.18,-933.29 1758.18,-897.29 1877.18,-897.29 1877.18,-933.29"/>
|
||||
<text text-anchor="middle" x="1817.68" y="-918.29" font-family="Arial" font-size="10.00">BaseExecutionStrategy</text>
|
||||
<text text-anchor="middle" x="1817.68" y="-907.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- local_strategy -->
|
||||
<g id="node17" class="node">
|
||||
<title>local_strategy</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1754.18,-664.29 1677.18,-664.29 1677.18,-628.29 1754.18,-628.29 1754.18,-664.29"/>
|
||||
<text text-anchor="middle" x="1715.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#ffffff">LocalStrategy</text>
|
||||
</g>
|
||||
<!-- base_strategy->local_strategy -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>base_strategy->local_strategy</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1811.15,-897.18C1793.84,-851.89 1746.75,-728.61 1725.87,-673.97"/>
|
||||
<polygon fill="gray" stroke="gray" points="1729.13,-672.69 1722.29,-664.6 1722.59,-675.19 1729.13,-672.69"/>
|
||||
</g>
|
||||
<!-- lan_strategy -->
|
||||
<g id="node18" class="node">
|
||||
<title>lan_strategy</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1854.18,-664.29 1781.18,-664.29 1781.18,-628.29 1854.18,-628.29 1854.18,-664.29"/>
|
||||
<text text-anchor="middle" x="1817.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#212529">LANStrategy</text>
|
||||
</g>
|
||||
<!-- base_strategy->lan_strategy -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>base_strategy->lan_strategy</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1817.68,-897.18C1817.68,-852.08 1817.68,-729.63 1817.68,-674.65"/>
|
||||
<polygon fill="gray" stroke="gray" points="1821.18,-674.6 1817.68,-664.6 1814.18,-674.6 1821.18,-674.6"/>
|
||||
</g>
|
||||
<!-- cloud_strategy -->
|
||||
<g id="node19" class="node">
|
||||
<title>cloud_strategy</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1961.18,-664.29 1882.18,-664.29 1882.18,-628.29 1961.18,-628.29 1961.18,-664.29"/>
|
||||
<text text-anchor="middle" x="1921.68" y="-643.79" font-family="Arial" font-size="10.00" fill="#212529">CloudStrategy</text>
|
||||
</g>
|
||||
<!-- base_strategy->cloud_strategy -->
|
||||
<g id="edge43" class="edge">
|
||||
<title>base_strategy->cloud_strategy</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1824.35,-897.18C1841.99,-851.89 1890.01,-728.61 1911.29,-673.97"/>
|
||||
<polygon fill="gray" stroke="gray" points="1914.57,-675.19 1914.94,-664.6 1908.05,-672.65 1914.57,-675.19"/>
|
||||
</g>
|
||||
<!-- clip_adapter -->
|
||||
<g id="node32" class="node">
|
||||
<title>clip_adapter</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1412.68,-428.29 1340.68,-428.29 1340.68,-392.29 1412.68,-392.29 1412.68,-428.29"/>
|
||||
<text text-anchor="middle" x="1376.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#ffffff">CLIPAdapter</text>
|
||||
<text text-anchor="middle" x="1376.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Local)</text>
|
||||
</g>
|
||||
<!-- local_strategy->clip_adapter -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>local_strategy->clip_adapter</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1691.03,-628.27C1631.12,-586.92 1477.85,-481.12 1409.93,-434.24"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1411.92,-431.36 1401.7,-428.56 1407.94,-437.12 1411.92,-431.36"/>
|
||||
</g>
|
||||
<!-- gcp_vision -->
|
||||
<g id="node33" class="node">
|
||||
<title>gcp_vision</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1540.18,-428.29 1441.18,-428.29 1441.18,-392.29 1540.18,-392.29 1540.18,-428.29"/>
|
||||
<text text-anchor="middle" x="1490.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#212529">GCPVisionAdapter</text>
|
||||
<text text-anchor="middle" x="1490.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#212529">(Cloud)</text>
|
||||
</g>
|
||||
<!-- local_strategy->gcp_vision -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>local_strategy->gcp_vision</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1702.78,-628.22C1682.83,-602.22 1642.81,-551.56 1604.68,-512.29 1577.5,-484.29 1543.76,-455 1520.07,-435.22"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1522.07,-432.33 1512.13,-428.64 1517.6,-437.71 1522.07,-432.33"/>
|
||||
</g>
|
||||
<!-- yolo_adapter -->
|
||||
<g id="node34" class="node">
|
||||
<title>yolo_adapter</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1646.18,-428.29 1569.18,-428.29 1569.18,-392.29 1646.18,-392.29 1646.18,-428.29"/>
|
||||
<text text-anchor="middle" x="1607.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#212529">YOLOAdapter</text>
|
||||
<text text-anchor="middle" x="1607.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#212529">(Planned)</text>
|
||||
</g>
|
||||
<!-- lan_strategy->yolo_adapter -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>lan_strategy->yolo_adapter</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1802.24,-628.08C1765.53,-587.17 1672.91,-483.97 1630.06,-436.23"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1632.43,-433.62 1623.15,-428.52 1627.22,-438.3 1632.43,-433.62"/>
|
||||
</g>
|
||||
<!-- speech_api -->
|
||||
<g id="node40" class="node">
|
||||
<title>speech_api</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1802.18,-117.29 1701.18,-117.29 1701.18,-81.29 1802.18,-81.29 1802.18,-117.29"/>
|
||||
<text text-anchor="middle" x="1751.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#212529">Speech-to-Text API</text>
|
||||
<text text-anchor="middle" x="1751.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#212529">(Audio Transcript)</text>
|
||||
</g>
|
||||
<!-- cloud_strategy->speech_api -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>cloud_strategy->speech_api</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M1916.31,-628.08C1891.75,-549.34 1790.02,-223.21 1760.04,-127.07"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1763.37,-126.01 1757.05,-117.5 1756.69,-128.09 1763.37,-126.01"/>
|
||||
</g>
|
||||
<!-- base_event_source -->
|
||||
<g id="node20" class="node">
|
||||
<title>base_event_source</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="1490.68,-1252.29 1378.68,-1252.29 1378.68,-1216.29 1490.68,-1216.29 1490.68,-1252.29"/>
|
||||
<text text-anchor="middle" x="1434.68" y="-1237.29" font-family="Arial" font-size="10.00">SegmentEventSource</text>
|
||||
<text text-anchor="middle" x="1434.68" y="-1226.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- file_watcher_source -->
|
||||
<g id="node21" class="node">
|
||||
<title>file_watcher_source</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1338.18,-933.29 1213.18,-933.29 1213.18,-897.29 1338.18,-897.29 1338.18,-933.29"/>
|
||||
<text text-anchor="middle" x="1275.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#ffffff">FileWatcherEventSource</text>
|
||||
<text text-anchor="middle" x="1275.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Local/LAN)</text>
|
||||
</g>
|
||||
<!-- base_event_source->file_watcher_source -->
|
||||
<g id="edge47" class="edge">
|
||||
<title>base_event_source->file_watcher_source</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1426.13,-1216.25C1399.93,-1164 1320.58,-1005.81 1288.93,-942.7"/>
|
||||
<polygon fill="gray" stroke="gray" points="1291.88,-940.79 1284.27,-933.42 1285.63,-943.92 1291.88,-940.79"/>
|
||||
</g>
|
||||
<!-- cloud_storage_source -->
|
||||
<g id="node22" class="node">
|
||||
<title>cloud_storage_source</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1500.68,-933.29 1368.68,-933.29 1368.68,-897.29 1500.68,-897.29 1500.68,-933.29"/>
|
||||
<text text-anchor="middle" x="1434.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#212529">CloudStorageEventSource</text>
|
||||
<text text-anchor="middle" x="1434.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#212529">(GCS Pub/Sub)</text>
|
||||
</g>
|
||||
<!-- base_event_source->cloud_storage_source -->
|
||||
<g id="edge48" class="edge">
|
||||
<title>base_event_source->cloud_storage_source</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1434.68,-1216.25C1434.68,-1164.21 1434.68,-1007.11 1434.68,-943.48"/>
|
||||
<polygon fill="gray" stroke="gray" points="1438.18,-943.42 1434.68,-933.42 1431.18,-943.42 1438.18,-943.42"/>
|
||||
</g>
|
||||
<!-- webhook_source -->
|
||||
<g id="node23" class="node">
|
||||
<title>webhook_source</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1645.18,-933.29 1532.18,-933.29 1532.18,-897.29 1645.18,-897.29 1645.18,-933.29"/>
|
||||
<text text-anchor="middle" x="1588.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#212529">WebhookEventSource</text>
|
||||
<text text-anchor="middle" x="1588.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#212529">(External)</text>
|
||||
</g>
|
||||
<!-- base_event_source->webhook_source -->
|
||||
<g id="edge49" class="edge">
|
||||
<title>base_event_source->webhook_source</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1442.96,-1216.25C1468.34,-1164 1545.19,-1005.81 1575.85,-942.7"/>
|
||||
<polygon fill="gray" stroke="gray" points="1579.14,-943.94 1580.36,-933.42 1572.84,-940.88 1579.14,-943.94"/>
|
||||
</g>
|
||||
<!-- event_source_manager -->
|
||||
<g id="node24" class="node">
|
||||
<title>event_source_manager</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1309.68,-664.29 1199.68,-664.29 1199.68,-628.29 1309.68,-628.29 1309.68,-664.29"/>
|
||||
<text text-anchor="middle" x="1254.68" y="-649.29" font-family="Arial" font-size="10.00" fill="#ffffff">EventSourceManager</text>
|
||||
<text text-anchor="middle" x="1254.68" y="-638.29" font-family="Arial" font-size="10.00" fill="#ffffff">(Environment-based)</text>
|
||||
</g>
|
||||
<!-- file_watcher_source->event_source_manager -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>file_watcher_source->event_source_manager</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1274.34,-897.18C1270.79,-852.08 1261.16,-729.63 1256.83,-674.65"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1260.32,-674.29 1256.04,-664.6 1253.34,-674.84 1260.32,-674.29"/>
|
||||
</g>
|
||||
<!-- cloud_storage_source->event_source_manager -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>cloud_storage_source->event_source_manager</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1423.15,-897.18C1392.42,-851.61 1308.47,-727.07 1271.99,-672.96"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1274.84,-670.93 1266.35,-664.6 1269.04,-674.85 1274.84,-670.93"/>
|
||||
</g>
|
||||
<!-- webhook_source->event_source_manager -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>webhook_source->event_source_manager</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1567.28,-897.18C1509.57,-851.04 1350.61,-723.97 1284.32,-670.98"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1286.33,-668.11 1276.34,-664.6 1281.96,-673.58 1286.33,-668.11"/>
|
||||
</g>
|
||||
<!-- redis_events -->
|
||||
<g id="node25" class="node">
|
||||
<title>redis_events</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1299.68,-428.29 1197.68,-428.29 1197.68,-392.29 1299.68,-392.29 1299.68,-428.29"/>
|
||||
<text text-anchor="middle" x="1248.68" y="-413.29" font-family="Arial" font-size="10.00" fill="#ffffff">Redis Event Queue</text>
|
||||
<text text-anchor="middle" x="1248.68" y="-402.29" font-family="Arial" font-size="10.00" fill="#ffffff">(segment_events)</text>
|
||||
</g>
|
||||
<!-- event_source_manager->redis_events -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>event_source_manager->redis_events</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1254.24,-628.08C1253.21,-587.85 1250.63,-487.37 1249.38,-438.64"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1252.88,-438.43 1249.12,-428.52 1245.88,-438.61 1252.88,-438.43"/>
|
||||
<text text-anchor="middle" x="1268.68" y="-523.89" font-family="Arial" font-size="8.00">publishes</text>
|
||||
<text text-anchor="middle" x="1268.68" y="-514.89" font-family="Arial" font-size="8.00">events</text>
|
||||
</g>
|
||||
<!-- event_processor -->
|
||||
<g id="node26" class="node">
|
||||
<title>event_processor</title>
|
||||
<polygon fill="#28a745" stroke="black" points="1282.68,-117.29 1188.68,-117.29 1188.68,-81.29 1282.68,-81.29 1282.68,-117.29"/>
|
||||
<text text-anchor="middle" x="1235.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#ffffff">Event Processor</text>
|
||||
<text text-anchor="middle" x="1235.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#ffffff">(triggers analysis)</text>
|
||||
</g>
|
||||
<!-- redis_events->event_processor -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>redis_events->event_processor</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1247.96,-392.24C1245.82,-341.27 1239.45,-189.79 1236.83,-127.5"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1240.31,-127.13 1236.4,-117.29 1233.32,-127.43 1240.31,-127.13"/>
|
||||
<text text-anchor="middle" x="1262.18" y="-287.89" font-family="Arial" font-size="8.00">consumes</text>
|
||||
<text text-anchor="middle" x="1262.18" y="-278.89" font-family="Arial" font-size="8.00">events</text>
|
||||
</g>
|
||||
<!-- event_processor->logo_worker -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>event_processor->logo_worker</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1219.03,-117.29C1118.31,-220.17 590.46,-759.27 465.31,-887.09"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="462.48,-884.98 457.99,-894.57 467.48,-889.87 462.48,-884.98"/>
|
||||
<text text-anchor="middle" x="844.68" y="-523.89" font-family="Arial" font-size="8.00">triggers</text>
|
||||
<text text-anchor="middle" x="844.68" y="-514.89" font-family="Arial" font-size="8.00">analysis</text>
|
||||
</g>
|
||||
<!-- logo_worker->django -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>logo_worker->django</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M446.55,-935.81C483.06,-1026.45 636.1,-1406.32 679.91,-1515.08"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="676.71,-1516.49 683.69,-1524.46 683.2,-1513.87 676.71,-1516.49"/>
|
||||
<text text-anchor="middle" x="599.68" y="-1236.89" font-family="Arial" font-size="8.00">analysis</text>
|
||||
<text text-anchor="middle" x="599.68" y="-1227.89" font-family="Arial" font-size="8.00">results</text>
|
||||
</g>
|
||||
<!-- logo_worker->local_strategy -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>logo_worker->local_strategy</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M476.74,-894.69C483.88,-891.64 491.4,-888.82 498.68,-886.79 1003.74,-745.56 1195.76,-932.06 1662.68,-693.29 1673.48,-687.77 1683.89,-679.59 1692.59,-671.65"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1695.24,-673.95 1700.05,-664.51 1690.41,-668.89 1695.24,-673.95"/>
|
||||
</g>
|
||||
<!-- visual_worker->lan_strategy -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>visual_worker->lan_strategy</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M594.34,-894.72C601.93,-891.62 609.94,-888.79 617.68,-886.79 975.57,-794.36 1084.97,-889.51 1445.68,-808.79 1593.23,-775.77 1635.63,-770.42 1765.68,-693.29 1775.84,-687.26 1785.84,-679.11 1794.32,-671.34"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1796.8,-673.81 1801.63,-664.39 1791.97,-668.74 1796.8,-673.81"/>
|
||||
</g>
|
||||
<!-- audio_worker->cloud_strategy -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>audio_worker->cloud_strategy</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M828.42,-894.66C835.33,-891.61 842.61,-888.81 849.68,-886.79 1144.76,-802.58 1236.56,-877.23 1535.68,-808.79 1687.16,-774.13 1730.23,-769.44 1865.68,-693.29 1876.54,-687.18 1887.37,-678.92 1896.58,-671.08"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1899.01,-673.6 1904.21,-664.37 1894.39,-668.35 1899.01,-673.6"/>
|
||||
</g>
|
||||
<!-- text_worker->cloud_strategy -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>text_worker->cloud_strategy</title>
|
||||
<path fill="none" stroke="#f57f17" stroke-dasharray="5,2" d="M714,-894.79C721.72,-891.68 729.84,-888.83 737.68,-886.79 1065.47,-801.63 1163.94,-876.92 1495.68,-808.79 1664.43,-774.13 1714.1,-775.14 1865.68,-693.29 1876.64,-687.37 1887.5,-679.15 1896.7,-671.29"/>
|
||||
<polygon fill="#f57f17" stroke="#f57f17" points="1899.14,-673.8 1904.31,-664.55 1894.5,-668.56 1899.14,-673.8"/>
|
||||
</g>
|
||||
<!-- base_ai -->
|
||||
<g id="node31" class="node">
|
||||
<title>base_ai</title>
|
||||
<polygon fill="#ce93d8" stroke="black" points="1536.68,-664.29 1444.68,-664.29 1444.68,-628.29 1536.68,-628.29 1536.68,-664.29"/>
|
||||
<text text-anchor="middle" x="1490.68" y="-649.29" font-family="Arial" font-size="10.00">DetectionAdapter</text>
|
||||
<text text-anchor="middle" x="1490.68" y="-638.29" font-family="Arial" font-size="10.00">(Abstract)</text>
|
||||
</g>
|
||||
<!-- base_ai->clip_adapter -->
|
||||
<g id="edge44" class="edge">
|
||||
<title>base_ai->clip_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1482.3,-628.08C1462.58,-587.6 1413.13,-486.1 1389.56,-437.72"/>
|
||||
<polygon fill="gray" stroke="gray" points="1392.6,-435.98 1385.08,-428.52 1386.31,-439.04 1392.6,-435.98"/>
|
||||
</g>
|
||||
<!-- base_ai->gcp_vision -->
|
||||
<g id="edge45" class="edge">
|
||||
<title>base_ai->gcp_vision</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1490.68,-628.08C1490.68,-587.85 1490.68,-487.37 1490.68,-438.64"/>
|
||||
<polygon fill="gray" stroke="gray" points="1494.18,-438.52 1490.68,-428.52 1487.18,-438.52 1494.18,-438.52"/>
|
||||
</g>
|
||||
<!-- base_ai->yolo_adapter -->
|
||||
<g id="edge46" class="edge">
|
||||
<title>base_ai->yolo_adapter</title>
|
||||
<path fill="none" stroke="gray" stroke-dasharray="1,5" d="M1499.28,-628.08C1519.53,-587.6 1570.28,-486.1 1594.46,-437.72"/>
|
||||
<polygon fill="gray" stroke="gray" points="1597.72,-439.03 1599.07,-428.52 1591.46,-435.9 1597.72,-439.03"/>
|
||||
</g>
|
||||
<!-- vision_api -->
|
||||
<g id="node39" class="node">
|
||||
<title>vision_api</title>
|
||||
<polygon fill="#ffc107" stroke="black" points="1672.18,-117.29 1581.18,-117.29 1581.18,-81.29 1672.18,-81.29 1672.18,-117.29"/>
|
||||
<text text-anchor="middle" x="1626.68" y="-102.29" font-family="Arial" font-size="10.00" fill="#212529">Cloud Vision API</text>
|
||||
<text text-anchor="middle" x="1626.68" y="-91.29" font-family="Arial" font-size="10.00" fill="#212529">(Logo Detection)</text>
|
||||
</g>
|
||||
<!-- gcp_vision->vision_api -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>gcp_vision->vision_api</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1498.19,-392.24C1520.71,-341.06 1587.84,-188.54 1615.04,-126.73"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1618.37,-127.85 1619.2,-117.29 1611.97,-125.03 1618.37,-127.85"/>
|
||||
</g>
|
||||
<!-- local_storage->file_watcher_source -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>local_storage->file_watcher_source</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1045.51,-1216.18C1057.76,-1190.12 1082.96,-1139.4 1110.68,-1100.29 1154.5,-1038.45 1216.41,-974.35 1250.58,-940.54"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1253.16,-942.92 1257.83,-933.41 1248.25,-937.92 1253.16,-942.92"/>
|
||||
<text text-anchor="middle" x="1137.18" y="-1111.89" font-family="Arial" font-size="8.00">monitors</text>
|
||||
<text text-anchor="middle" x="1137.18" y="-1102.89" font-family="Arial" font-size="8.00">HLS segments</text>
|
||||
</g>
|
||||
<!-- gcs_storage->cloud_storage_source -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>gcs_storage->cloud_storage_source</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M1158.38,-1216.25C1207,-1163.46 1355.23,-1002.54 1412.12,-940.78"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="1414.7,-943.14 1418.9,-933.42 1409.56,-938.4 1414.7,-943.14"/>
|
||||
<text text-anchor="middle" x="1275.68" y="-1111.89" font-family="Arial" font-size="8.00">storage</text>
|
||||
<text text-anchor="middle" x="1275.68" y="-1102.89" font-family="Arial" font-size="8.00">events</text>
|
||||
</g>
|
||||
<!-- hls_player -->
|
||||
<g id="node38" class="node">
|
||||
<title>hls_player</title>
|
||||
<polygon fill="#28a745" stroke="black" points="968.18,-933.29 867.18,-933.29 867.18,-897.29 968.18,-897.29 968.18,-933.29"/>
|
||||
<text text-anchor="middle" x="917.68" y="-918.29" font-family="Arial" font-size="10.00" fill="#ffffff">HLS.js Player</text>
|
||||
<text text-anchor="middle" x="917.68" y="-907.29" font-family="Arial" font-size="10.00" fill="#ffffff">+ Canvas Overlays</text>
|
||||
</g>
|
||||
<!-- angular->hls_player -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>angular->hls_player</title>
|
||||
<path fill="none" stroke="#2e7d32" d="M909.24,-1213.46C910.79,-1158.86 915.13,-1006.23 916.91,-943.62"/>
|
||||
<polygon fill="#2e7d32" stroke="#2e7d32" points="920.41,-943.45 917.2,-933.35 913.41,-943.25 920.41,-943.45"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 40 KiB |
@@ -1,153 +0,0 @@
|
||||
|
||||
|
||||
|
||||
+-------+
|
||||
| OBS | RTMP stream
|
||||
+---+---+ --------------+
|
||||
| |
|
||||
v |
|
||||
+-----+------+ |
|
||||
| nginx-rtmp |-- HLS ───+ |
|
||||
| (RTMP/HLS) | | |
|
||||
+-----+------+ | |
|
||||
| | |
|
||||
HLS on /media | |
|
||||
| | |
|
||||
v | |
|
||||
+----------+-----------+ | |
|
||||
| Host “media/” dir |<----+ |
|
||||
+----------+-----------+ |
|
||||
| File watcher |
|
||||
v |
|
||||
+----------+-----------+ |
|
||||
| Django Backend | |
|
||||
| (REST + ASGI + WS) +<--WebSocket--+
|
||||
+---+------------------+ |
|
||||
| | |
|
||||
| +-------+ HTTP/API |
|
||||
| v |
|
||||
| +-------+------+ |
|
||||
| | PostgreSQL | |
|
||||
| +-------+------+ |
|
||||
| |
|
||||
| Celery tasks |
|
||||
v |
|
||||
+------+--------+ queue +------+--------+
|
||||
| celery-logo |<-----------| celery-default |
|
||||
+---------------+ +---------------+
|
||||
| ^
|
||||
| |
|
||||
+----> Redis (broker) <------+
|
||||
|
||||
Frontend:
|
||||
+-----------+
|
||||
| Angular |
|
||||
| (4200) |
|
||||
+-----+-----+
|
||||
|
|
||||
| HTTP/API (/api/…)
|
||||
|
|
||||
+-----v-----+
|
||||
| Nginx |
|
||||
| (SSP) |
|
||||
+-----------+
|
||||
|
||||
Legend:
|
||||
|
||||
• OBS pushes RTMP into nginx-rtmp, which writes HLS (.m3u8/.ts) into the shared media/ folder.
|
||||
• Django’s file-watcher polls media/ for new segments and enqueues Celery tasks.
|
||||
• Django Backend serves REST API, WebSockets (via Channels → Redis), and hosts analysis logic.
|
||||
• Celery Workers consume tasks from Redis:
|
||||
– celery-logo for logo detection
|
||||
– celery-default (or others) for visual analysis, etc.
|
||||
• PostgreSQL holds application & analysis metadata.
|
||||
• Angular (dev or static via Nginx) calls the Django API and listens on WebSockets.
|
||||
• Nginx (frontend) proxies /api and /ws to Django and serves the Angular SPA.
|
||||
|
||||
This layout keeps streaming, AI-processing, data storage, and UI layers cleanly separated.
|
||||
|
||||
|
||||
|
||||
|
||||
codex
|
||||
Below is an updated architecture sketch—with the local webcam path and AI execution‐strategy layer added. Each box is a service or component, and arrows show the main flows:
|
||||
|
||||
+--------+
|
||||
| OBS | RTMP stream
|
||||
+---+----+ --------+
|
||||
| |
|
||||
v |
|
||||
+-------+------+ |
|
||||
| nginx-rtmp |-- HLS →| (writes .m3u8/.ts to host media/)
|
||||
| (RTMP/HLS) | |
|
||||
+-------+------+ |
|
||||
| |
|
||||
+------------+---------------+-------------+
|
||||
| HLS |
|
||||
v |
|
||||
+------+-------+ |
|
||||
| Host “media/”|<-----------------------------------+
|
||||
| directory | (mount in backend & nginx-rtmp)
|
||||
+------+-------+
|
||||
|
|
||||
| (poll .ts files)
|
||||
v
|
||||
+------+-------+
|
||||
| File Watcher | → enqueues Celery tasks
|
||||
+------+-------+
|
||||
|
|
||||
v
|
||||
+------+-------+
|
||||
| Django | REST API & WebSocket (ASGI)
|
||||
| Backend |
|
||||
| - RTMP/Webcam|
|
||||
| Adapters |
|
||||
+------+-------+
|
||||
| +--------------------------------+
|
||||
| WebSocket | |
|
||||
+---------->+ Frontend Nginx (SPA + proxy) |
|
||||
| | – Serves Angular app on 80 |
|
||||
| | – Proxies /api → Django |
|
||||
v | – Proxies /ws → Django |
|
||||
+------+-------+ | – Proxies /streaming → nginx-rtmp
|
||||
| PostgreSQL | +--------------------------------+
|
||||
+------+-------+
|
||||
|
|
||||
v
|
||||
+------+-------+ +------------------------+
|
||||
| Redis |<-----------+ Celery Workers |
|
||||
| (broker) | tasks | – Worker queues: |
|
||||
+------+-------+ | logo_detection, |
|
||||
| | visual_analysis, |
|
||||
| | default… |
|
||||
+------------------→ | – Uses AnalysisEngine |
|
||||
| with Execution |
|
||||
| Strategies: |
|
||||
| • local (in-worker) |
|
||||
| • remote LAN (via |
|
||||
| worker_host) |
|
||||
| • cloud (via API) |
|
||||
+------------------------+
|
||||
+--------+
|
||||
| Webcam | local device
|
||||
+---+----+
|
||||
|
|
||||
| via Django’s Webcam
|
||||
|SourceAdapter (ffmpeg)
|
||||
v
|
||||
[ Django Backend ]
|
||||
|
|
||||
v
|
||||
+------+-------+
|
||||
| Host “media/”|
|
||||
+--------------+
|
||||
|
||||
Key points:
|
||||
|
||||
• OBS → nginx-rtmp → host “media/” → Django file-watcher → Celery tasks
|
||||
• Local Webcam → Django backend (WebcamSourceAdapter with ffmpeg) → host “media/” → same pipeline
|
||||
• Django Backend (REST + ASGI/WebSocket) ↔ Angular (served by Frontend Nginx)
|
||||
• Celery Workers pick up tasks from Redis, run AnalysisEngine → can execute locally, offload to remote LAN workers, or call cloud APIs
|
||||
• PostgreSQL stores streams, queue, and analysis results
|
||||
|
||||
This should give a clear bird’s-eye of how data and control flow through your streaming+AI stack.
|
||||
@@ -1,172 +0,0 @@
|
||||
|
||||
> generate a diagram of the architecture to include in readme, just the diagram output in /def. ask question if needed, suggest options which technology to use to generate them I need a graphviz like
|
||||
diagram of how services connect and some of design decisions on the code, like adapter for sources, and strategy for execution (local, lan, cloud), how the │
|
||||
│ queue is organized in different analysis task, media storage, that could be media_root server by nginx or some cloud storage
|
||||
|
||||
● I'll create an architecture diagram showing the service connections and design patterns. Let me ask a few questions to make it accurate:
|
||||
|
||||
Questions:
|
||||
1. Current media storage: Are you currently using local nginx-served files or cloud storage?
|
||||
2. Queue organization: Do you have separate queues like logo_queue, visual_queue, etc., or task routing by queue names?
|
||||
3. GCP services: Which specific GCP services are you integrating (Cloud Vision, Cloud Storage, others)?
|
||||
|
||||
Diagram tool options:
|
||||
1. Graphviz/DOT - Clean, professional, version-controllable
|
||||
2. PlantUML - Good for architectural diagrams, supports multiple output formats
|
||||
3. Mermaid - GitHub-friendly, can render directly in markdown
|
||||
4. Python + matplotlib/networkx - Programmatic, customizable
|
||||
|
||||
My recommendation: Graphviz/DOT because it:
|
||||
- Creates clean, professional diagrams
|
||||
- Is text-based (version control friendly)
|
||||
- Excellent for showing service connections and data flow
|
||||
- Industry standard for technical architecture
|
||||
|
||||
Should I proceed with Graphviz showing:
|
||||
- Service containers (Django, Celery workers, Redis, PostgreSQL, nginx)
|
||||
- Design patterns (Source Adapters, Execution Strategies)
|
||||
- Queue segregation by analysis type
|
||||
- Media storage options (local vs cloud)
|
||||
- WebSocket connections for real-time updates
|
||||
|
||||
What's your preference for the tool and any specific details to include/exclude?
|
||||
|
||||
> even if not ready the idea is to include planed features, maybe use a different style or color to signal this, also maybe use something like svg so easily updetable for when they are done, 2. same thing
|
||||
now is only logo detection, need default as base,3, GCS and Vision for now, extra.. about tool options can you give links for demos that are reprensetative of each to check the general feel. But I think
|
||||
graphviz is the de facto, for super flexible and programable, not at all the usual WYSIWYG generic look which I'd like to avoid
|
||||
|
||||
● Perfect! Here are representative demos to see the visual style:
|
||||
|
||||
Tool Options with Demos:
|
||||
|
||||
1. Graphviz/DOT - https://graphviz.org/gallery/
|
||||
- Clean, programmatic, no WYSIWYG look
|
||||
- Example: https://graphviz.org/Gallery/directed/cluster.html
|
||||
2. PlantUML - https://real-world-plantuml.com/
|
||||
- Architecture: https://plantuml.com/c4-plantuml
|
||||
3. Mermaid - https://mermaid.live/
|
||||
- Architecture: https://mermaid.live/edit#pako:eNptkk1PwzAMhv-K5QtIq6vCx9ZlqiAmEEKIwwSInBqaOG2mxqlWM9Gf6nO6hB3dN0_8vI8f2ccEJqFABKNQzFklUMsK3CrRkrOQF2h8Lk2nPXRt67DpeQHe90QKFNYzb8e4qFXt-Rc
|
||||
4. Python + networkx - https://networkx.org/documentation/stable/auto_examples/index.html
|
||||
|
||||
Graphviz is definitely the best choice for your needs - professional, flexible, no generic look, SVG output, version-controllable.
|
||||
|
||||
For the diagram, I'll include:
|
||||
|
||||
✅ Current (solid lines/colors):
|
||||
- Django API, Celery logo workers, Redis, PostgreSQL
|
||||
- Webcam/RTMP source adapters
|
||||
- Local execution strategy
|
||||
- GCS + Vision API integration
|
||||
|
||||
🔄 Planned (dashed lines/lighter colors):
|
||||
- Additional analysis workers (visual, audio, text)
|
||||
- LAN/Cloud execution strategies
|
||||
- Advanced queue routing
|
||||
- GPU effects pipeline
|
||||
|
||||
Media storage: I'll show both nginx-served local and GCS options with a switch.
|
||||
|
||||
Should I create the Graphviz DOT file now? It'll generate a clean SVG that you can easily update as features are implemented.
|
||||
|
||||
> yes if you need to generate many files, use a folder inside /def
|
||||
|
||||
● I'll create the architecture diagram using Graphviz. Let me create a folder and generate the files:
|
||||
|
||||
● Bash(mkdir -p /home/mariano/wdir/snk/def/architecture)
|
||||
⎿ (No content)
|
||||
|
||||
● Write(~/wdir/snk/def/architecture/system_architecture.dot)
|
||||
⎿ Wrote 175 lines to ../def/architecture/system_architecture.dot
|
||||
digraph MediaAnalyzer {
|
||||
// Graph settings
|
||||
rankdir=TB;
|
||||
bgcolor="white";
|
||||
fontname="Arial";
|
||||
fontsize=12;
|
||||
// Node defaults
|
||||
node [fontname="Arial", fontsize=10, shape=box, style=filled];
|
||||
edge [fontname="Arial", fontsize=8];
|
||||
… +165 lines (ctrl+r to expand)
|
||||
|
||||
● Bash(cd /home/mariano/wdir/snk/def/architecture && dot -Tsvg system_architecture.dot -o architecture_diagram.svg)
|
||||
⎿ (No content)
|
||||
|
||||
● Write(README.md)
|
||||
⎿ User rejected write to README.md
|
||||
1 + # Architecture Diagram Generation
|
||||
2 +
|
||||
3 + ## Files
|
||||
4 + - `system_architecture.dot` - Graphviz source file
|
||||
5 + - `architecture_diagram.svg` - Generated SVG diagram
|
||||
6 +
|
||||
7 + ## To Update Diagram
|
||||
8 +
|
||||
9 + ```bash
|
||||
10 + cd def/architecture
|
||||
11 + dot -Tsvg system_architecture.dot -o architecture_diagram.svg
|
||||
12 + ```
|
||||
13 +
|
||||
14 + ## Design Patterns Shown
|
||||
15 +
|
||||
16 + 1. **Source Adapters** - Abstract different video input sources
|
||||
17 + 2. **Execution Strategies** - Local vs distributed processing
|
||||
18 + 3. **Queue Segregation** - Separate workers for different analysis types
|
||||
19 + 4. **Analysis Adapters** - Pluggable AI models
|
||||
20 +
|
||||
21 + ## Color Coding
|
||||
22 +
|
||||
23 + - **Green (✓)** - Currently implemented
|
||||
24 + - **Yellow (○)** - Planned features
|
||||
25 + - **Dotted lines** - Inheritance/abstraction
|
||||
26 + - **Dashed lines** - Planned connections
|
||||
27 +
|
||||
28 + ## Update Process
|
||||
29 +
|
||||
30 + When implementing new features:
|
||||
31 + 1. Change color from `#fff3cd` (yellow/planned) to `#d4edda` (green/implemented)
|
||||
32 + 2. Change edge style from `dashed` to `solid`
|
||||
33 + 3. Regenerate SVG
|
||||
34 \ No newline at end of file
|
||||
|
||||
> dont step on the actual README !! in any case name it something like diagram md
|
||||
|
||||
● Write(diagram.md)
|
||||
⎿ Wrote 33 lines to diagram.md
|
||||
# Architecture Diagram Generation
|
||||
## Files
|
||||
- `system_architecture.dot` - Graphviz source file
|
||||
- `architecture_diagram.svg` - Generated SVG diagram
|
||||
## To Update Diagram
|
||||
```bash
|
||||
cd def/architecture
|
||||
… +23 lines (ctrl+r to expand)
|
||||
|
||||
● Perfect! The architecture diagram is now generated at:
|
||||
|
||||
- Source: /def/architecture/system_architecture.dot
|
||||
- Generated SVG: /def/architecture/architecture_diagram.svg
|
||||
- Instructions: /def/architecture/diagram.md
|
||||
|
||||
The diagram shows:
|
||||
✅ Current implementation (green, solid lines) - Logo detection with CLIP/GCP Vision🔄 Planned features (yellow, dashed lines) - Additional analysis types, LAN/Cloud strategies📐 Design patterns - Source
|
||||
adapters, execution strategies, queue segregation
|
||||
|
||||
Now you can update the README to reference def/architecture/architecture_diagram.svg for the architecture diagram!
|
||||
|
||||
> can we make it less wide without making square smallers, so it looks better in the main github
|
||||
page, I think that the workers could be aligned verticaly to make a clearer separation with the
|
||||
"core", media and frontend, also a bit more contrast in the colors, not much, especialy grouping
|
||||
background squares
|
||||
|
||||
● Update(system_architecture.dot)
|
||||
⎿ Updated system_architecture.dot with 2 additions
|
||||
4 bgcolor="white";
|
||||
5 fontname="Arial";
|
||||
6 fontsize=12;
|
||||
7 + ratio=fill;
|
||||
8 + size="12,16!";
|
||||
9
|
||||
10 // Node defaults
|
||||
11 node [fontname="Arial", fontsize=10, shape=box, style=filled];
|
||||
⎿ 5-hour limit reached ∙ resets 1pm
|
||||
/upgrade to increase your usage limit.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Architecture Diagram Generation
|
||||
|
||||
## Files
|
||||
- `system_architecture.dot` - Graphviz source file
|
||||
- `architecture_diagram.svg` - Generated SVG diagram
|
||||
|
||||
## To Update Diagram
|
||||
|
||||
```bash
|
||||
cd def/architecture
|
||||
dot -Tsvg system_architecture.dot -o architecture_diagram.svg
|
||||
```
|
||||
|
||||
## Design Patterns Shown
|
||||
|
||||
1. **Source Adapters** - Abstract different video input sources
|
||||
2. **Execution Strategies** - Local vs distributed processing
|
||||
3. **Queue Segregation** - Separate workers for different analysis types
|
||||
4. **Analysis Adapters** - Pluggable AI models
|
||||
|
||||
## Color Coding
|
||||
|
||||
- **Green (✓)** - Currently implemented
|
||||
- **Yellow (○)** - Planned features
|
||||
- **Dotted lines** - Inheritance/abstraction
|
||||
- **Dashed lines** - Planned connections
|
||||
|
||||
## Update Process
|
||||
|
||||
When implementing new features:
|
||||
1. Change color from `#fff3cd` (yellow/planned) to `#d4edda` (green/implemented)
|
||||
2. Change edge style from `dashed` to `solid`
|
||||
3. Regenerate SVG
|
||||
@@ -1,224 +0,0 @@
|
||||
digraph MediaAnalyzer {
|
||||
// Graph settings
|
||||
rankdir=TB;
|
||||
bgcolor="white";
|
||||
fontname="Arial";
|
||||
fontsize=12;
|
||||
ratio=fill;
|
||||
size="27.5,30!";
|
||||
ranksep=0.3;
|
||||
nodesep=0.3;
|
||||
|
||||
// Node defaults
|
||||
node [fontname="Arial", fontsize=10, shape=box, style=filled];
|
||||
edge [fontname="Arial", fontsize=8];
|
||||
|
||||
// Color scheme
|
||||
subgraph cluster_legend {
|
||||
label="Legend";
|
||||
style=filled;
|
||||
color=lightgray;
|
||||
node [shape=plaintext, style=filled];
|
||||
|
||||
implemented [label="✅ Implemented", fillcolor="#28a745", color="#ffffff", fontcolor="#ffffff"];
|
||||
planned [label="⏳ Planned", fillcolor="#ffc107", color="#212529", fontcolor="#212529"];
|
||||
code_pattern [label="📐 Code Pattern", fillcolor="#8e24aa", color="#ffffff", fontcolor="#ffffff"];
|
||||
service_arch [label="⚡ Service/Architecture", fillcolor="#1976d2", color="#ffffff", fontcolor="#ffffff"];
|
||||
|
||||
implemented -> planned [style=invis];
|
||||
code_pattern -> service_arch [style=invis];
|
||||
}
|
||||
|
||||
// Input Sources
|
||||
subgraph cluster_sources {
|
||||
label="Video Sources";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
webcam [label="Webcam", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
rtmp [label="RTMP/OBS", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
files [label="File Upload", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Source Adapters (Design Pattern)
|
||||
subgraph cluster_adapters {
|
||||
label="Source Adapters Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_adapter [label="BaseSourceAdapter\n(Abstract)", fillcolor="#ce93d8"];
|
||||
webcam_adapter [label="WebcamAdapter", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
rtmp_adapter [label="RtmpAdapter", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
file_adapter [label="FileAdapter", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Core Services
|
||||
subgraph cluster_core {
|
||||
label="Core Platform";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
django [label="Django API\n+ Channels\n:8000", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
postgres [label="PostgreSQL\nDatabase", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
redis [label="Redis\nCache & Broker", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
nginx [label="NGINX\nReverse Proxy\n:80", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
}
|
||||
|
||||
// Execution Strategies (Design Pattern)
|
||||
subgraph cluster_execution {
|
||||
label="Execution Strategies Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_strategy [label="BaseExecutionStrategy\n(Abstract)", fillcolor="#ce93d8"];
|
||||
local_strategy [label="LocalStrategy", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
lan_strategy [label="LANStrategy", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
cloud_strategy [label="CloudStrategy", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Event Source Pattern (NEW)
|
||||
subgraph cluster_event_sources {
|
||||
label="Event Source Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_event_source [label="SegmentEventSource\n(Abstract)", fillcolor="#ce93d8"];
|
||||
file_watcher_source [label="FileWatcherEventSource\n(Local/LAN)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
cloud_storage_source [label="CloudStorageEventSource\n(GCS Pub/Sub)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
webhook_source [label="WebhookEventSource\n(External)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Event-Driven Processing Pipeline
|
||||
subgraph cluster_events {
|
||||
label="Event-Driven Processing";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
event_source_manager [label="EventSourceManager\n(Environment-based)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
redis_events [label="Redis Event Queue\n(segment_events)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
event_processor [label="Event Processor\n(triggers analysis)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
}
|
||||
|
||||
// Analysis Workers
|
||||
subgraph cluster_workers {
|
||||
label="Celery Workers";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
logo_worker [label="Logo Detection\nWorker\n(logo_queue)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
visual_worker [label="Visual Properties\nWorker\n(visual_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
audio_worker [label="Audio Transcript\nWorker\n(audio_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
text_worker [label="Text Recognition\nWorker\n(text_queue)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// AI Adapters (Design Pattern)
|
||||
subgraph cluster_ai_adapters {
|
||||
label="Analysis Adapters Pattern";
|
||||
style=filled;
|
||||
color="#8e24aa";
|
||||
|
||||
base_ai [label="DetectionAdapter\n(Abstract)", fillcolor="#ce93d8"];
|
||||
clip_adapter [label="CLIPAdapter\n(Local)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
gcp_vision [label="GCPVisionAdapter\n(Cloud)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
yolo_adapter [label="YOLOAdapter\n(Planned)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Storage Options
|
||||
subgraph cluster_storage {
|
||||
label="Media Storage";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
local_storage [label="Local Files\n(nginx-served)", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
gcs_storage [label="Google Cloud\nStorage", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Frontend
|
||||
subgraph cluster_frontend {
|
||||
label="Frontend";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
angular [label="Angular 17 SPA\n+ WebSocket\n:4200", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
hls_player [label="HLS.js Player\n+ Canvas Overlays", fillcolor="#28a745", fontcolor="#ffffff"];
|
||||
}
|
||||
|
||||
// Cloud Services
|
||||
subgraph cluster_cloud {
|
||||
label="GCP Services";
|
||||
style=filled;
|
||||
color="#1976d2";
|
||||
|
||||
vision_api [label="Cloud Vision API\n(Logo Detection)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
speech_api [label="Speech-to-Text API\n(Audio Transcript)", fillcolor="#ffc107", fontcolor="#212529"];
|
||||
}
|
||||
|
||||
// Connections - Current Implementation (solid)
|
||||
webcam -> webcam_adapter [color="#2e7d32"];
|
||||
rtmp -> rtmp_adapter [color="#2e7d32"];
|
||||
|
||||
webcam_adapter -> django [color="#2e7d32"];
|
||||
rtmp_adapter -> django [color="#2e7d32"];
|
||||
|
||||
django -> postgres [color="#2e7d32"];
|
||||
django -> redis [color="#2e7d32"];
|
||||
django -> local_storage [color="#2e7d32"];
|
||||
django -> gcs_storage [color="#2e7d32"];
|
||||
|
||||
// Event source pattern connections
|
||||
local_storage -> file_watcher_source [label="monitors\nHLS segments", color="#2e7d32"];
|
||||
gcs_storage -> cloud_storage_source [label="storage\nevents", color="#2e7d32"];
|
||||
file_watcher_source -> event_source_manager [color="#2e7d32"];
|
||||
cloud_storage_source -> event_source_manager [color="#2e7d32"];
|
||||
webhook_source -> event_source_manager [color="#2e7d32"];
|
||||
|
||||
// Event-driven processing flow
|
||||
event_source_manager -> redis_events [label="publishes\nevents", color="#2e7d32"];
|
||||
redis_events -> event_processor [label="consumes\nevents", color="#2e7d32"];
|
||||
event_processor -> logo_worker [label="triggers\nanalysis", color="#2e7d32"];
|
||||
|
||||
redis -> logo_worker [color="#2e7d32"];
|
||||
logo_worker -> local_strategy [color="#2e7d32"];
|
||||
local_strategy -> clip_adapter [color="#2e7d32"];
|
||||
local_strategy -> gcp_vision [color="#2e7d32"];
|
||||
gcp_vision -> vision_api [color="#2e7d32"];
|
||||
|
||||
// WebSocket real-time updates
|
||||
logo_worker -> django [label="analysis\nresults", color="#2e7d32"];
|
||||
django -> angular [label="WebSocket\nAPI", color="#2e7d32"];
|
||||
angular -> hls_player [color="#2e7d32"];
|
||||
nginx -> angular [color="#2e7d32"];
|
||||
|
||||
// Planned Connections (dashed)
|
||||
files -> file_adapter [color="#f57f17", style=dashed];
|
||||
file_adapter -> django [color="#f57f17", style=dashed];
|
||||
|
||||
redis -> visual_worker [color="#f57f17", style=dashed];
|
||||
redis -> audio_worker [color="#f57f17", style=dashed];
|
||||
redis -> text_worker [color="#f57f17", style=dashed];
|
||||
|
||||
visual_worker -> lan_strategy [color="#f57f17", style=dashed];
|
||||
audio_worker -> cloud_strategy [color="#f57f17", style=dashed];
|
||||
text_worker -> cloud_strategy [color="#f57f17", style=dashed];
|
||||
|
||||
cloud_strategy -> speech_api [color="#f57f17", style=dashed];
|
||||
lan_strategy -> yolo_adapter [color="#f57f17", style=dashed];
|
||||
|
||||
// Inheritance relationships (dotted)
|
||||
base_adapter -> webcam_adapter [style=dotted, color=gray];
|
||||
base_adapter -> rtmp_adapter [style=dotted, color=gray];
|
||||
base_adapter -> file_adapter [style=dotted, color=gray];
|
||||
|
||||
base_strategy -> local_strategy [style=dotted, color=gray];
|
||||
base_strategy -> lan_strategy [style=dotted, color=gray];
|
||||
base_strategy -> cloud_strategy [style=dotted, color=gray];
|
||||
|
||||
base_ai -> clip_adapter [style=dotted, color=gray];
|
||||
base_ai -> gcp_vision [style=dotted, color=gray];
|
||||
base_ai -> yolo_adapter [style=dotted, color=gray];
|
||||
|
||||
// Event source inheritance (NEW)
|
||||
base_event_source -> file_watcher_source [style=dotted, color=gray];
|
||||
base_event_source -> cloud_storage_source [style=dotted, color=gray];
|
||||
base_event_source -> webhook_source [style=dotted, color=gray];
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
digraph SystemOverview {
|
||||
// Graph settings
|
||||
rankdir=TB;
|
||||
compound=true;
|
||||
fontname="Helvetica";
|
||||
node [fontname="Helvetica", fontsize=11];
|
||||
edge [fontname="Helvetica", fontsize=10];
|
||||
|
||||
// Title
|
||||
labelloc="t";
|
||||
label="System Monitoring Platform - Architecture Overview";
|
||||
fontsize=16;
|
||||
|
||||
// Styling
|
||||
node [shape=box, style="rounded,filled"];
|
||||
|
||||
// External
|
||||
subgraph cluster_external {
|
||||
label="External";
|
||||
style=dashed;
|
||||
color=gray;
|
||||
|
||||
browser [label="Browser\n(Dashboard)", fillcolor="#E3F2FD"];
|
||||
machines [label="Monitored\nMachines", fillcolor="#FFF3E0", shape=box3d];
|
||||
}
|
||||
|
||||
// Core Services
|
||||
subgraph cluster_services {
|
||||
label="Application Services";
|
||||
style=filled;
|
||||
color="#E8F5E9";
|
||||
fillcolor="#E8F5E9";
|
||||
|
||||
gateway [label="Gateway\n(FastAPI)", fillcolor="#C8E6C9"];
|
||||
aggregator [label="Aggregator\n(gRPC Server)", fillcolor="#C8E6C9"];
|
||||
alerts [label="Alerts\nService", fillcolor="#C8E6C9"];
|
||||
collector [label="Collector\n(gRPC Client)", fillcolor="#DCEDC8"];
|
||||
}
|
||||
|
||||
// Data Layer
|
||||
subgraph cluster_data {
|
||||
label="Data Layer";
|
||||
style=filled;
|
||||
color="#FFF8E1";
|
||||
fillcolor="#FFF8E1";
|
||||
|
||||
redis [label="Redis\n(Pub/Sub + State)", fillcolor="#FFECB3", shape=cylinder];
|
||||
timescale [label="TimescaleDB\n(Time-series)", fillcolor="#FFECB3", shape=cylinder];
|
||||
}
|
||||
|
||||
// Event Stream
|
||||
subgraph cluster_events {
|
||||
label="Event Stream";
|
||||
style=filled;
|
||||
color="#F3E5F5";
|
||||
fillcolor="#F3E5F5";
|
||||
|
||||
events [label="Redis Pub/Sub\n(Events)", fillcolor="#E1BEE7", shape=hexagon];
|
||||
}
|
||||
|
||||
// Connections
|
||||
browser -> gateway [label="WebSocket\nREST", color="#1976D2"];
|
||||
gateway -> aggregator [label="gRPC", color="#388E3C"];
|
||||
gateway -> redis [label="State\nQuery", style=dashed];
|
||||
gateway -> timescale [label="Historical\nQuery", style=dashed];
|
||||
|
||||
machines -> collector [label="psutil", color="#F57C00", style=dotted];
|
||||
collector -> aggregator [label="gRPC\nStream", color="#388E3C"];
|
||||
|
||||
aggregator -> redis [label="Current\nState", color="#FFA000"];
|
||||
aggregator -> timescale [label="Store\nMetrics", color="#FFA000"];
|
||||
aggregator -> events [label="Publish", color="#7B1FA2"];
|
||||
|
||||
events -> alerts [label="Subscribe", color="#7B1FA2"];
|
||||
events -> gateway [label="Subscribe", color="#7B1FA2"];
|
||||
|
||||
alerts -> timescale [label="Store\nAlerts", style=dashed];
|
||||
}
|
||||