mirror of
https://git.imnavajas.es/jjimenez/safekat.git
synced 2025-07-25 22:52:08 +00:00
falta calcular maximo solapas
This commit is contained in:
@ -385,6 +385,7 @@ return [
|
|||||||
'noInterior' => 'No se hay resultados para el interior',
|
'noInterior' => 'No se hay resultados para el interior',
|
||||||
'noCubiertaSobrecubierta' => 'No se hay resultados para cubierta/sobrecubierta',
|
'noCubiertaSobrecubierta' => 'No se hay resultados para cubierta/sobrecubierta',
|
||||||
'errorPresupuesto' => 'Se ha producido un error al calcular el presupuesto. Póngase en contacto con el administrador',
|
'errorPresupuesto' => 'Se ha producido un error al calcular el presupuesto. Póngase en contacto con el administrador',
|
||||||
|
'error_sobrecubierta_sin_solapas' => 'Debe seleccionar "sobrecubierta" en los datos del libro para introducir el ancho de solapa'
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -116,10 +116,9 @@
|
|||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
<div class="form-check form-switch mb-2">
|
<div class="form-check form-switch mb-2">
|
||||||
<input class="form-check-input solapas_sobrecubierta" type="checkbox"
|
<input class="form-check-input solapas_sobrecubierta" type="checkbox"
|
||||||
id="solapas_sobrecubierta" name="solapas_sobrecubierta" tabindex="6" value="1"
|
id="solapas_sobrecubierta" name="solapas_sobrecubierta" tabindex="6" value="1">
|
||||||
onclick="return false;">
|
|
||||||
<label class="form-check-label"
|
<label class="form-check-label"
|
||||||
for="solapas_sobrecubierta"><?= lang('Presupuestos.solapasSobrecubierta') ?></label>
|
for="solapas_sobrecubierta"><?= lang('Presupuestos.sobrecubierta') ?></label>
|
||||||
</div>
|
</div>
|
||||||
</div><!--//.mb-3 -->
|
</div><!--//.mb-3 -->
|
||||||
</div>
|
</div>
|
||||||
@ -301,10 +300,10 @@
|
|||||||
</div><!--//.mb-3 -->
|
</div><!--//.mb-3 -->
|
||||||
</div><!--//.col -->
|
</div><!--//.col -->
|
||||||
|
|
||||||
<input type="checkbox" id="solapas_cubierta" name="solapas_cubierta" value="1"
|
<input type="checkbox" id="serv_solapas_cubierta" name="serv_solapas_cubierta" value="1"
|
||||||
class="form-check-input d-none" service="manipulado"
|
class="form-check-input d-none" service="manipulado"
|
||||||
service-id=<?= $serviciosAutomaticos['solapas_cubierta'] ?>>
|
service-id=<?= $serviciosAutomaticos['solapas_cubierta'] ?>>
|
||||||
<input type="checkbox" id="solapas_sobrecubierta" name="solapas_sobrecubierta" value="1"
|
<input type="checkbox" id="serv_solapas_sobrecubierta" name="serv_solapas_sobrecubierta" value="1"
|
||||||
class="form-check-input d-none" service="manipulado"
|
class="form-check-input d-none" service="manipulado"
|
||||||
service-id=<?= $serviciosAutomaticos['solapas_sobrecubierta'] ?>>
|
service-id=<?= $serviciosAutomaticos['solapas_sobrecubierta'] ?>>
|
||||||
<input type="checkbox" id="solapas_faja" name="solapas_faja" value="1"
|
<input type="checkbox" id="solapas_faja" name="solapas_faja" value="1"
|
||||||
|
|||||||
@ -113,6 +113,9 @@ class PresupuestoAdminEdit {
|
|||||||
$(document).on('update-presupuesto', this.updatePresupuesto.bind(this));
|
$(document).on('update-presupuesto', this.updatePresupuesto.bind(this));
|
||||||
|
|
||||||
this.guardar.on('click', this.guardarPresupuesto.bind(this));
|
this.guardar.on('click', this.guardarPresupuesto.bind(this));
|
||||||
|
|
||||||
|
$('#lomo_cubierta').on('change', this.datosLibro.changeAnchoSolapasCubierta);
|
||||||
|
$('#lomo_sobrecubierta').on('change', this.datosLibro.changeAnchoSolapasSobrecubierta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -244,7 +244,11 @@ class Comparador {
|
|||||||
this.gramajeSobrecubierta.init();
|
this.gramajeSobrecubierta.init();
|
||||||
this.papelSobrecubierta.onChange(() => self.gramajeSobrecubierta.empty());
|
this.papelSobrecubierta.onChange(() => self.gramajeSobrecubierta.empty());
|
||||||
this.sobrecubierta.on('change', () => {
|
this.sobrecubierta.on('change', () => {
|
||||||
if (this.sobrecubierta.val() == 1) {
|
if (this.sobrecubierta.select2('data')[0].id == 1) {
|
||||||
|
if($('#solapas_sobrecubierta').prop('checked') == false){
|
||||||
|
this.sobrecubierta.val(0);
|
||||||
|
popErrorAlert(window.language.Presupuestos.errores.error_sobrecubierta_sin_solapas, 'divAlarmasComparador')
|
||||||
|
}
|
||||||
$('#solapas_sobrecubierta').prop('checked', true);
|
$('#solapas_sobrecubierta').prop('checked', true);
|
||||||
$('#div_solapas_ancho_sobrecubierta').removeClass('d-none');
|
$('#div_solapas_ancho_sobrecubierta').removeClass('d-none');
|
||||||
$('#solapas_ancho_sobrecubierta').val(60);
|
$('#solapas_ancho_sobrecubierta').val(60);
|
||||||
@ -316,7 +320,6 @@ class Comparador {
|
|||||||
if (this.tipo_impresion_id == 1 || this.tipo_impresion_id == 3 || this.tipo_impresion_id == 5 || this.tipo_impresion_id == 7) {
|
if (this.tipo_impresion_id == 1 || this.tipo_impresion_id == 3 || this.tipo_impresion_id == 5 || this.tipo_impresion_id == 7) {
|
||||||
$('.comp_guardas_items').on('change', this.obtenerComparadorGuardas.bind(this));
|
$('.comp_guardas_items').on('change', this.obtenerComparadorGuardas.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#changePaginasComparador(element) {
|
#changePaginasComparador(element) {
|
||||||
|
|||||||
@ -89,6 +89,9 @@ class DatosLibro {
|
|||||||
this.solapasSobrecubierta.on('change', this.changeSolapasSobrecubierta.bind(this));
|
this.solapasSobrecubierta.on('change', this.changeSolapasSobrecubierta.bind(this));
|
||||||
this.tamanioPersonalizado.on('change', this.changeTipoTamanio.bind(this));
|
this.tamanioPersonalizado.on('change', this.changeTipoTamanio.bind(this));
|
||||||
|
|
||||||
|
this.anchoSolapasCubierta.on('change', this.changeAnchoSolapasCubierta.bind(this));
|
||||||
|
this.anchoSolapasSobrecubierta.on('change', this.changeAnchoSolapasSobrecubierta.bind(this));
|
||||||
|
|
||||||
this.paginas.on('change', this.changePaginas.bind(this));
|
this.paginas.on('change', this.changePaginas.bind(this));
|
||||||
this.tirada.on('change', this.changeTirada.bind(this));
|
this.tirada.on('change', this.changeTirada.bind(this));
|
||||||
}
|
}
|
||||||
@ -211,25 +214,30 @@ class DatosLibro {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.solapasCubierta.prop('checked')) {
|
if (this.solapasCubierta.prop('checked')) {
|
||||||
$('#div_solapas_ancho').removeClass('d-none');
|
$('#div_solapas_ancho').removeClass('d-none');
|
||||||
$(document).trigger('add-servicio-lineas', 'solapas');
|
$(document).trigger('add-servicio-lineas', 'solapas_cubierta');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$('#div_solapas_ancho').addClass('d-none');
|
$('#div_solapas_ancho').addClass('d-none');
|
||||||
this.anchoSolapasCubierta.val(60);
|
$(document).trigger('remove-servicio-lineas', 'solapas_cubierta');
|
||||||
$(document).trigger('remove-servicio-lineas', 'solapas');
|
$(document).trigger('remove-servicio-lineas', 'solapas_grandes_cubierta');
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
// para que se actualice el comparador
|
// para que se actualice el comparador
|
||||||
$('paginas').trigger('change');
|
$('paginas').trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
$(document).trigger('update-presupuesto', {
|
changeAnchoSolapasCubierta(){
|
||||||
update_lineas : false,
|
|
||||||
update_servicios : true,
|
if(this.checkSolapasGrandes('cubierta')){
|
||||||
update_envios: false,
|
$(document).trigger('add-servicio-lineas', 'solapas_grandes_cubierta');
|
||||||
update_resumen: false,
|
}
|
||||||
update_tiradas_alternativas: false
|
else{
|
||||||
});
|
$(document).trigger('remove-servicio-lineas', 'solapas_grandes_cubierta');
|
||||||
|
}
|
||||||
|
|
||||||
|
// para que se actualice el comparador
|
||||||
|
$('paginas').trigger('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -237,21 +245,53 @@ class DatosLibro {
|
|||||||
|
|
||||||
if (this.solapasSobrecubierta.prop('checked')) {
|
if (this.solapasSobrecubierta.prop('checked')) {
|
||||||
this.divSolapasSobrecubierta.removeClass('d-none');
|
this.divSolapasSobrecubierta.removeClass('d-none');
|
||||||
$(document).trigger('remove-servicio-lineas', 'solapas');
|
$(document).trigger('add-servicio-lineas', 'solapas_sobrecubierta');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.divSolapasSobrecubierta.addClass('d-none');
|
this.divSolapasSobrecubierta.addClass('d-none');
|
||||||
this.anchoSolapasSobrecubierta.val(60);
|
$(document).trigger('remove-servicio-lineas', 'solapas_sobrecubierta');
|
||||||
$(document).trigger('add-servicio-lineas', 'solapas');
|
$(document).trigger('remove-servicio-lineas', 'solapas_grandes_sobrecubierta');
|
||||||
|
if ($('#compSobrecubierta').length) {
|
||||||
|
$('#compSobrecubierta').val(0).trigger('change');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$(document).trigger('update-presupuesto', {
|
}
|
||||||
update_lineas : false,
|
|
||||||
update_servicios : true,
|
|
||||||
update_envios: false,
|
|
||||||
update_resumen: false,
|
|
||||||
update_tiradas_alternativas: false
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
changeAnchoSolapasSobrecubierta(){
|
||||||
|
|
||||||
|
if(this.checkSolapasGrandes('cubierta')){
|
||||||
|
$(document).trigger('add-servicio-lineas', 'solapas_grandes_sobrecubierta');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$(document).trigger('remove-servicio-lineas', 'solapas_grandes_sobrecubierta');
|
||||||
|
}
|
||||||
|
|
||||||
|
// para que se actualice el comparador
|
||||||
|
$('paginas').trigger('change');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSolapasGrandes(elemento) {
|
||||||
|
|
||||||
|
const ancho_libro = 2 * this.getDimensionLibro().ancho;
|
||||||
|
let ancho_solapas = 0.0;
|
||||||
|
let lomo = 0.0;
|
||||||
|
if(elemento == 'cubierta'){
|
||||||
|
ancho_solapas = 2 * parseFloat($('solapas_ancho').val());
|
||||||
|
lomo = parseFloat($('lomo_cubierta').val());
|
||||||
|
}
|
||||||
|
else if (elemento == 'sobrecubierta'){
|
||||||
|
ancho_solapas = 2 * parseFloat($('solapas_ancho_sobrecubierta').val());
|
||||||
|
lomo = parseFloat($('lomo_sobrecubierta').val());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ancho_libro + ancho_solapas + lomo > 630)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
changePaginas() {
|
changePaginas() {
|
||||||
@ -393,7 +433,7 @@ class DatosLibro {
|
|||||||
if (datos.solapasCubierta) {
|
if (datos.solapasCubierta) {
|
||||||
this.solapasCubierta.prop('checked', true);
|
this.solapasCubierta.prop('checked', true);
|
||||||
this.anchoSolapasCubierta.val(datos.solapasCubiertaAncho);
|
this.anchoSolapasCubierta.val(datos.solapasCubiertaAncho);
|
||||||
this.divSolapasSobrecubierta.removeClass('d-none');
|
this.divSolapasCubierta.removeClass('d-none');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.solapasCubierta.prop('checked', false);
|
this.solapasCubierta.prop('checked', false);
|
||||||
|
|||||||
@ -110,9 +110,6 @@ class Servicios {
|
|||||||
else if (servicio == 'acabadoSobrecubierta') {
|
else if (servicio == 'acabadoSobrecubierta') {
|
||||||
this.serviciosAcabado.updateAcabadosExteriores(0, 1);
|
this.serviciosAcabado.updateAcabadosExteriores(0, 1);
|
||||||
}
|
}
|
||||||
else if (servicio == 'solapas') {
|
|
||||||
this.ServiciosManipulado.addSolapas();
|
|
||||||
}
|
|
||||||
else if (servicio == 'ferro'){
|
else if (servicio == 'ferro'){
|
||||||
const id = $('#ferro').attr('service-id');
|
const id = $('#ferro').attr('service-id');
|
||||||
this.serviciosExtra.getPresupuestoExtra(id);
|
this.serviciosExtra.getPresupuestoExtra(id);
|
||||||
@ -139,56 +136,115 @@ class Servicios {
|
|||||||
};
|
};
|
||||||
this.ServiciosManipulado.getPresupuestoManipulado(id);
|
this.ServiciosManipulado.getPresupuestoManipulado(id);
|
||||||
}
|
}
|
||||||
|
else if (servicio == 'solapas_cubierta'){
|
||||||
|
const id = $('#serv_solapas_cubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.getPresupuestoManipulado(id);
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_sobrecubierta'){
|
||||||
|
const id = $('#serv_solapas_sobrecubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.getPresupuestoManipulado(id);
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_grandes_cubierta'){
|
||||||
|
const id = $('#solapas_grandes_cubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.getPresupuestoManipulado(id);
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_grandes_sobrecubierta'){
|
||||||
|
const id = $('#solapas_grandes_sobrecubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.getPresupuestoManipulado(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeServicio(event, servicio) {
|
removeServicio(event, servicio) {
|
||||||
if (servicio == 'solapas') {
|
|
||||||
this.ServiciosManipulado.removeSolapas();
|
if (servicio == 'ferro'){
|
||||||
}
|
|
||||||
else if (servicio == 'ferro'){
|
|
||||||
const id = $('#ferro').attr('service-id');
|
const id = $('#ferro').attr('service-id');
|
||||||
this.serviciosExtra.table.rows().every(function () {
|
this.serviciosExtra.table.rows().every(function () {
|
||||||
var data = this.data();
|
let data = this.data();
|
||||||
if (data.tarifa_id == id) {
|
if (data.tarifa_id == id) {
|
||||||
this.remove().draw();
|
this.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.serviciosExtra.table.draw();
|
||||||
}
|
}
|
||||||
else if (servicio == 'prototipo'){
|
else if (servicio == 'prototipo'){
|
||||||
const id = $('#prototipo').attr('service-id');
|
const id = $('#prototipo').attr('service-id');
|
||||||
this.serviciosExtra.table.rows().every(function () {
|
this.serviciosExtra.table.rows().every(function () {
|
||||||
var data = this.data();
|
var data = this.data();
|
||||||
if (data.tarifa_id == id) {
|
if (data.tarifa_id == id) {
|
||||||
this.remove().draw();
|
this.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.serviciosExtra.table.draw();
|
||||||
}
|
}
|
||||||
else if (servicio == 'retractilado'){
|
else if (servicio == 'retractilado'){
|
||||||
const id = $('#retractilado').attr('service-id');
|
const id = $('#retractilado').attr('service-id');
|
||||||
this.serviciosAcabado.table.rows().every(function () {
|
this.serviciosAcabado.table.rows().every(function () {
|
||||||
var data = this.data();
|
let data = this.data();
|
||||||
if (data.tarifa_id == id) {
|
if (data.tarifa_id == id) {
|
||||||
this.remove().draw();
|
this.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.serviciosAcabado.table.draw();
|
||||||
}
|
}
|
||||||
else if (servicio == 'retractilado5'){
|
else if (servicio == 'retractilado5'){
|
||||||
const id = $('#retractilado5').attr('service-id');
|
const id = $('#retractilado5').attr('service-id');
|
||||||
this.serviciosAcabado.table.rows().every(function () {
|
this.serviciosAcabado.table.rows().every(function () {
|
||||||
var data = this.data();
|
let data = this.data();
|
||||||
if (data.tarifa_id == id) {
|
if (data.tarifa_id == id) {
|
||||||
this.remove().draw();
|
this.remove().draw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.serviciosAcabado.table.draw();
|
||||||
}
|
}
|
||||||
else if(servicio == 'plegado_guardas'){
|
else if(servicio == 'plegado_guardas'){
|
||||||
const id = $('#plegado_guardas').attr('service-id');
|
const id = $('#plegado_guardas').attr('service-id');
|
||||||
this.ServiciosManipulado.table.rows().every(function () {
|
this.ServiciosManipulado.table.rows().every(function () {
|
||||||
var data = this.data();
|
var data = this.data();
|
||||||
if (data.tarifa_id == id) {
|
if (data && data.tarifa_id == id) {
|
||||||
this.remove().draw();
|
this.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.ServiciosManipulado.table.draw();
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_cubierta'){
|
||||||
|
const id = $('#serv_solapas_cubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.table.rows().every(function () {
|
||||||
|
let data = this.data();
|
||||||
|
if (data && parseInt(data.tarifa_id) == id) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.ServiciosManipulado.table.draw();
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_sobrecubierta'){
|
||||||
|
const id = $('#serv_solapas_sobrecubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.table.rows().every(function () {
|
||||||
|
let data = this.data();
|
||||||
|
if (data && data.tarifa_id == id) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.ServiciosManipulado.table.draw();
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_grandes_cubierta'){
|
||||||
|
const id = $('#solapas_grandes_cubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.table.rows().every(function () {
|
||||||
|
let data = this.data();
|
||||||
|
if (data && data.tarifa_id == id) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.ServiciosManipulado.table.draw();
|
||||||
|
}
|
||||||
|
else if (servicio == 'solapas_grandes_sobrecubierta'){
|
||||||
|
const id = $('#solapas_grandes_sobrecubierta').attr('service-id');
|
||||||
|
this.ServiciosManipulado.table.rows().every(function () {
|
||||||
|
var data = this.data();
|
||||||
|
if (data && data.tarifa_id == id) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.ServiciosManipulado.table.draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1318,52 +1374,6 @@ class ServiciosManipulado {
|
|||||||
showBreadCrumbSaveButton(true);
|
showBreadCrumbSaveButton(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSolapas() {
|
|
||||||
|
|
||||||
const self = this;
|
|
||||||
new Ajax('/serviciosmanipulados/serviciosolapas', {}, {},
|
|
||||||
function (response) {
|
|
||||||
if (response.service) {
|
|
||||||
|
|
||||||
const tarifa_id = response.service;
|
|
||||||
var rows = self.table.rows().data().toArray();
|
|
||||||
var found = rows.some(row => row.tarifa_id === tarifa_id);
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
self.getPresupuestoManipulado(tarifa_id);
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
removeSolapas() {
|
|
||||||
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
new Ajax('/serviciosmanipulados/serviciosolapas', {}, {},
|
|
||||||
function (response) {
|
|
||||||
if (response.service) {
|
|
||||||
|
|
||||||
const tarifa_id = response.service;
|
|
||||||
var rows = self.table.rows().data().toArray();
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
if (rows[i]['tarifa_id'] == tarifa_id) {
|
|
||||||
self.table.row(i).remove().draw();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateServicios() {
|
updateServicios() {
|
||||||
|
|
||||||
@ -1380,7 +1390,6 @@ class ServiciosManipulado {
|
|||||||
if (tarifa_ids.length > 0) {
|
if (tarifa_ids.length > 0) {
|
||||||
const data = {
|
const data = {
|
||||||
datos: tarifa_ids,
|
datos: tarifa_ids,
|
||||||
solapas: $('#solapas').is(':checked') ? 1 : 0,
|
|
||||||
tirada: tirada
|
tirada: tirada
|
||||||
};
|
};
|
||||||
new Ajax('/serviciosmanipulados/update/' + id, data, {},
|
new Ajax('/serviciosmanipulados/update/' + id, data, {},
|
||||||
|
|||||||
Reference in New Issue
Block a user