Corte de caja asistido (esperado vs contado, diferencias, PDF) y fotos antes/despues con comparacion en expediente

This commit is contained in:
2026-09-08 07:27:57 +00:00
parent e8690dd74e
commit 4f9a16103f
7 changed files with 672 additions and 81 deletions

View File

@@ -1341,6 +1341,11 @@ class SkeenFrontendController(http.Controller):
'name': a.name or '',
'mimetype': a.mimetype or '',
'notes': a.notes or '',
'antes_despues': a.antes_despues,
'visita_id': a.visita_id.id if a.visita_id else None,
'cita_id': a.cita_id.id if a.cita_id else None,
'servicio_id': a.servicio_id.id if a.servicio_id else None,
'servicio': a.servicio_id.name if a.servicio_id else '',
'create_date': a.create_date.strftime('%Y-%m-%d %H:%M') if a.create_date else None,
'url': f'/skeen/frontend/v1/patients/adjuntos/{a.id}/file',
}
@@ -1383,18 +1388,50 @@ class SkeenFrontendController(http.Controller):
return json_response({'status': 'error', 'message': 'Archivo vacío'}, 400)
if len(raw) > 10 * 1024 * 1024:
return json_response({'status': 'error', 'message': 'El archivo excede 10 MB'}, 400)
adj = request.env['skeen.patient.adjunto'].sudo().create({
adj_vals = {
'partner_id': patient.id,
'kind': kind,
'name': data.get('name') or 'archivo',
'file': file_b64,
'mimetype': data.get('mimetype') or '',
'notes': data.get('notes', ''),
})
}
if data.get('antes_despues') in ('sin_clasificar', 'antes', 'despues'):
adj_vals['antes_despues'] = data['antes_despues']
for key in ('visita_id', 'cita_id', 'servicio_id'):
if data.get(key):
adj_vals[key] = int(data[key])
adj = request.env['skeen.patient.adjunto'].sudo().create(adj_vals)
# Si clasifican la foto y no dieron servicio, heredarlo de la visita
if adj.visita_id and not adj.servicio_id:
adj.servicio_id = adj.visita_id.servicio_id
return json_response({'status': 'success', 'adjunto': self._patient_adjunto_to_dict(adj)}, 201)
except Exception as e:
return json_response({'status': 'error', 'message': str(e)}, 500)
@http.route('/skeen/frontend/v1/patients/adjuntos/<int:adj_id>', type='http', auth='none', methods=['PUT'], csrf=False)
@require_role('recepcion')
def update_patient_adjunto(self, adj_id, **kw):
"""Actualiza la clasificación antes/después (y contexto) de un adjunto."""
try:
adj = request.env['skeen.patient.adjunto'].sudo().browse(adj_id)
if not adj.exists():
return json_response({'status': 'error', 'message': 'Adjunto no encontrado'}, 404)
data = _parse_json_body()
vals = {}
if data.get('antes_despues') in ('sin_clasificar', 'antes', 'despues'):
vals['antes_despues'] = data['antes_despues']
for key in ('visita_id', 'cita_id', 'servicio_id'):
if key in data:
vals[key] = int(data[key]) if data.get(key) else None
if vals:
adj.write(vals)
if adj.visita_id and not adj.servicio_id:
adj.servicio_id = adj.visita_id.servicio_id
return json_response({'status': 'success', 'adjunto': self._patient_adjunto_to_dict(adj)})
except Exception as e:
return json_response({'status': 'error', 'message': str(e)}, 500)
@http.route('/skeen/frontend/v1/patients/adjuntos/<int:adj_id>', type='http', auth='none', methods=['DELETE'], csrf=False)
@require_role('recepcion')
def delete_patient_adjunto(self, adj_id, **kw):
@@ -2664,6 +2701,9 @@ class SkeenFrontendController(http.Controller):
if kw.get('date'):
domain.append(('date', '=', kw.get('date')))
closings = request.env['skeen.corte.caja'].sudo().search(domain, order='date desc', limit=100)
# Los pagos pueden llegar después de abrir la caja: refrescar
# los esperados de los cortes abiertos.
closings.filtered(lambda c: c.state == 'open').refresh_totals()
return json_response({
'status': 'success',
'cash_closings': [self._cash_closing_to_dict(c) for c in closings],
@@ -2693,9 +2733,24 @@ class SkeenFrontendController(http.Controller):
closing = request.env['skeen.corte.caja'].sudo().browse(closing_id)
if not closing.exists():
return json_response({'status': 'error', 'message': 'Corte no encontrado'}, 404)
closing.refresh_totals()
data = _parse_json_body()
if 'closing_cash' in data:
closing.write({'closing_cash': data['closing_cash']})
vals = {}
for key in ('closing_cash', 'counted_cash', 'counted_card',
'counted_transfer', 'counted_other'):
if key in data:
try:
vals[key] = float(data[key] or 0)
except (TypeError, ValueError):
return json_response({'status': 'error', 'message': f'Valor inválido: {key}'}, 400)
if 'notes' in data:
vals['notes'] = data.get('notes', '')
# Compatibilidad: si solo se envió el conteo, el cierre de efectivo
# registrado es el contado (difference del listado usa closing_cash).
if 'closing_cash' not in vals and 'counted_cash' in vals:
vals['closing_cash'] = vals['counted_cash']
if vals:
closing.write(vals)
closing.action_close()
return json_response({'status': 'success', 'cash_closing': self._cash_closing_to_dict(closing)})
except Exception as e:
@@ -2715,6 +2770,15 @@ class SkeenFrontendController(http.Controller):
'total_sales': c.total_sales,
'closing_cash': c.closing_cash,
'difference': c.difference,
'counted_cash': c.counted_cash,
'counted_card': c.counted_card,
'counted_transfer': c.counted_transfer,
'counted_other': c.counted_other,
'diff_cash': c.diff_cash,
'diff_card': c.diff_card,
'diff_transfer': c.diff_transfer,
'diff_other': c.diff_other,
'notes': c.notes or '',
'state': c.state,
}