diff --git a/frontend/src/pages/Inventario.tsx b/frontend/src/pages/Inventario.tsx index e8d3675..32ac3b5 100644 --- a/frontend/src/pages/Inventario.tsx +++ b/frontend/src/pages/Inventario.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import { useCallback, useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { Boxes, Plus, AlertTriangle, PackageCheck, ArrowDownToLine, ArrowUpFromLine, History } from 'lucide-react'; +import { Boxes, Plus, AlertTriangle, PackageCheck, ArrowDownToLine, ArrowUpFromLine, History, Wallet } from 'lucide-react'; import Layout from '../components/Layout'; import MovimientosPanel from '../components/inventario/MovimientosPanel'; import AlertasPanel from '../components/inventario/AlertasPanel'; @@ -153,6 +153,16 @@ const Inventario: FC = () => { } }; + const toggleMonedero = async (it: InventoryItem) => { + try { + await odooApi.updateInventoryItem(it.id, { genera_monedero: it.genera_monedero === false }); + toast.success(it.genera_monedero === false ? 'El artículo ahora genera monedero' : 'El artículo ya no genera monedero'); + load(); + } catch { + toast.error('Error al actualizar monedero'); + } + }; + const lvl = summary.levels; return ( @@ -276,6 +286,17 @@ const Inventario: FC = () => { {it.expiry_date || '—'}
+
diff --git a/frontend/src/pages/Servicios.tsx b/frontend/src/pages/Servicios.tsx index d3c3856..e78dfe5 100644 --- a/frontend/src/pages/Servicios.tsx +++ b/frontend/src/pages/Servicios.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; import { useEffect, useState, useCallback } from 'react'; -import { Star, Edit2, Plus, Clock, Tag, Package, Search } from 'lucide-react'; +import { Star, Wallet, Edit2, Plus, Clock, Tag, Package, Search } from 'lucide-react'; import Layout from '../components/Layout'; import { Card, @@ -123,6 +123,17 @@ const Servicios: FC = () => { } }; + const toggleMonedero = async (s: Service) => { + try { + await odooApi.updateService(s.id, { genera_monedero: s.genera_monedero === false }); + toast.success(s.genera_monedero === false ? 'El servicio ahora genera monedero' : 'El servicio ya no genera monedero'); + await load(); + } catch (err) { + toast.error('Error al actualizar monedero'); + console.error(err); + } + }; + const openCreate = () => { setEditing(null); setForm(emptyForm); @@ -273,18 +284,31 @@ const Servicios: FC = () => { />

{s.name}

- +
+ + +
diff --git a/frontend/src/services/odoo.ts b/frontend/src/services/odoo.ts index 98e1f14..33cb6f5 100644 --- a/frontend/src/services/odoo.ts +++ b/frontend/src/services/odoo.ts @@ -239,6 +239,7 @@ export interface Service { color: string; service_group: string; is_favorite: boolean; + genera_monedero?: boolean; } export interface Doctor { @@ -383,6 +384,7 @@ export interface InventoryItem { expiry_date: string | null; last_count_date: string | null; active: boolean; + genera_monedero?: boolean; notes: string; stock_level: InventoryLevel; } diff --git a/odoo-addons/skeen_citas/models/cita.py b/odoo-addons/skeen_citas/models/cita.py index ad7754c..12673df 100644 --- a/odoo-addons/skeen_citas/models/cita.py +++ b/odoo-addons/skeen_citas/models/cita.py @@ -35,6 +35,8 @@ class SkeenServicio(models.Model): color = fields.Char(string='Color', default='#1abc9c') service_group = fields.Char(string='Grupo') is_favorite = fields.Boolean(string='Favorito', default=False) + genera_monedero = fields.Boolean(string='Genera monedero', default=True, + help='Si está activo, la venta de este servicio acumula puntos monedero.') tag_ids = fields.Many2many('skeen.service.tag', string='Etiquetas') # Configuración diff --git a/odoo-addons/skeen_citas/views/cita_views.xml b/odoo-addons/skeen_citas/views/cita_views.xml index cc1aabe..8ffc7bd 100644 --- a/odoo-addons/skeen_citas/views/cita_views.xml +++ b/odoo-addons/skeen_citas/views/cita_views.xml @@ -120,6 +120,7 @@ + diff --git a/odoo-addons/skeen_inventario/models/inventario.py b/odoo-addons/skeen_inventario/models/inventario.py index 55867c1..7441f03 100644 --- a/odoo-addons/skeen_inventario/models/inventario.py +++ b/odoo-addons/skeen_inventario/models/inventario.py @@ -28,6 +28,8 @@ class SkeenInventarioItem(models.Model): expiry_date = fields.Date(string='Caducidad') last_count_date = fields.Date(string='Último conteo físico') active = fields.Boolean(string='Activo', default=True) + genera_monedero = fields.Boolean(string='Genera monedero', default=True, + help='Si está activo, la venta de este artículo acumula puntos monedero.') notes = fields.Text(string='Notas') move_ids = fields.One2many('skeen.inventario.move', 'item_id', string='Movimientos') diff --git a/odoo-addons/skeen_inventario/views/inventario_views.xml b/odoo-addons/skeen_inventario/views/inventario_views.xml index e979aa2..12e30c3 100644 --- a/odoo-addons/skeen_inventario/views/inventario_views.xml +++ b/odoo-addons/skeen_inventario/views/inventario_views.xml @@ -16,6 +16,7 @@ + + diff --git a/odoo-addons/skeen_ventas/models/venta.py b/odoo-addons/skeen_ventas/models/venta.py index 5da1e5f..6a52445 100644 --- a/odoo-addons/skeen_ventas/models/venta.py +++ b/odoo-addons/skeen_ventas/models/venta.py @@ -87,11 +87,16 @@ class SkeenVenta(models.Model): 'payment_state': 'paid', 'amount_paid': venta.total, }) - # Acumular puntos + # Acumular puntos (proporcional a líneas que generan monedero) monedero = self.env['skeen.monedero'].search([('partner_id', '=', venta.partner_id.id)], limit=1) if not monedero: monedero = self.env['skeen.monedero'].create({'partner_id': venta.partner_id.id}) - points = int(venta.total / 10) + subtotal_total = sum(l.subtotal for l in venta.line_ids) + elegible = sum( + l.subtotal for l in venta.line_ids + if (l.service_id and l.service_id.genera_monedero) or (l.item_id and l.item_id.genera_monedero) + ) + points = int(venta.total * (elegible / subtotal_total) / 10) if subtotal_total else 0 if points > 0: monedero.add_points(points, description=f'Venta {venta.name}', reference=venta.name) diff --git a/odoo-addons/skeen_whatsapp/controllers/frontend.py b/odoo-addons/skeen_whatsapp/controllers/frontend.py index 6399535..dc18f28 100644 --- a/odoo-addons/skeen_whatsapp/controllers/frontend.py +++ b/odoo-addons/skeen_whatsapp/controllers/frontend.py @@ -1555,7 +1555,7 @@ class SkeenFrontendController(http.Controller): if not service.exists(): return json_response({'status': 'error', 'message': 'Servicio no encontrado'}, 404) data = _parse_json_body() - allowed = ['name', 'code', 'category', 'price', 'duration_min', 'package_price', 'package_sessions', 'package_notes', 'description', 'color', 'service_group', 'is_favorite', 'active'] + allowed = ['name', 'code', 'category', 'price', 'duration_min', 'package_price', 'package_sessions', 'package_notes', 'description', 'color', 'service_group', 'is_favorite', 'genera_monedero', 'active'] vals = {k: data[k] for k in allowed if k in data} Servicio = request.env['skeen.servicio'].sudo() if vals.get('name'): @@ -1586,6 +1586,7 @@ class SkeenFrontendController(http.Controller): 'color': s.color, 'service_group': s.service_group or '', 'is_favorite': s.is_favorite, + 'genera_monedero': s.genera_monedero, } # ============================================================ @@ -3290,6 +3291,7 @@ class SkeenFrontendController(http.Controller): 'expiry_date': it.expiry_date.strftime('%Y-%m-%d') if it.expiry_date else None, 'last_count_date': it.last_count_date.strftime('%Y-%m-%d') if it.last_count_date else None, 'active': it.active, + 'genera_monedero': it.genera_monedero, 'notes': it.notes or '', 'stock_level': it.stock_level, } @@ -3373,7 +3375,7 @@ class SkeenFrontendController(http.Controller): return json_response({'status': 'error', 'message': 'Item no encontrado'}, 404) data = _parse_json_body() allowed = ['name', 'kind', 'sku', 'category', 'unit', 'qty_optimal', 'qty_min', - 'cost', 'expiry_date', 'notes', 'active'] + 'cost', 'expiry_date', 'notes', 'active', 'genera_monedero'] vals = {} for k in allowed: if k in data: