From 8faf0b65b9d91b34d5d7484a743a995186a2d660 Mon Sep 17 00:00:00 2001 From: wsf Date: Mon, 24 Aug 2026 22:08:21 -0300 Subject: [PATCH] feat(prototipo): agregar pantallas trazables ICONIX Co-authored-by: wsf Signed-off-by: wsf --- README.md | 10 ++++- prototipo/README.md | 36 ++++++++++++++++ prototipo/app.js | 82 ++++++++++++++++++++++++++++++++++++ prototipo/index.html | 99 ++++++++++++++++++++++++++++++++++++++++++++ prototipo/styles.css | 3 ++ 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 prototipo/README.md create mode 100644 prototipo/app.js create mode 100644 prototipo/index.html create mode 100644 prototipo/styles.css diff --git a/README.md b/README.md index 20ebbc4..ea3f121 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ sap_tfi_2026/ │ └── pdf/ ← versiones PDF ├── evidence/ │ └── index.md ← catálogo de evidencias +├── prototipo/ ← pantallas navegables y trazabilidad ICONIX └── .gitignore ``` @@ -54,6 +55,13 @@ sap_tfi_2026/ ## Cómo usar este repositorio para el TP +### Explorar el prototipo de fronteras + +Abrí **`prototipo/index.html`** para recorrer las pantallas de presupuesto, +confirmación, reserva y facturación. La vista **Trazabilidad** muestra la +relación entre cada componente de interfaz (boundary), la acción que dispara +(control) y los modelos afectados (entity). + ### Paso 1: Entender la metodología ICONIX Antes de hacer cualquier diagrama, leé: @@ -332,4 +340,4 @@ Material de lectura recomendado para profundizar en la metodología ICONIX y su ## Licencia -Este material se distribuye bajo la licencia Creative Commons BY-SA 4.0. Podés usarlo, modificarlo y redistribuirlo libremente, citando la fuente. \ No newline at end of file +Este material se distribuye bajo la licencia Creative Commons BY-SA 4.0. Podés usarlo, modificarlo y redistribuirlo libremente, citando la fuente. diff --git a/prototipo/README.md b/prototipo/README.md new file mode 100644 index 0000000..b3f0a5e --- /dev/null +++ b/prototipo/README.md @@ -0,0 +1,36 @@ +# Prototipo de fronteras ICONIX + +Prototipo navegable que representa las pantallas asociadas a los diagramas de +robustez del flujo comercial. Su objetivo es mostrar, con fines didácticos, la +trazabilidad entre: + +- **Boundary**: pantalla, formulario, botón, enlace, tabla o mensaje visible. +- **Control**: acción disparada por una interacción. +- **Entity**: modelo y datos consultados o modificados. + +## Abrir + +No requiere instalación ni servidor. Abrir `index.html` en un navegador. + +Para servirlo localmente (opcional): + +```bash +python3 -m http.server 8000 --directory prototipo +``` + +Luego visitar . + +## Escenarios incluidos + +| Pantalla | Caso de uso | Diagrama de robustez | +|---|---|---| +| Presupuesto | CU-VEN-001 | D-ROB-VEN-001 | +| Confirmación | CU-VEN-004 | D-ROB-VEN-004 | +| Reserva | CU-ENT-002 | D-ROB-ENT-002 | +| Facturación | CU-FAC-001 | D-ROB-FAC-001 | + +## Alcance + +Es un prototipo educativo, no una reproducción exacta ni conectada a Odoo. Los +datos son ficticios y las acciones se ejecutan en memoria. Las correspondencias +se basan en las fuentes PlantUML existentes en `diagrams/plantuml/`. diff --git a/prototipo/app.js b/prototipo/app.js new file mode 100644 index 0000000..0cb7711 --- /dev/null +++ b/prototipo/app.js @@ -0,0 +1,82 @@ +const scenarios = { + presupuesto: { + title: "Crear presupuesto", diagram: "D-ROB-VEN-001", state: "BORRADOR", + actions: [["Guardar", "primary", "save"], ["Enviar por email", "", "email"], ["Cancelar", "", "cancel"]], + trace: [ + ["Botón Nuevo + formulario", "Crea y actualiza el presupuesto", "sale.order"], + ["Campos cliente y líneas", "Calcula importes", "res.partner + sale.order.line"], + ["Botón Enviar por email", "action_quotation_send()", "sale.order + mail.mail"] + ] + }, + confirmacion: { + title: "Confirmar pedido", diagram: "D-ROB-VEN-004", state: "PRESUPUESTO ENVIADO", + actions: [["Confirmar", "primary", "confirm"], ["Volver al presupuesto", "", "back"]], + trace: [ + ["Formulario + botón Confirmar", "action_confirm()", "sale.order"], + ["Mensaje de validación", "_confirmation_error_message()", "sale.order + sale.order.line"], + ["Resumen de entrega", "_action_confirm()", "stock.picking"], + ["Opción de factura", "_create_invoices()", "account.move"] + ] + }, + reserva: { + title: "Reservar productos", diagram: "D-ROB-ENT-002", state: "ESPERANDO RESERVA", + actions: [["Comprobar disponibilidad", "primary", "reserve"], ["Ver reglas", "", "rules"]], + trace: [ + ["Botón Comprobar disponibilidad", "action_assign()", "stock.picking"], + ["Tabla de disponibilidad", "Busca y reserva cantidad", "stock.move.line"], + ["Aviso de faltante", "Ejecuta reabastecimiento", "stock.warehouse.orderpoint + purchase.order"] + ] + }, + factura: { + title: "Crear factura", diagram: "D-ROB-FAC-001", state: "PARA FACTURAR", + actions: [["Crear factura", "primary", "invoice"], ["Cancelar", "", "cancel"]], + trace: [ + ["Botón Crear factura", "_create_invoices()", "sale.order"], + ["Opciones de facturación", "Valida líneas facturables", "sale.order.line"], + ["Vista previa de datos", "Calcula y crea factura", "account.move + account.move.line + account.tax"] + ] + } +}; + +const money = new Intl.NumberFormat("es-AR", {style:"currency", currency:"ARS", maximumFractionDigits:0}); +const lines = [{product:"Notebook Pro 14", qty:2, price:210000, discount:0, tax:"IVA 21%"},{product:"Dock USB-C", qty:3, price:22000, discount:5, tax:"IVA 21%"}]; +let activeScenario = "presupuesto"; + +function renderLines(){ + const body=document.querySelector("#order-lines"); + body.innerHTML=lines.map((line,i)=>`${line.product}${line.qty}${money.format(line.price)}${line.discount}%${line.tax}${money.format(line.qty*line.price*(1-line.discount/100))}`).join(""); + const untaxed=lines.reduce((sum,line)=>sum+line.qty*line.price*(1-line.discount/100),0); + document.querySelector("#untaxed").textContent=money.format(untaxed); + document.querySelector("#tax").textContent=money.format(untaxed*.21); + document.querySelector("#total").textContent=money.format(untaxed*1.21); +} + +function renderScenario(key){ + activeScenario=key; const scenario=scenarios[key]; + document.querySelectorAll(".nav-item").forEach(item=>item.classList.toggle("active",item.dataset.scenario===key)); + document.querySelectorAll(".screen").forEach(screen=>screen.classList.toggle("active",screen.id===`screen-${key}`)); + document.querySelector("#screen-title").textContent=scenario.title; + document.querySelector("#diagram-id").textContent=scenario.diagram; + document.querySelector("#record-state").textContent=scenario.state; + document.querySelector("#primary-actions").innerHTML=scenario.actions.map(([label,kind,action])=>``).join(""); + document.querySelector("#trace-grid").innerHTML=scenario.trace.map(row=>`
Boundary${row[0]}

Elemento que ve o utiliza el actor.

Control${row[1]}

Comportamiento disparado.

Entity${row[2]}

Datos consultados o modificados.

`).join(""); + document.querySelector("#diagram-link").href=`../diagrams/svg/${encodeURIComponent(scenario.diagram)}%20${encodeURIComponent("—")}%20${diagramFilename(key)}.svg`; + log("Pantalla preparada",`Estás explorando ${scenario.diagram}. Usá los botones para simular el flujo.`); +} + +function diagramFilename(key){return {presupuesto:"Diagrama%20de%20Robustez:%20Crear%20Presupuesto",confirmacion:"Diagrama%20de%20Robustez:%20Confirmar%20Pedido",reserva:"Diagrama%20de%20Robustez:%20Reservar%20Productos",factura:"Diagrama%20de%20Robustez:%20Crear%20Factura"}[key]} +function log(title,detail){document.querySelector("#event-title").textContent=title;document.querySelector("#event-detail").textContent=detail} + +document.querySelector("#scenario-nav").addEventListener("click",event=>{const item=event.target.closest("[data-scenario]");if(item)renderScenario(item.dataset.scenario)}); +document.querySelector(".view-switch").addEventListener("click",event=>{const button=event.target.closest("[data-view]");if(!button)return;document.querySelectorAll(".view-switch button").forEach(b=>b.classList.toggle("active",b===button));document.querySelectorAll(".view").forEach(v=>v.classList.toggle("active",v.id===`${button.dataset.view}-view`))}); +document.querySelector("#primary-actions").addEventListener("click",event=>{ + const action=event.target.dataset.action;if(!action)return; + const messages={save:["Presupuesto guardado","Boundary formulario → actualiza entity sale.order."],email:["Correo preparado","Boundary Enviar → action_quotation_send() → mail.mail."],cancel:["Acción cancelada","No se modificaron entidades en esta simulación."],back:["Volver al presupuesto","Navegación hacia view_sale_order_form."],rules:["Reglas consultadas","Control de reabastecimiento → stock.warehouse.orderpoint."],confirm:["Pedido confirmado","action_confirm() cambió sale.order y generó stock.picking."],reserve:["Reserva procesada","action_assign() reservó 2 unidades y detectó disponibilidad parcial."],invoice:["Factura borrador creada","_create_invoices() → account.move + account.move.line."]}; + if(action==="confirm")document.querySelector("#record-state").textContent="PEDIDO CONFIRMADO"; + if(action==="reserve"){document.querySelector("#reserved-a").textContent="2";document.querySelector("#reserved-b").textContent="1";document.querySelector("#reserve-state-a").textContent="Reservado";document.querySelector("#reserve-state-a").className="pill ok";document.querySelector("#reserve-state-b").textContent="Parcial";document.querySelector("#reserve-state-b").className="pill partial";document.querySelector("#record-state").textContent="PARCIALMENTE DISPONIBLE"} + if(action==="invoice")document.querySelector("#record-state").textContent="FACTURA BORRADOR"; + log(...messages[action]); +}); +document.querySelector("#add-line").addEventListener("click",()=>{lines.push({product:"Monitor 27 pulgadas",qty:1,price:185000,discount:0,tax:"IVA 21%"});renderLines();log("Línea agregada","El formulario actualiza sale.order.line y recalcula los importes de sale.order.")}); + +renderLines();renderScenario(activeScenario); diff --git a/prototipo/index.html b/prototipo/index.html new file mode 100644 index 0000000..1357a96 --- /dev/null +++ b/prototipo/index.html @@ -0,0 +1,99 @@ + + + + + + + Prototipo ICONIX · SAP-TFI-2026 + + + +
+ + S + SAP · TFILaboratorio ICONIX + +
UAI · IngenieríaPrototipo educativo
+
+ +
+ + +
+
+

D-ROB-VEN-001

Crear presupuesto

+
+ + +
+
+ +
+
+

Ventas / Presupuestos / Nuevo

+
+
+
+ BORRADOR +
+ +
+
PresupuestoNuevo
+
+ + + + +
+
+
ProductoCant.PrecioDto.ImpuestoSubtotal
+ +
Base imponible $ 0IVA 21% $ 0Total $ 0
+
+ +
+
✓

Pedido listo para confirmar

S00042 · Industrias Horizonte S.A.

$ 588.060
+
EstadoPresupuesto enviado
Líneas2 productos válidos
StockSe generará una entrega
FacturaciónSegún cantidades entregadas
+
Al confirmar

El sistema cambia el estado del pedido, fija la fecha y genera el picking de almacén.

+
+ +
+
TransferenciaWH/OUT/00031
+
PedidoReservaPreparadoEntregado
+
ProductoDemandaDisponibleReservadoResultado
Notebook Pro 14250Pendiente
Dock USB-C310Pendiente
+
Disponibilidad parcial

La segunda línea puede requerir reabastecimiento según la regla configurada.

+
+ +
+
$

Pedido confirmado

S00042 · Pendiente de facturar

$ 588.060
+
+

Datos que se crearán

account.moveEstado: draft
account.move.line2 líneas
account.taxIVA 21%
+
+
+
+

Acción observada

Formulario preparado

Seleccioná una acción para ver su relación con ICONIX.

+
+ +
+

Esta vista une cada elemento visible con el control que invoca y las entidades que consulta o modifica.

Abrir diagrama SVG ↗
+
+
+
+
+ + + diff --git a/prototipo/styles.css b/prototipo/styles.css new file mode 100644 index 0000000..57f73d0 --- /dev/null +++ b/prototipo/styles.css @@ -0,0 +1,3 @@ +:root{--ink:#17202a;--muted:#687383;--line:#dfe4e8;--paper:#fff;--canvas:#f2f4f5;--blue:#2864dc;--blue-soft:#eaf1ff;--teal:#087e78;--amber:#b46b07;--red:#b53b45;--shadow:0 18px 55px rgba(25,34,45,.10)} +*{box-sizing:border-box}body{margin:0;background:var(--canvas);color:var(--ink);font:15px/1.45 Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}.topbar{height:72px;background:#122030;color:white;display:flex;align-items:center;justify-content:space-between;padding:0 30px;box-shadow:0 1px 0 rgba(255,255,255,.1)}.brand{display:flex;gap:12px;align-items:center;color:white;text-decoration:none}.brand-mark{display:grid;place-items:center;width:38px;height:38px;background:#f1b740;color:#17202a;font:bold 20px Georgia;border-radius:11px}.brand span:last-child{display:flex;flex-direction:column}.brand small,.course{color:#aeb9c7;font-size:12px}.course{display:flex;gap:22px}.status-dot:before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:#53d398;margin-right:7px}.workspace{display:grid;grid-template-columns:230px minmax(0,1fr);min-height:calc(100vh - 72px)}.sidebar{padding:30px 18px;background:#f8f9fa;border-right:1px solid var(--line)}.eyebrow{text-transform:uppercase;letter-spacing:.11em;font-size:10px;font-weight:800;color:var(--muted);margin:0 0 12px}.nav-item{width:100%;border:0;background:transparent;text-align:left;padding:12px;border-radius:10px;color:var(--ink);cursor:pointer;display:grid;grid-template-columns:28px 1fr;font-weight:700}.nav-item span{color:#9aa3ad}.nav-item small{grid-column:2;color:var(--muted);font-weight:500}.nav-item.active{background:white;color:var(--blue);box-shadow:0 5px 20px rgba(25,34,45,.08)}.legend{margin-top:36px;padding:16px 12px;border-top:1px solid var(--line);display:grid;gap:10px}.legend p{margin-bottom:2px}.legend span{font-size:12px;color:var(--muted)}.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:8px}.boundary{background:var(--blue)}.control{background:var(--amber)}.entity{background:var(--teal)}.stage{padding:34px clamp(20px,4vw,60px);max-width:1400px;width:100%;margin:0 auto}.stage-heading{display:flex;align-items:end;justify-content:space-between;margin-bottom:20px}.stage-heading h1{font:700 clamp(28px,3vw,42px)/1.1 Georgia,serif;margin:0}.view-switch{padding:4px;background:#e3e7ea;border-radius:10px}.view-switch button{border:0;padding:8px 14px;border-radius:7px;background:transparent;color:var(--muted);cursor:pointer}.view-switch button.active{background:white;color:var(--ink);box-shadow:0 2px 8px rgba(0,0,0,.08)}.view{display:none}.view.active{display:block}.browser-frame{background:white;border:1px solid #d5dbe0;border-radius:14px;overflow:hidden;box-shadow:var(--shadow)}.browser-bar{height:42px;background:#edf0f2;display:flex;align-items:center;gap:7px;padding:0 14px;border-bottom:1px solid var(--line)}.browser-bar>span{width:9px;height:9px;border-radius:50%;background:#bec5cb}.browser-bar>span:first-child{background:#e2766f}.browser-bar p{margin:auto;color:#89939d;font-size:12px}.app-shell{min-height:520px}.actionbar{height:64px;display:flex;align-items:center;justify-content:space-between;padding:0 26px;border-bottom:1px solid var(--line)}.actionbar button{border:1px solid var(--line);background:white;padding:9px 14px;border-radius:7px;margin-right:8px;font-weight:700;cursor:pointer}.actionbar button.primary{background:var(--blue);border-color:var(--blue);color:white}.record-state{font-size:11px;font-weight:800;letter-spacing:.08em;color:var(--blue);background:var(--blue-soft);padding:7px 10px;border-radius:20px}.screen{display:none;padding:30px}.screen.active{display:block}.form-title{display:flex;align-items:baseline;gap:12px;margin-bottom:25px}.form-title span{font-size:13px;color:var(--muted)}.form-title strong{font:700 26px Georgia,serif}.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 26px}.form-grid label{color:var(--muted);font-size:12px;font-weight:700}.form-grid input,.form-grid select{display:block;width:100%;margin-top:6px;padding:10px;border:1px solid var(--line);border-radius:6px;background:white;color:var(--ink)}.tabs{margin-top:30px;border-bottom:1px solid var(--line)}.tabs button{border:0;background:transparent;padding:10px 2px;margin-right:24px;color:var(--muted)}.tabs button.active{color:var(--blue);border-bottom:2px solid var(--blue);font-weight:700}.table-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:13px}th{text-align:left;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.06em}th,td{padding:13px 10px;border-bottom:1px solid var(--line)}.text-button{border:0;background:transparent;color:var(--blue);padding:14px 10px;cursor:pointer}.totals{margin-left:auto;width:min(330px,100%);display:grid;gap:7px;padding-top:12px}.totals span,.totals strong{display:flex;justify-content:space-between}.totals strong{font-size:17px;border-top:1px solid var(--line);padding-top:10px}.summary-card{display:flex;align-items:center;gap:15px;padding:18px;background:#f8fafb;border:1px solid var(--line);border-radius:10px}.summary-card p,.summary-card h2{margin:0}.summary-card p{color:var(--muted);font-size:12px}.summary-card h2{font-size:17px}.summary-card>strong{margin-left:auto}.icon{display:grid;place-items:center;width:42px;height:42px;border-radius:50%;font-weight:800}.success{background:#ddf5ea;color:#14825e}.invoice{background:var(--blue-soft);color:var(--blue)}.validation-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin:24px 0}.validation-grid article{border:1px solid var(--line);border-radius:9px;padding:16px}.validation-grid span{display:block;color:var(--muted);font-size:11px;margin-bottom:5px}.callout{border-left:4px solid var(--blue);background:var(--blue-soft);padding:14px 16px}.callout p{margin:3px 0 0;color:var(--muted)}.callout.warning{border-color:#e39a27;background:#fff5df;margin-top:24px}.progress{display:flex;margin:25px 0}.progress span{flex:1;border-top:3px solid var(--line);padding-top:9px;color:var(--muted);font-size:12px}.progress .done,.progress .active{border-color:var(--blue);color:var(--blue);font-weight:700}.pill{padding:5px 8px;border-radius:14px;font-size:11px}.waiting{background:#f0f2f4;color:#687383}.ok{background:#ddf5ea;color:#087658}.partial{background:#fff0cf;color:#94600b}.invoice-options{display:grid;gap:12px;margin:24px 0}.invoice-options label{display:grid;grid-template-columns:22px 1fr;padding:16px;border:1px solid var(--line);border-radius:9px;font-weight:700}.invoice-options input{grid-row:1/3}.invoice-options small{font-weight:400;color:var(--muted)}.model-preview{background:#182433;color:white;border-radius:10px;padding:18px}.model-preview .eyebrow{color:#9eacba}.model-preview div{display:flex;justify-content:space-between;border-top:1px solid rgba(255,255,255,.12);padding:10px 0;font-family:ui-monospace,monospace;font-size:12px}.model-preview b{color:#8fdfc5}.event-log{margin-top:16px;display:grid;grid-template-columns:220px 1fr;gap:25px;padding:17px 20px;background:#17202a;color:white;border-radius:10px}.event-log p{margin:0;color:#b2bdc8}.event-log .eyebrow{color:#7f8d9a;margin-bottom:4px}.trace-intro{display:flex;justify-content:space-between;align-items:center;background:white;padding:18px 22px;border-radius:12px;margin-bottom:15px}.trace-intro p{margin:0}.trace-intro a{color:var(--blue);font-weight:700;text-decoration:none}.trace-grid{display:grid;gap:12px}.trace-row{display:grid;grid-template-columns:1fr 1fr 1fr;background:white;border:1px solid var(--line);border-radius:10px;overflow:hidden}.trace-cell{padding:18px;border-right:1px solid var(--line)}.trace-cell:last-child{border:0}.trace-cell span{display:block;font-size:10px;text-transform:uppercase;letter-spacing:.08em;font-weight:800;margin-bottom:6px}.trace-cell.boundary-cell span{color:var(--blue)}.trace-cell.control-cell span{color:var(--amber)}.trace-cell.entity-cell span{color:var(--teal)}.trace-cell p{margin:0;color:var(--muted);font-size:13px} +@media(max-width:850px){.workspace{grid-template-columns:1fr}.sidebar{border:0;border-bottom:1px solid var(--line);padding:16px}.sidebar nav{display:flex;overflow:auto;gap:6px}.nav-item{min-width:150px}.legend{display:none}.stage{padding:24px 14px}.form-grid,.validation-grid{grid-template-columns:1fr}.trace-row{grid-template-columns:1fr}.trace-cell{border-right:0;border-bottom:1px solid var(--line)}.event-log{grid-template-columns:1fr;gap:6px}.course{display:none}}