Punto 7: gerencia puede corregir tickets sin cancelarlos (lineas, precios, descuento y notas) con ajuste automatico de monedero e inventario
This commit is contained in:
@@ -4360,6 +4360,61 @@ class SkeenFrontendController(http.Controller):
|
||||
except Exception as e:
|
||||
return json_response({'status': 'error', 'message': str(e)}, 500)
|
||||
|
||||
@http.route('/skeen/frontend/v1/sales/<int:sale_id>', type='http',
|
||||
auth='none', methods=['PUT', 'OPTIONS'], csrf=False)
|
||||
@require_role('recepcion')
|
||||
@forbid_roles('marketing')
|
||||
def update_sale(self, sale_id, **kw):
|
||||
"""Corrige un ticket ya emitido (gerencia / administrador).
|
||||
|
||||
Recepción a veces cobra mal — un servicio equivocado, una cantidad o un
|
||||
precio alzado, falta un concepto. Antes la única salida era cancelar y
|
||||
rehacer, que revierte el cobro completo. Aquí se modifica el ticket
|
||||
conservando el pago registrado.
|
||||
|
||||
`lines` reemplaza la lista de conceptos (con `id` para editar, sin `id`
|
||||
para alta; las que no aparecen se eliminan). Se requiere `motivo` y
|
||||
queda en el chatter del ticket y en la bitácora de auditoría.
|
||||
"""
|
||||
try:
|
||||
if request.frontend_user.role not in ('admin', 'gerencia'):
|
||||
return json_response({'status': 'error', 'message': 'Sin permiso'}, 403)
|
||||
sale = request.env['skeen.venta'].sudo().browse(sale_id)
|
||||
if not sale.exists():
|
||||
return json_response({'status': 'error', 'message': 'Venta no encontrada'}, 404)
|
||||
data = _parse_json_body()
|
||||
motivo = (data.get('motivo') or data.get('reason') or '').strip()
|
||||
if not motivo:
|
||||
return json_response({'status': 'error',
|
||||
'message': 'Indica el motivo de la corrección'}, 400)
|
||||
antes = {'total': sale.total, 'conceptos': len(sale.line_ids),
|
||||
'state': sale.state}
|
||||
result = sale.action_edit(
|
||||
reason=motivo,
|
||||
lines=data.get('lines'),
|
||||
discount=data.get('discount'),
|
||||
tax=data.get('tax'),
|
||||
notes=data.get('notes'),
|
||||
date=data.get('date'),
|
||||
editor=request.frontend_user.login,
|
||||
)
|
||||
self._auditar('ventas', f'venta:{sale.id}', 'edicion_ticket',
|
||||
old_value='%.2f · %d conceptos · %s' % (
|
||||
antes['total'], antes['conceptos'], antes['state']),
|
||||
new_value='%.2f · %d conceptos · %s' % (
|
||||
sale.total, len(sale.line_ids), sale.state),
|
||||
entity_label=f'{sale.name} · {sale.partner_id.name}',
|
||||
notes=motivo)
|
||||
return json_response({'status': 'success',
|
||||
'sale': self._sale_con_pagos(sale),
|
||||
'result': result})
|
||||
except ValidationError as e:
|
||||
return json_response({'status': 'error',
|
||||
'message': e.args[0] if e.args else str(e)}, 400)
|
||||
except Exception as e:
|
||||
request.env.cr.rollback()
|
||||
return json_response({'status': 'error', 'message': str(e)}, 500)
|
||||
|
||||
@http.route('/skeen/frontend/v1/sales/<int:sale_id>/pay', type='http', auth='none', methods=['POST'], csrf=False)
|
||||
@require_role('recepcion')
|
||||
@forbid_roles('marketing')
|
||||
@@ -4410,7 +4465,7 @@ class SkeenFrontendController(http.Controller):
|
||||
amount = data.get('amount') # None o <=0 => devolución total de lo pagado
|
||||
reason = data.get('reason', '')
|
||||
result = sale.action_refund(amount=amount, reason=reason)
|
||||
return json_response({'status': 'success', 'sale': self._sale_to_dict(sale), 'result': result})
|
||||
return json_response({'status': 'success', 'sale': self._sale_con_pagos(sale), 'result': result})
|
||||
except Exception as e:
|
||||
return json_response({'status': 'error', 'message': str(e)}, 500)
|
||||
|
||||
@@ -4438,7 +4493,7 @@ class SkeenFrontendController(http.Controller):
|
||||
cancelled_by=request.frontend_user.login,
|
||||
restore_stock=bool(data.get('restore_stock', True)),
|
||||
)
|
||||
return json_response({'status': 'success', 'sale': self._sale_to_dict(sale), 'result': result})
|
||||
return json_response({'status': 'success', 'sale': self._sale_con_pagos(sale), 'result': result})
|
||||
except Exception as e:
|
||||
request.env.cr.rollback()
|
||||
return json_response({'status': 'error', 'message': str(e)}, 500)
|
||||
@@ -4518,6 +4573,14 @@ class SkeenFrontendController(http.Controller):
|
||||
except Exception as e:
|
||||
return json_response({'status': 'error', 'message': str(e)}, 500)
|
||||
|
||||
def _sale_con_pagos(self, v):
|
||||
# _sale_to_dict + los pagos ligados: el modal de Tickets los muestra
|
||||
# y tras una corrección hay que devolverlos o parecería que el cobro
|
||||
# se perdió.
|
||||
pagos = [self._payment_to_dict(p)
|
||||
for p in request.env['skeen.pago'].sudo().search([('venta_id', '=', v.id)])]
|
||||
return {**self._sale_to_dict(v), 'pagos': pagos}
|
||||
|
||||
def _sale_to_dict(self, v):
|
||||
return {
|
||||
'id': v.id,
|
||||
@@ -4557,6 +4620,9 @@ class SkeenFrontendController(http.Controller):
|
||||
'is_prescribed': line.is_prescribed,
|
||||
'prescribed_by_id': line.prescribed_by_id.id if line.prescribed_by_id else None,
|
||||
'prescribed_by': line.prescribed_by_id.name if line.prescribed_by_id else None,
|
||||
'paquete_id': line.paquete_id.id if line.paquete_id else None,
|
||||
'paquete': line.paquete_id.name if line.paquete_id else '',
|
||||
'sesiones': line.paquete_id.sessions if line.paquete_id else 0,
|
||||
} for line in v.line_ids],
|
||||
}
|
||||
|
||||
@@ -6081,6 +6147,10 @@ class SkeenFrontendController(http.Controller):
|
||||
'total': total,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
# Los módulos disponibles salen de la selección del modelo para
|
||||
# que el filtro del frontend nunca se desincronice.
|
||||
'modulos': [{'value': k, 'label': v} for k, v in
|
||||
request.env['skeen.config.audit']._fields['module'].selection],
|
||||
'total_pages': (total + page_size - 1) // page_size,
|
||||
})
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user