From 02f7bd47c30a4f01e02bedd936411f5b403ea52e Mon Sep 17 00:00:00 2001 From: Esteban Sassone Date: Thu, 5 Apr 2018 17:26:33 -0300 Subject: [PATCH] Refs #17634 - Refactor: Facturador --- src/siu/sq/lib/constantes.php | 14 + src/siu/sq/lib/daos/catalogo.php | 16 + src/siu/sq/lib/daos/dao_alarmas.php | 161 +++ src/siu/sq/lib/daos/dao_reloj_facturador.php | 256 +++++ src/siu/sq/lib/modelo/catalogo.php | 20 + src/siu/sq/lib/modelo/facturador.php | 918 ++++++++++++++++++ src/siu/sq/lib/modelo/facturador_alarma.php | 178 ++++ src/siu/sq/lib/modelo/reloj.php | 306 ++++++ src/siu/sq/lib/modelo/reloj_instancia.php | 49 + src/siu/sq/lib/procesos/catalogo.php | 15 + .../proceso_confirmar_facturacion.php | 62 ++ .../procesos/proceso_generar_facturacion.php | 123 +++ .../procesos/proceso_revertir_facturacion.php | 54 ++ 13 files changed, 2172 insertions(+) create mode 100644 src/siu/sq/lib/daos/dao_alarmas.php create mode 100644 src/siu/sq/lib/daos/dao_reloj_facturador.php create mode 100644 src/siu/sq/lib/modelo/facturador.php create mode 100644 src/siu/sq/lib/modelo/facturador_alarma.php create mode 100644 src/siu/sq/lib/modelo/reloj.php create mode 100644 src/siu/sq/lib/modelo/reloj_instancia.php create mode 100644 src/siu/sq/lib/procesos/proceso_confirmar_facturacion.php create mode 100644 src/siu/sq/lib/procesos/proceso_generar_facturacion.php create mode 100644 src/siu/sq/lib/procesos/proceso_revertir_facturacion.php diff --git a/src/siu/sq/lib/constantes.php b/src/siu/sq/lib/constantes.php index 7d2db9f..262c328 100644 --- a/src/siu/sq/lib/constantes.php +++ b/src/siu/sq/lib/constantes.php @@ -20,4 +20,18 @@ class constantes const PROCESO_INICIAR_WORKERS = 14; const PROCESO_VERIFICAR_ACTIVIDAD_WORKERS = 16; const PROCESO_REENVIAR_MENSAJE = 17; + const PROCESO_GENERAR_FACTURACION = 4; + const PROCESO_REVERTIR_FACTURACION = 7; + const PROCESO_CONFIRMAR_FACTURACION = 8; + const RELOJ_FACTURACION_ESTADO_NOINICIADO = 1; + const RELOJ_FACTURACION_ESTADO_ACTUAL = 2; + const RELOJ_FACTURACION_ESTADO_EJECUTADO = 3; + const BUFFER_RELOJES = 'BUF_RELOJ'; + const ID_TIPO_FACTURADOR_ALARMA_VALOR = 1; + const ID_TIPO_FACTURADOR_ALARMA_VENTA_ANULADA = 2; + const FACTURADOR_ALARMA_IMP_MAX = 1; + const FACTURADOR_ALARMA_VEN_ANULAD = 2; + const PARAMETROS_SISTEMA_FACTURACION_LIMITE = 'FACT_LIMIT'; + const PARAMETROS_SISTEMA_CANTIDAD_CICLOS_REVIVIR_VENTAS_SUSCRIPCION_INACTIVA = 'CNT_CIC_SI'; + const PARAMETROS_SISTEMA_CANTIDAD_CICLOS_REVIVIR_VENTAS_SUSCRIPCION_ACTIVA = 'CNT_CIC_SA'; } diff --git a/src/siu/sq/lib/daos/catalogo.php b/src/siu/sq/lib/daos/catalogo.php index efd694d..eb417b8 100644 --- a/src/siu/sq/lib/daos/catalogo.php +++ b/src/siu/sq/lib/daos/catalogo.php @@ -26,4 +26,20 @@ class catalogo extends \SIU\SQ\Lib\entorno\catalogo { return new dao_comprobantes($this->entorno); } + + /** + * @return dao_alarmas + */ + function alarmas() + { + return new dao_alarmas($this->entorno); + } + + /** + * @return dao_reloj_facturador + */ + function reloj_faturador() + { + return new dao_reloj_facturador($this->entorno); + } } diff --git a/src/siu/sq/lib/daos/dao_alarmas.php b/src/siu/sq/lib/daos/dao_alarmas.php new file mode 100644 index 0000000..fc6961d --- /dev/null +++ b/src/siu/sq/lib/daos/dao_alarmas.php @@ -0,0 +1,161 @@ +quote($filtro['id']); + } + if(isset($filtro['codigo'])) { + $where .= " AND fa.codigo = " . $this->quote($filtro['codigo']); + } + if(isset($filtro['descripcion'])) { + $where .= " AND fa.descripcion = " . $this->quote($filtro['descripcion']); + } + if(isset($filtro['descripcion_comportamiento'])) { + $where .= " AND fa.descripcion_comportamiento = " . $this->quote($filtro['descripcion_comportamiento']); + } + if(isset($filtro['estado'])) { + $where .= " AND fa.estado = " . $this->quote($filtro['estado']); + } + + $sql = "SELECT + fa.id, + fa.codigo, + fa.descripcion, + fa.id_tipo_facturador_alarma, + fa.descripcion_comportamiento, + fa.estado, + fa.estado_actualizado_en, + CASE tfa.parametros + WHEN 'par_rango_inferior' THEN fap.par_rango_inferior + WHEN 'par_rango_superior' THEN fap.par_rango_superior + WHEN 'par_valor' THEN fap.par_valor + END parametro_valor, + row_to_json(tfa.*) tipos_facturador_alarmas, + json_agg(fap.*) facturador_alarmas_parametros + FROM + facturador_alarmas fa + LEFT JOIN tipos_facturador_alarmas tfa ON fa.id_tipo_facturador_alarma = tfa.id + LEFT JOIN facturador_alarmas_parametros fap ON fa.id = fap.id_facturador_alarma + WHERE 1=1 + $where + GROUP BY fa.id,fap.id,tfa.id + ORDER BY fa.id;"; + $filas = $this->entorno()->db()->consultar($sql); + //convierto los string json en arrays + $resultado = []; + foreach($filas as $f){ + $f['tipos_facturador_alarmas'] = $this->json_decode_caracteres_especiales($f['tipos_facturador_alarmas']); + $f['facturador_alarmas_parametros'] = $this->json_decode_caracteres_especiales($f['facturador_alarmas_parametros']); + $resultado[] = $f; + } + return $resultado; + } + + function obtener_alarma($id) + { + $rs = $this->obtener_alarmas(['id' => $id]); + if(count($rs) == 1){ + return $rs[0]; + }else{ + if(count($rs) > 1){ + throw new error_negocio("DAO_ALARMAS: " + . "La consulta devuelve mas de un registro."); + }else{ + return null; + } + } + } + + function obtener_alarmas_configuradas($filtro = []) + { + $where = ""; + if(isset($filtro['id'])) { + $where .= " AND id = " . $this->quote($filtro['id']); + } + if(isset($filtro['id_facturacion'])) { + $where .= " AND id_facturacion = " . $this->quote($filtro['id_facturacion']); + } + $sql = "SELECT + id, + id_facturacion, + id_facturador_alarma + FROM + facturaciones_alarmas_configuradas + WHERE 1=1 + $where"; + return $this->entorno()->db()->consultar($sql); + } + + function obtener_alarmas_generadas($filtro = []) + { + $where = ""; + if(isset($filtro['id'])) { + $where .= " AND fa.id = " . $this->quote($filtro['id_facturador_alarma']); + } + if(isset($filtro['id_facturacion'])) { + $where .= " AND sfag.id_facturacion = " . $this->quote($filtro['id_facturacion']); + } + //NOTA: la unica manera de anidar funciones de agregacion (json_agg()) es utilizando subqueries + $sql = "SELECT + fa.id, + fa.codigo, + fa.descripcion, + fa.id_tipo_facturador_alarma, + fa.descripcion_comportamiento, + fa.estado, + fa.estado_actualizado_en, + CASE WHEN COUNT(sfag.*) > 0 THEN json_agg(sfag.*) ELSE '[]' END facturaciones_alarmas_generadas + FROM + facturador_alarmas fa + LEFT JOIN ( + SELECT + fag.id, + fag.id_facturacion, + fag.id_facturador_alarma, + json_agg(fagv.*) facturaciones_alarmas_generadas_ventas + FROM + facturaciones_alarmas_generadas fag + JOIN facturaciones_alarmas_generadas_ventas fagv ON fag.id = fagv.id_facturacion_alarma_generada + GROUP BY fag.id + ) sfag ON fa.id = sfag.id_facturador_alarma + WHERE 1=1 + $where + GROUP BY fa.id"; + $filas = $this->entorno()->db()->consultar($sql); + //convierto los string json en arrays + $resultado = []; + foreach($filas as $f){ + $f['facturaciones_alarmas_generadas'] = $this->json_decode_caracteres_especiales($f['facturaciones_alarmas_generadas']); + $resultado[] = $f; + } + return $resultado; + } + + function obtener_alarmas_parametros($filtro = array()) + { + $where = ""; + if(isset($filtro['id'])) { + $where .= " AND id = " . $this->quote($filtro['id']); + } + if(isset($filtro['estado'])) { + $where .= " AND estado = " . $this->quote($filtro['estado']); + } + $sql = "SELECT + id, + codigo, + descripcion, + parametros, + estado, + estado_actualizado_en + FROM + tipos_facturador_alarmas + WHERE 1=1 + $where"; + return $this->entorno()->db()->consultar($sql); + } +} diff --git a/src/siu/sq/lib/daos/dao_reloj_facturador.php b/src/siu/sq/lib/daos/dao_reloj_facturador.php new file mode 100644 index 0000000..b51b81d --- /dev/null +++ b/src/siu/sq/lib/daos/dao_reloj_facturador.php @@ -0,0 +1,256 @@ +quote($filtro['id']); + } + if(isset($filtro['id_reloj_facturador_estado'])){ + if(is_array($filtro['id_reloj_facturador_estado'])){ + $where .= " AND rfe.id IN (" . implode(',', $this->quote($filtro['id_reloj_facturador_estado'])) . ")"; + }else { + $where .= " AND rfe.id = " . $this->quote($filtro['id_reloj_facturador_estado']); + } + } + if(isset($filtro['id_mes'])){ + $where .= " AND rf.id_mes = " . $filtro['id_mes']; + } + if(isset($filtro['id_anio'])){ + $where .= " AND rf.id_anio = " . $filtro['id_anio']; + } + if(isset($filtro['id_desde'])){ + $where .= " AND rf.id >= " . $this->quote($filtro['id_desde']); + } + $sql = "SELECT + rf.id, + rf.codigo, + rf.descripcion, + rf.id_anio, + rf.id_mes, + rf.dia_inicio_facturacion, + rf.dia_limite_facturacion, + rf.id_reloj_facturador_estado, + rf.facturador_ejecutado_en, + rf.creado_por, + rf.creado_en, + rf.modificado_por, + rf.modificado_en, + rfe.descripcion estado, + a.descripcion anio, + m.descripcion mes + FROM + reloj_facturador rf, + reloj_facturador_estados rfe, + anios a, + meses m + WHERE + rf.id_reloj_facturador_estado = rfe.id + AND rf.id_anio = a.id + AND rf.id_mes = m.id + $where ORDER BY rf.id ASC;"; + return $this->entorno()->db()->consultar($sql); + } + + function obtener_reloj($filtro) + { + $reloj = $this->obtener_relojes($filtro); + if(!empty($reloj)){ + if(count($reloj) == 1){ + return $reloj[0]; + }else{ + throw new error_negocio("DAO_RELOJ_FACTURACION: Se ha encontrado mas de un reloj para la consulta."); + } + } + } + + /** + * + * @param integer $limit + * @return array periodos de facturacion futuros segun la fecha actual + * NOTA: si los periodos de facturacion no son mensuales esto deberia modificarse + */ + function obtener_siguientes_periodos_de_facturacion($limit) + { + $sql = "SELECT + rf.id, + rf.codigo, + rf.descripcion, + rf.id_anio, + rf.id_mes, + rf.dia_inicio_facturacion, + rf.dia_limite_facturacion, + rf.id_reloj_facturador_estado, + rf.facturador_ejecutado_en, + rf.creado_por, + rf.creado_en, + rf.modificado_por, + rf.modificado_en + FROM + reloj_facturador rf + JOIN anios a ON rf.id_anio = a.id + JOIN meses m ON rf.id_mes = m.id + WHERE + a.anio > (SELECT EXTRACT(YEAR FROM CURRENT_DATE)) + OR (a.anio = (SELECT EXTRACT(YEAR FROM CURRENT_DATE)) + AND m.numero_mes > (SELECT EXTRACT(MONTH FROM CURRENT_DATE))) + ORDER BY a.anio, m.numero_mes LIMIT $limit"; + + return $this->entorno()->db()->consultar($sql); + } + + function obtener_cantidad_de_relojes_posteriores_al_actual() + { + $filtro['id_reloj_facturador_estado'] = constantes::RELOJ_FACTURACION_ESTADO_ACTUAL; + $reloj_actual = $this->obtener_reloj($filtro); + + $sql = "SELECT + count(*) + FROM + reloj_facturador rf + WHERE + (id_anio = " . $reloj_actual['id_anio'] . " + AND id_mes > " . $reloj_actual['id_mes'] . ") + OR (id_anio > " . $reloj_actual['id_anio'] . ");"; + return $this->entorno()->db()->consultar($sql)[0]['count']; + } + + function obtener_ultimo_reloj() + { + $sql = "SELECT + rf.id, + rf.codigo, + rf.descripcion, + rf.id_anio, + rf.id_mes, + rf.dia_inicio_facturacion, + rf.dia_limite_facturacion, + rf.id_reloj_facturador_estado, + rf.facturador_ejecutado_en, + rf.creado_por, + rf.creado_en, + rf.modificado_por, + rf.modificado_en, + a.anio numero_anio, + m.numero_mes + FROM + reloj_facturador rf + JOIN anios a ON rf.id_anio = a.id + JOIN meses m ON rf.id_mes = m.id + WHERE + rf.id_anio = (SELECT max(id_anio) + FROM reloj_facturador) + AND rf.id_mes = (SELECT max(id_mes) + FROM reloj_facturador + WHERE id_anio = (SELECT max(id_anio) + FROM reloj_facturador));"; + return $this->entorno()->db()->consultar($sql)[0]; + } + + function obtener_anio($filtro) + { + $where = ""; + if(isset($filtro['numero_anio'])){ + $where .= " AND anio = " . $this->quote($filtro['numero_anio']); + } + if(isset($filtro['id'])){ + $where .= " AND id = " . $this->quote($filtro['id']); + } + $sql = "SELECT + id, + codigo, + descripcion, + anio + FROM + anios + WHERE 1=1 + $where"; + $r = $this->entorno()->db()->consultar($sql); + return !empty($r) ? $r[0] : NULL; + } + + function obtener_mes($filtro) + { + $where = ""; + if(isset($filtro['numero_mes'])){ + $where .= " AND numero_mes = " . $this->quote($filtro['numero_mes']); + } + if(isset($filtro['id'])){ + $where .= " AND id = " . $this->quote($filtro['id']); + } + $sql = "SELECT + id, + codigo, + descripcion, + numero_mes + FROM + meses + WHERE 1=1 + $where"; + $r = $this->entorno()->db()->consultar($sql); + return !empty($r) ? $r[0] : NULL; + } + + function get_reloj_facturador_estados($filtro = array(),$order_by = "ORDER BY codigo",$limit = "") + { + $where = ""; + if(isset($filtro['id'])){ + $where .= " AND id = " . $this->quote($filtro['id']); + } + if(isset($filtro['codigo'])){ + $where .= " AND codigo = " . $this->quote($filtro['codigo']); + } + if(isset($filtro['descripcion'])){ + $where .= " AND descripcion = " . $filtro['descripcion']; + } + if(isset($filtro['es_final'])){ + $where .= " AND es_final = " . $filtro['es_final']; + } + + $sql = "SELECT + id, + codigo, + descripcion, + es_final + FROM + reloj_facturador_estados + WHERE 1=1 + $where $order_by $limit"; + return $this->entorno()->db()->consultar($sql); + } + + function get_feriados($filtro = array()) + { + $where = ""; + + if(isset($filtro['1er_vencimiento'])){ + if(is_array($filtro['1er_vencimiento'])){ + $where .= " OR mes = " . $this->quote($filtro['1er_vencimiento']['mes']); + $where .= " AND anio = " . $this->quote($filtro['1er_vencimiento']['anio']); + + } + } + + if(isset($filtro['2do_vencimiento'])){ + if(is_array($filtro['2do_vencimiento'])){ + $where .= " OR mes = " . $this->quote($filtro['2do_vencimiento']['mes']); + $where .= " AND anio = " . $this->quote($filtro['2do_vencimiento']['anio']); + + } + } + + $sql = "SELECT dia + FROM feriados + WHERE 1=1 + $where"; + + return $this->entorno()->db()->consultar($sql); + } +} diff --git a/src/siu/sq/lib/modelo/catalogo.php b/src/siu/sq/lib/modelo/catalogo.php index 32e038a..c2e22c2 100644 --- a/src/siu/sq/lib/modelo/catalogo.php +++ b/src/siu/sq/lib/modelo/catalogo.php @@ -18,4 +18,24 @@ class catalogo extends \SIU\SQ\Lib\entorno\catalogo { return new parametro_sistema($this->entorno, $id); } + + function facturador() + { + return new facturador($this->entorno); + } + + function facturador_alarma($id = null) + { + return new facturador_alarma($this->entorno, $id); + } + + function reloj($id_reloj = null, $id_periodo_fact_excepciones = null) + { + return new reloj($this->entorno, $id_reloj, $id_periodo_fact_excepciones); + } + + function reloj_instancia($id_servicio_instancia) + { + return new reloj_instancia($this->entorno, $id_servicio_instancia); + } } diff --git a/src/siu/sq/lib/modelo/facturador.php b/src/siu/sq/lib/modelo/facturador.php new file mode 100644 index 0000000..6c4ae4a --- /dev/null +++ b/src/siu/sq/lib/modelo/facturador.php @@ -0,0 +1,918 @@ +reloj_facturador = lib::catalogo_modelo()->reloj(); + $this->id_reloj = $this->reloj_facturador->get_actual(); + $this->cantidad_ventas_pre_generadas = 0; + $this->importe_ventas_pre_generadas = 0; + $this->cantidad_ventas_generadas = 0; + $this->importe_ventas_generadas = 0; + $this->cantidad_ventas_cobradas = 0; + $this->importe_ventas_cobradas = 0; + $this->id_facturacion = null; + $this->ids_facturador_alarmas = []; + //Nota: cuando se crea una venta, el campo ventas.cobranza_vence_en se setea con la fecha + // del proceso + el valor del parametros_sistema("DIAS_1er_V"). VER SI ES FERIADO???? + // + // SI el parametro '"APLIC_MORA"' esta en 'S' + // $this->calculadora_de_mora = new calculadora_de_mora(); + } + + public function set_proceso($proceso) + { + $this->proceso = $proceso; + } + + protected function validar() + { + if(empty($this->ids_servicios_instancias)){ + throw new error_negocio("No se especificaron servicios para facturar."); + } + } + + public function guardar() + { + throw new error_negocio("FACTURADOR: Utilizar el metodo 'Generar' o 'Confirmar' para completar una acción de facturación."); + } + + + public function generar() + { + $this->validar(); + $this->generar_cabecera_facturacion(); + $this->guardar_instancias_a_facturar(); + if(!empty($this->ids_facturador_alarmas)){ + $this->guardar_facturaciones_alarmas_configuradas(); + } + try { + $this->programar_cuotas_continuas(); + $this->procesar_ventas_vencidas(); + $this->generar_ventas_por_suscripciones(); + $this->procesar_ventas_por_suscripcion_y_cliente(); + $this->contar_ventas_pregeneradas(); + } catch (error_negocio $e) { + lib::entorno()->log()->error("Facturador: Error al generar la facturacion: " . $e->getMessage()); + $this->revertir(); + throw $e; + } + } + + function contar_ventas_pregeneradas() + { + // Obtener las ventas en estado VENTA_ESTADO_PRE_FACT + $filtro['id_venta_estado'] = constantes::VENTA_ESTADO_PRE_FACT; + $filtro['id_periodo_facturacion'] = $this->id_reloj; + $ventas_prefacturadas = sq_uv::catalogo_daos()->ventas()->get_ventas($filtro); + $ids_ventas_agrupadas_en_sumarizada = array_column(sq_uv::catalogo_daos()->ventas()->get_ventas_sumarizadas(),'id_venta_sumarizada'); + foreach($ventas_prefacturadas as $venta_prefact) + { + // Verificar si la venta no es parte de una sumarizada + if(!in_array($venta_prefact['id'], $ids_ventas_agrupadas_en_sumarizada)){ + $this->cantidad_ventas_pre_generadas++; + $this->importe_ventas_pre_generadas += $venta_prefact['importe_total']; + } + } + } + + public function confirmar() + { + try { + $this->cambiar_estado_ventas(); + // Si no queda ninguna instancia para facturar en el id de reloj actual, avanzo el facturador + $datos = sq_uv::catalogo_daos()->servicios()->get_servicios_instancias_no_facturados($this->id_reloj); + if(empty($datos)) { + $this->reloj_facturador->avanzar(); + } + $this->eliminar_log_reversion(); + } catch (error_negocio $e) { + $this->revertir_confirmacion(); + throw $e; + } + } + + protected function revertir_confirmacion() + { + ei_arbol(array(), 'FACTURADOR: REVERTIR CONFIRMACION: NO IMPLEMENTADO!'); + } + + function get_id_reloj() + { + return $this->id_reloj; + } + + public function get_cantidad_ventas_pre_generadas() + { + return $this->cantidad_ventas_pre_generadas; + } + + public function get_importe_ventas_pre_generadas() + { + return $this->importe_ventas_pre_generadas; + } + + public function get_cantidad_ventas_generadas() + { + return $this->cantidad_ventas_generadas; + } + + public function get_ventas_generadas() + { + return $this->ventas_generadas; + } + + public function get_importe_ventas_generadas() + { + return $this->importe_ventas_generadas; + } + + public function get_cantidad_ventas_cobradas() + { + return $this->cantidad_ventas_cobradas; + } + + public function get_importe_ventas_cobradas() + { + return $this->importe_ventas_cobradas; + } + + public function set_dia_primer_vencimiento($id_reloj_1er_vencimiento, $dia_1er_vencimiento) + { + $this->dias_vencimiento['dia_1er_vencimiento'] = $dia_1er_vencimiento; + $this->dias_vencimiento['id_reloj_1er_vencimiento'] = $id_reloj_1er_vencimiento; + } + + public function set_dia_segundo_vencimiento($id_reloj_2do_vencimiento, $dia_2do_vencimiento) + { + $this->dias_vencimiento['dia_2do_vencimiento'] = $dia_2do_vencimiento; + $this->dias_vencimiento['id_reloj_2do_vencimiento'] = $id_reloj_2do_vencimiento; + } + + public function set_instancias_a_facturar($ids_servicios_instancias) + { + $this->ids_servicios_instancias = $ids_servicios_instancias; + } + +// Obsoleto, Borrar?????? + public function set_id_facturacion($id_facturacion) + { + $this->id_facturacion = $id_facturacion; + } + + public function get_id_facturacion() + { + return $this->id_facturacion; + } + + public function set_ids_facturador_alarmas(array $ids_facturador_alarmas) + { + $this->ids_facturador_alarmas = $ids_facturador_alarmas; + } + + public function revertir() + { + $this->revertir_facturacion(); + + $this->eliminar_facturaciones_alarmas_configuradas(); + $this->eliminar_facturaciones_alarmas_generadas(); + $this->eliminar_instancias_a_facturar(); + $this->eliminar_cabecera_facturacion(); + + } + + //----------------------------------------------------------- + // GENERACION + //----------------------------------------------------------- + + protected function generar_cabecera_facturacion() + { + if(!isset($this->dias_vencimiento['dia_1er_vencimiento']) AND !isset($this->dias_vencimiento['id_reloj_1er_vencimiento'])){ + throw new error_negocio("No se definieron las fechas de vencimiento para el facturador."); + } + if(lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_VENCIMIENTOS) == constantes::SEGUNDO_VENCIMIENTO) { + if(!isset($this->dias_vencimiento['dia_2do_vencimiento']) AND !isset($this->dias_vencimiento['id_reloj_2do_vencimiento'])){ + throw new error_negocio("No se definieron las fechas de vencimiento para el facturador."); + } + $datos['id_reloj_facturador_2do_venc'] = $this->dias_vencimiento['id_reloj_2do_vencimiento']; + $datos['dia_2do_vencimiento'] = $this->dias_vencimiento['dia_2do_vencimiento']; + } + $datos['id_ejecucion_proceso'] = $this->proceso->get_id_ejecucion(); + $datos['id_reloj_facturador_1er_venc'] = $this->dias_vencimiento['id_reloj_1er_vencimiento']; + $datos['dia_1er_vencimiento'] = $this->dias_vencimiento['dia_1er_vencimiento']; + $datos['id_reloj_facturador'] = lib::catalogo_modelo()->reloj()->get_actual(); + $sql = sql::sql_array_a_insert("facturaciones", $datos, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + // Obtener el id de facturación + $this->id_facturacion = $this->entorno()->db()->ultimo_insert_id("facturaciones_id_seq"); + } + + protected function guardar_instancias_a_facturar() + { + $sql = null; + foreach($this->ids_servicios_instancias as $instancia){ + $datos['id_servicio_instancia'] = $instancia; + $datos['id_facturacion'] = $this->id_facturacion; + $sql .= sql::sql_array_a_insert("facturaciones_servicios_instancias", $datos, $this->entorno()->db()); + } + if($sql) { + $this->entorno()->db()->ejecutar($sql); + }else{ + throw new error_negocio("FACTURADOR: No se encontraron servicios instancias para procesar."); + } + } + + protected function guardar_facturaciones_alarmas_configuradas() + { + $sql = null; + foreach($this->ids_facturador_alarmas as $id){ + $datos['id_facturador_alarma'] = $id; + $datos['id_facturacion'] = $this->id_facturacion; + $sql .= sql::sql_array_a_insert("facturaciones_alarmas_configuradas", $datos, $this->entorno()->db()); + } + if($sql){ + $this->entorno()->db()->ejecutar($sql); + } + } + + protected function programar_cuotas_continuas() + { + /* + * Iterar suscripciones ACTIVAS + * SI $this->id_periodo_facturacion actual no foma parte de una excepcion de la instancia + * SI el programa de venta seleccionado por el cliente, tiene un articulo con frecuencia SIEMPRE, + * crear registo en "Servicios prestaciones pre programadas" + * - con id_periodo_factuacion = $this->id_periodo_facturacion + * - marcar como creado_por_facturador en TRUE + */ + //-- Obtener las suscripciones ACTIVAS, FUERA PERIODO EXCEPCION, FRECUENCIA SIEMPRE, + $filtro['ids_servicios_instancias'] = $this->ids_servicios_instancias; + $suscripciones = sq_uv::catalogo_daos()->servicios()->get_suscripciones_cuotas_continuas($filtro); + foreach($suscripciones as $suscripcion){ + $suscripcion_obj = sq_uv::catalogo_modelo()->suscripcion($suscripcion['id']); + $suscripcion_obj->set_creado_por_facturador(TRUE); + $prog_venta = sq_uv::catalogo_modelo()->programacion_venta()->get_detalle( $suscripcion['id_servicio_instancia_prog_venta'] ); + if(isset($prog_venta['detalle']) AND count($prog_venta['detalle']) == 1){ + $suscripcion_obj->crear_pre_programacion_venta_continua( $this->id_reloj, $prog_venta['detalle'][0] ); + }else{ + // No debería traer mas de uno, sino definir procedimiento. + throw new error_negocio("FACTURADOR: Se ha encontrado mas de un articulo para el programa de venta."); + } + } + $this->proceso->agregar_notificacion("Cantidad de pre-programaciones de cuotas continuas generadas:" . count($suscripciones), 'info'); + } + + protected function procesar_ventas_vencidas() + { + /** + * Iterar todas las ventas en estado VENCIDAS + * - generar una nueva venta clonada de la anterior (copiar articulos y convenios) + * - en la nueva venta: + * - marcar el campo id_venta_origen con una referencia a la vencida. + * - determina la nueva fecha de vencimiento en base a la fecha del proceso y se valor sumarle el valor del parametro "DIAS_1er_V" + * - marcar id_periodo_factuacion = $this->id_periodo_facturacion + * - Crear un comprobante con dos filas: Importe Adeudado=$$ y Mora=$$ + * - estado_venta: PREFACTURACION + * - Si param_sistema('APLIC_MORA') ]= 'S + * - Calculo de MORA (Segun configuracion): + * - "CONSERVAR_MAPEO_ARTICULOS". + * Iterar articulos: + * - crear un articulo de tipo MORA por cada uno. + * - calcular el $ segun el porcentaje establecido para la de mora. + * - Al articulo MORA ponerle en el campo id_articulo_copia_mapeo_presupuestario el articulo original + * - "MAPEO_MORA" + * - crear un unico articulo MORA calculando el $ en base al porcentaje + * Fin Si + * - pasar la venta original al estado ANULADA + */ + //-- Iterar todas las ventas en estado VENCIDAS de las INSTANCIAS seleccionadas + $filtro['id_venta_estado'] = constantes::VENTA_VENCIDA; + $filtro['ids_servicios_instancias'] = $this->ids_servicios_instancias; + $ventas_vencidas = sq_uv::catalogo_daos()->ventas()->get_ventas($filtro,null,'ORDER BY v.id','id_venta_origen'); + if(!empty($ventas_vencidas)){ + $ventas_vencidas = $this->filtrar_ventas_que_ya_fueron_revividas_n_veces($ventas_vencidas); + } + $this->proceso->agregar_notificacion("Cantidad de ventas vencidas a procesar:" . count($ventas_vencidas), 'info'); + foreach($ventas_vencidas as $venta_vencida) + { + // Solo procesar las ventas vencidas que pertenezcan los servicios instancias a facturar + $servicio_instancia = sq_uv::catalogo_daos()->servicios()->get_servicio_instancia_por_venta($venta_vencida['id']); + if(!empty($servicio_instancia)) { + if (!in_array($servicio_instancia['id_servicio_instancia'], $this->ids_servicios_instancias)) { + // Si el servicio instancia asociado a la venta no se encuentra entre los servicios a facturar saltar a la proxima venta + continue; + } + }else{ + $msg = "Facturador: La venta con el ID. '" . $venta_vencida['id'] . "' no posee una prestacion preprogramada"; + lib::entorno()->log()->error($msg . var_export($servicio_instancia, true)); + throw new error_negocio($msg); + } + //-- generar una nueva venta clonada de la anterior (copiar articulos y convenios) + $venta_clonada = sq_uv::catalogo_modelo()->modelador_venta()->clonar($venta_vencida['id']); + //-- marcar el campo id_venta_origen con una referencia a la vencida. + $venta_clonada->set_id_venta_origen($venta_vencida['id']); + $id_tipo_vencimiento = sq_uv::catalogo_daos()->ventas()->get_tipo_vencimiento($venta_vencida['id']); + $venta_clonada->set_primer_vencimiento($id_tipo_vencimiento, $this->dias_vencimiento['id_reloj_1er_vencimiento'], $this->dias_vencimiento['dia_1er_vencimiento']); + if(lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_VENCIMIENTOS) == constantes::SEGUNDO_VENCIMIENTO) { + $venta_clonada->set_segundo_vencimiento($this->dias_vencimiento['id_reloj_2do_vencimiento'], $this->dias_vencimiento['dia_2do_vencimiento']); + } + //-- marcar id_periodo_factuacion = $this->id_periodo_facturacion + $venta_clonada->set_id_periodo_facturacion( $this->id_reloj ); + +//TODO: -- Crear un comprobante con dos filas: Importe Adeudado=$$ y Mora=$$ +//TODO: -- Si param_sistema('APLIC_MORA') ]= 'S + + // Iniciar la venta a agrupar en TRUE + $venta_clonada->set_a_agrupar(TRUE); + //-- estado_venta: PREFACTURACION + $venta_clonada->cambiar_estado(constantes::VENTA_ESTADO_PRE_FACT,true); + // Guardar la nueva venta + $venta_clonada->guardar(); + + //-- Reasignar el id de la nueva venta a la prestacion pre-programada que estaba asociada la venta origen + $prestacion_preprog_datos = sq_uv::catalogo_daos()->servicios()->get_prestaciones_programadas(['id_venta' => $venta_vencida['id']]); + if(empty($prestacion_preprog_datos)){ + throw new error_negocio("FACTURADOR: No se pudo obtener la prestacion pre-programada asociada a la venta vencida: '" . $venta_vencida['id'] . "'"); + }else{ + if(count($prestacion_preprog_datos) > 1){ + throw new error_negocio("FACTURADOR: Se obtuvo mas de una prestacion pre-programada asociada a la venta vencida: '" . $venta_vencida['id'] . "'"); + } + } + // Generar cambios para una reversion de facturación + $this->registrar_punto_reversion($prestacion_preprog_datos[0]['id'], $venta_vencida['id'], constantes::VENTA_VENCIDA); + + $prestacion_preprogramada = sq_uv::catalogo_modelo()->prestacion_preprogramada($prestacion_preprog_datos[0]['id']); + $prestacion_preprogramada->set_id_venta($venta_clonada->get_id()); + $prestacion_preprogramada->guardar(); + + //-- pasar la venta original al estado ANULADA + $this->anular_venta($venta_vencida['id']); + } + } + + private function anular_venta($id) + { + $venta_origen = sq_uv::catalogo_modelo()->modelador_venta()->cargar($id); + $venta_origen->cambiar_estado(constantes::VENTA_ESTADO_ANULADA,true); + $venta_origen->guardar(); + } + + /** + * Saca del array que recibe las ventas que ya no debe revivir y las anula + * @param array $ventas_vencidas + * @return array $ventas_vencidas que si deben ser revividas + */ + private function filtrar_ventas_que_ya_fueron_revividas_n_veces(array $ventas_vencidas) + { + $ids_ventas_vencidas = array_column($ventas_vencidas, 'id'); + $ids_resultado = []; + $resultado = []; + $max_nro_ciclos_suscripcion_activa = (int)lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_CICLOS_REVIVIR_VENTAS_SUSCRIPCION_ACTIVA); + $max_nro_ciclos_suscripcion_inactiva = (int)lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_CICLOS_REVIVIR_VENTAS_SUSCRIPCION_INACTIVA); + $dao_ventas = sq_uv::catalogo_daos()->ventas(); + // Recupero los estados de las suscripciones + $filtro = ['id_venta' => $ids_ventas_vencidas]; + $prestaciones = sq_uv::catalogo_daos()->servicios()->get_prestaciones_programadas($filtro); + foreach($prestaciones as $p){ + switch ($p['estado']) {//estado, es el estado de la suscripcion + case constantes::SERVICIOS_SUSCRIPCIONES_ESTADO_ACTIVO: + if($dao_ventas->get_nro_ciclos_facturador($p['id_venta']) < $max_nro_ciclos_suscripcion_activa){ + $ids_resultado[] = $p['id_venta']; + }else{ + //anulo las ventas que ya no se deben revivir + $this->anular_venta($p['id_venta']); + } + break; + case constantes::SERVICIOS_SUSCRIPCIONES_ESTADO_INACTIVO: + if($dao_ventas->get_nro_ciclos_facturador($p['id_venta']) < $max_nro_ciclos_suscripcion_inactiva){ + $ids_resultado[] = $p['id_venta']; + }else{ + //anulo las ventas que ya no se deben revivir + $this->anular_venta($p['id_venta']); + } + break; + } + } + + foreach($ids_resultado as $id){ + foreach($ventas_vencidas as $vv){ + if($id === $vv['id']){ + $resultado[] = $vv; + } + } + } + return $resultado; + } + + protected function generar_ventas_por_suscripciones() + { + /* Consultar suscripciones activas, trayendo data del servicio y del servicio_instancia + * Iterar suscripciones ACTIVAS + * 1) Iterar prestaciones PREPROGRAMADAS con id_venta == NULL & id_periodo_facturacion = ACTUAL + * - Invocar la factory de ventas con los parametros: + * - a_agrupar = true, + * - id_periodo_facturacion = $this->id_periodo_facturacion + * - estado_venta: PREFACTURACION + * - Crear articulos programados (registrar en ventas_articulos) y poner en cada fila el ID del servicio que lo genero + * - Guardar venta y dejar el ID generado en el registro de la prestacion PREPROGRAMADA que lo genero +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +TODO:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * 2) Iterar prestaciones por EVENTO con id_venta == NULL & id_periodo_facturacion = ACTUAL + * - Invocar la factory de ventas con los parametros: + * - a_agrupar = true, + * - id_periodo_facturacion = $this->id_periodo_facturacion + * - estado_venta: PREFACTURACION + * - Crear articulos programados (registrar en ventas_articulos) y poner en la fila el ID del servicio que lo genero + * - Guardar venta y dejar el ID generado en la prestacion por EVENTO que lo genero + **/ + //-- Iterar suscripciones ACTIVAS + $filtro['estado'] = constantes::SUSCRIPCION_ESTADO_ACTIVO; + $filtro['ids_servicios_instancias'] = $this->ids_servicios_instancias; + $suscripciones = sq_uv::catalogo_daos()->servicios()->get_suscripciones( $filtro ); + $ventas_generadas = []; + foreach(array_keys($suscripciones) as $id){ + // Cargar la suscripcion + $suscripcion = sq_uv::catalogo_modelo()->suscripcion( $suscripciones[$id]['id'] ); + //-- Iterar prestaciones PREPROGRAMADAS con id_venta == NULL & id_periodo_facturacion = ACTUAL + $prestaciones_preprogramadas = $suscripcion->get_prestaciones(); + foreach($prestaciones_preprogramadas as $prestacion){ + if($prestacion->get_id_venta() == NULL AND $prestacion->get_id_reloj_facturador() == $this->id_reloj){ + // obtener el convenio de cofinanciacion si existe + $id_convenio_de_cofinanciacion = $suscripcion->get_convenio_de_cofinanciacion(); + // si es cofinanciacion + if( $id_convenio_de_cofinanciacion ){ + $venta = sq_uv::catalogo_modelo()->modelador_venta()->nueva_convenio( $id_convenio_de_cofinanciacion ); + }else{ + $venta = sq_uv::catalogo_modelo()->modelador_venta()->nueva_convenio(); + } + + $venta->set_cliente($suscripciones[$id]['id_cliente']); + $venta->set_medio_pago($suscripciones[$id]['id_medio_pago']); + $venta->set_condicion_venta($suscripciones[$id]['id_condicion_venta']); +//TODO: modificar cuando se agregue el campo canal_venta en la suscripcion. + $venta->set_canal_venta( constantes::CANAL_VENTA_VITUAL ); + $venta->set_leyenda( $prestacion->get_leyenda() ); + + + $venta->set_a_agrupar(TRUE); + $venta->set_id_periodo_facturacion( $this->id_reloj ); + $venta->cambiar_estado( constantes::VENTA_ESTADO_PRE_FACT ); + + // Obtener los articulos de la prestacion preprogramada + $id_servicio_instancia_prog_venta_articulo = $prestacion->get_id_servicio_instancia_prog_venta_articulo(); + // Inicializar el array de articulos + $articulos = array(); + // A futuro la consulta deber�a obtener un array de articulos, hoy solo trae uno + //TODO: CHEQUEAR: la consulta devuelve un registro por valorizacion, es decir que + //si el servicios_instancias_prog_ventas_articulos tiene 2 valorizaciones + //(2 registros en servicios_instancias_prog_ventas_articulos_valorizaciones) + //entonces la consulta retorna 2 filas del mismo articulo, c/u con una valorizacion + //es decir que si hay mas de una valorizacion el $importe_total de la venta generada calculado mas abajo sera incorrecto + $articulos[] = sq_uv::catalogo_daos()->servicios()->get_articulo_por_id_serv_prog_venta_art($id_servicio_instancia_prog_venta_articulo); + if( empty($articulos) ){ + throw new error_negocio("FACTURADOR: GENERAR_VENTAS_POR_SUSCRIPCIONES: No se pudieron obtener los articulos por el programa de venta: " . $suscripcion->get_id_servicio_instancia_prog_venta()); + } + // obtener los convenios para asociar a los articulos + $convenios = $suscripcion->get_convenios(); + $importe_total = 0; + foreach(array_keys($articulos) as $id_a){ + $importe_total += $articulos[$id_a]['importe_unitario']; + $articulos[$id_a]['cantidad'] = 1; + // Asociar los convenios menos los de cofinanciacion. + foreach(array_keys($convenios) as $id_c){ + if( !$convenios[$id_c]['es_cofinanciacion'] ){ + $articulos[$id_a]['convenios'][] = array( 'id' => $convenios[$id_c]['id_convenio']); + } + } + } + $venta->set_importe_total($importe_total); + $venta->agregar_articulos($articulos); + // Setear los dias de vencimientos + $id_tipo_vencimiento = sq_uv::catalogo_daos()->servicios()->get_tipo_vencimiento_servicio_instancia($suscripciones[$id]['id_servicio_instancia']); + $venta->set_primer_vencimiento($id_tipo_vencimiento, $this->dias_vencimiento['id_reloj_1er_vencimiento'], $this->dias_vencimiento['dia_1er_vencimiento']); + if(lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_VENCIMIENTOS) == constantes::SEGUNDO_VENCIMIENTO) { + $venta->set_segundo_vencimiento($this->dias_vencimiento['id_reloj_2do_vencimiento'], $this->dias_vencimiento['dia_2do_vencimiento']); + } + //-- Guardar venta + $venta->guardar(); + + // Generar cambios para una reversion de facturación + $this->registrar_punto_reversion($prestacion->get_id()); + + //-- Dejar el ID generado en el registro de la prestacion PREPROGRAMADA que lo genero + $prestacion->set_id_venta( $venta->get_id() ); + $prestacion->guardar(); + + $ventas_generadas[] = $venta; + } + } + } + $this->proceso->agregar_notificacion("Cantidad de ventas generadas en estado de prefacturación:" . count($ventas_generadas), 'info'); + if(!empty($ventas_generadas)){ + //generacion de alarma de facturacion + $this->evaluar_generacion_de_alarmas($ventas_generadas); + } + } + + /** + * + * @param venta[] $ventas + */ + function evaluar_generacion_de_alarmas(array $ventas) + { + foreach ($this->ids_facturador_alarmas as $id){ + $facturador_alarma = lib::catalogo_modelo()->facturador_alarma($id); + foreach ($ventas as $venta){ + $facturador_alarma->evaluar_generacion($this->id_facturacion,$venta); + } + } + } + + protected function procesar_ventas_por_suscripcion_y_cliente() + { + /* + * Hacer una consulta de las ventas del id_periodo_facturacion == ACTUAL con estado en PREFACTURACION, + * agrupadas por suscripcion y cliente, con un COUNT de la cantidad de ventas incluidas + * - si tiene 1 venta: + * - Pasar el flag "a_agrupar" a false. + * - si tiene mas de 1 venta: + * * Crear una VENTA SUMARIZADA y un comprobante de presentacion asociado + * * Iterar las ventas (si la venta es cofinanciada hay que evitarla) + * - copiar los articulos y sus convenios + * - si la venta se apoya en una vencida (id_venta_referencia), + * no agregar articulos al comprobante poner frase del estilo "DEUDA ANTERIOR" y total + * - si es una venta basica, agregar los articulos al comprobante + * * Aplicar descuentos especiales (EJ: dos postgrados) + * - como? -> falta METADATA de que articulos participan de la regla + * * Guardar la venta. + * Si parametros_sistema('DESC_CRED') = 'S' + * * Reclamar credito si existe + * - si el saldo del credito del cliente es mayor a cero + * - tomar la cantidad disponible hasta el total de la deuda y guardarlo en la tabla de debitos + * Fin Si + */ + //-- Hacer una consulta de las ventas del id_periodo_facturacion == ACTUAL con estado en PREFACTURACION, + // agrupadas por cliente, con un COUNT de la cantidad de ventas incluidas + $filtro['id_venta_estado'] = constantes::VENTA_ESTADO_PRE_FACT; + $filtro['id_periodo_facturacion'] = $this->id_reloj; + $filtro['id_tipo_venta'] = constantes::TIPO_VENTA_BASICA; + $cantidad_ventas_por_suscripcion_cliente = sq_uv::catalogo_daos()->ventas()->get_cantidad_ventas_por_suscripcion_y_cliente($filtro); + $cantidad_ventas_sumarizadas = 0; + $ventas_sumarizadas_generadas = []; + foreach(array_keys($cantidad_ventas_por_suscripcion_cliente) as $id){ + $id_cliente = $cantidad_ventas_por_suscripcion_cliente[$id]['id_cliente']; + // Cargar las ventas para el cliente actual + $ventas_por_cliente = sq_uv::catalogo_daos()->ventas()->get_ventas_por_suscripcion_y_cliente($id_cliente, $cantidad_ventas_por_suscripcion_cliente[$id]['id_servicio_suscripcion'], $filtro); + if($cantidad_ventas_por_suscripcion_cliente[$id]['cantidad'] == 1){ + //-- Pasar el flag "a_agrupar" a false. + $venta = sq_uv::catalogo_modelo()->modelador_venta()->cargar($ventas_por_cliente[0]['id']); + $venta->set_a_agrupar(FALSE); + $venta->guardar(); + + //-- Si el cliente es beneficiario de convenio al 100% cobrar aplicando el convenio + if(sq_uv::catalogo_daos()->clientes()->es_beneficiario_convenio_100_y_tiene_pago_unico_asociado($id_cliente)){ + // Aplico el convenio para que la cobranza se genere en cero + foreach(array_keys($ventas_por_cliente) as $id_vc){ + $ventas_por_cliente[$id_vc]['importe_total'] = 0; + } + $this->generar_cobranza($venta, $ventas_por_cliente, $id_cliente, false, constantes::MEDIO_PAGO_MERCADOPAGO); + } else { + //-- Si parametros_sistema('DESC_CRED') = 'S' + if (lib::parametros_sistema()->get_valor('DESC_CRED') == 'S') { + // Recalamar credito si existe + $this->reclamar_credito($venta, $ventas_por_cliente, $id_cliente, $venta->get_importe_total()); + } + } + }else{ + $leyenda = null; + $articulos_acumulados = array(); + $ids_ventas = array(); + foreach(array_keys($ventas_por_cliente) as $id_vc){ + // Guardar ids de ventas a sumarizar + $ids_ventas[] = $ventas_por_cliente[$id_vc]['id']; + // Cargar venta para obtener articulos y convenios + $venta_cargada = sq_uv::catalogo_modelo()->modelador_venta()->cargar($ventas_por_cliente[$id_vc]['id']); + // Acumular los articulos de las diferentes ventas + $articulos_acumulados = array_merge($articulos_acumulados, $venta_cargada->get_articulos()); + //-- si la venta se apoya en una vencida (id_venta_referencia), +// if( $venta_cargada->get_id_venta_origen() ){ + // TODO:-- no agregar articulos al comprobante poner frase del estilo "DEUDA ANTERIOR" y total +// }else{ + // TODO:-- si es una venta basica, agregar los articulos al comprobante +// } + $leyenda[] = $venta_cargada->get_leyenda(); + } + // Obtener el importe total + $importe_total = 0; + foreach(array_keys($articulos_acumulados) as $id_a){ + $importe_total += $articulos_acumulados[$id_a]['importe_total']; + } + //-- Crear una VENTA SUMARIZADA y un comprobante de presentacion asociado + $venta = sq_uv::catalogo_modelo()->modelador_venta()->nueva_sumarizada(); + $venta->set_leyenda( implode(' || ', $leyenda) ); + $venta->set_cliente($id_cliente); + $venta->set_ids_ventas_sumarizadas($ids_ventas); + $venta->set_importe_total($importe_total); + $venta->set_canal_venta( constantes::CANAL_VENTA_FACTURADOR ); + $venta->set_medio_pago( lib::parametros_sistema()->get_valor( constantes::PARAMETROS_SISTEMA_MEDIO_DE_PAGO_POR_DEFECTO_PARA_VENTA_SUMARIZADA ) ); + $venta->set_condicion_venta( constantes::CONDICION_VENTA_EFECTIVO ); + //-- copiar los articulos y sus convenios + $venta->agregar_articulos($articulos_acumulados); +//TODO: Aplicar descuentos especiales (EJ: dos postgrados) + $venta->cambiar_estado( constantes::VENTA_ESTADO_PRE_FACT ); + // Setear el periodo de facturacion actual + $venta->set_id_periodo_facturacion( $this->id_reloj ); + // Setear los dias de vencimientos + $venta->set_primer_vencimiento(null, $this->dias_vencimiento['id_reloj_1er_vencimiento'], $this->dias_vencimiento['dia_1er_vencimiento']); + if(lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_VENCIMIENTOS) == constantes::SEGUNDO_VENCIMIENTO) + { + $venta->set_segundo_vencimiento($this->dias_vencimiento['id_reloj_2do_vencimiento'], $this->dias_vencimiento['dia_2do_vencimiento']); + } + //-- Guardar la venta. + $venta->guardar(); + + //-- Si el cliente es beneficiario de convenio al 100% cobrar aplicando el convenio + if(sq_uv::catalogo_daos()->clientes()->es_beneficiario_convenio_100_y_tiene_pago_unico_asociado($id_cliente)){ + // Aplico el convenio para que la cobranza se genere en cero + foreach(array_keys($ventas_por_cliente) as $id_vc){ + $ventas_por_cliente[$id_vc]['importe_total'] = 0; + } + $this->generar_cobranza($venta, $ventas_por_cliente, $id_cliente, false, constantes::MEDIO_PAGO_MERCADOPAGO); + } else { + //-- Si parametros_sistema('DESC_CRED') = 'S' + if( lib::parametros_sistema()->get_valor('DESC_CRED') == 'S'){ + // Reclamar credito si existe + $this->reclamar_credito($venta, $ventas_por_cliente, $id_cliente, $importe_total); + } + } + // Actualizar 'id_venta' en la prestacion pre-programada por el 'id' de la sumarizada + $filtro_prestacion_pp = [ + 'id_servicio_suscripcion' => $cantidad_ventas_por_suscripcion_cliente[$id]['id_servicio_suscripcion'], + 'id_reloj_facturador' => $this->id_reloj + ]; + // Debería obtener solo una prestación preprogramada + $datos_prestacion_pp = sq_uv::catalogo_daos()->servicios()->get_prestaciones_programadas($filtro_prestacion_pp); + if(count($datos_prestacion_pp) > 1){ + $msg = "Se encontraron mas de una prestacion pre-programada para la suscripcion y el reloj."; + $this->entorno()->log()->err(__METHOD__ . ": " . var_export($filtro_prestacion_pp, true)); + throw new error_negocio("FACTURADOR: " . $msg . " Verifique el log para mas informacion."); + } + if(count($datos_prestacion_pp) == 1){ + $prestacion = sq_uv::catalogo_modelo()->prestacion_preprogramada($datos_prestacion_pp[0]['id']); + $prestacion->set_id_venta( $venta->get_id() ); + $prestacion->guardar(); + } + if(count($datos_prestacion_pp) == 0){ + // Obtener las prestaciones asociadas a la suscripcion + $filtro_prestacion_pp = [ + 'id_servicio_suscripcion' => $cantidad_ventas_por_suscripcion_cliente[$id]['id_servicio_suscripcion'], + 'ventas_no_cobradas' => true + ]; + $datos_prestacion_pp = sq_uv::catalogo_daos()->servicios()->get_prestaciones_programadas($filtro_prestacion_pp, "ORDER BY s.id ASC"); + $prestacion = sq_uv::catalogo_modelo()->prestacion_preprogramada(max($datos_prestacion_pp)['id']); + $prestacion->set_id_venta( $venta->get_id() ); + $prestacion->guardar(); + } + + $cantidad_ventas_sumarizadas++; + $ventas_sumarizadas_generadas[] = $venta; + } + } + $this->proceso->agregar_notificacion("Cantidad de ventas sumarizadas generadas:" . $cantidad_ventas_sumarizadas, 'info'); + $this->proceso->agregar_notificacion("Cantidad de ventas cobradas:" . $this->cantidad_ventas_cobradas, 'info'); + + if(!empty($ventas_sumarizadas_generadas)){ + //generacion de alarma de facturacion + $this->evaluar_generacion_de_alarmas($ventas_sumarizadas_generadas); + } + } + + protected function reclamar_credito($venta, $ventas_por_cliente, $id_cliente, $importe_total) + { + $cliente = sq_uv::catalogo_modelo()->cliente($id_cliente); + if(!$cliente->tiene_credito()){ + return;//si el cliente no tiene credito no hago nada + } + //-- si el saldo del credito del cliente es mayor a cero + if($cliente->get_saldo_de_credito() >= $importe_total){ + // Generar cobranza + $this->generar_cobranza($venta, $ventas_por_cliente, $id_cliente, true, constantes::MEDIO_PAGO_CTA_CTE); + }else{ + // Pago parcial + } + } + + protected function generar_cobranza($venta, $ventas_por_cliente, $id_cliente, $utiliza_credito_disponible, $id_medio_pago) + { + $datos_cobranza = array(); + $importe_total_cobranza = 0; + foreach(array_keys($ventas_por_cliente) as $id_vc){ + $datos_cobranza['ventasCobradas'][] = array( + 'id_venta' => $ventas_por_cliente[$id_vc]['id'], + 'importe' => $ventas_por_cliente[$id_vc]['importe_total']); + $importe_total_cobranza += $ventas_por_cliente[$id_vc]['importe_total']; + } + + // Generar cobranza + $datos = [ + 'ventasCobradas' => $datos_cobranza['ventasCobradas'], + 'detalles' => [ + 0 => [ + "concepto" => 'NV', + "importe" => $importe_total_cobranza + ] + ], + 'id_cliente' => $id_cliente, + 'importe' => $importe_total_cobranza, + 'id_medio_pago' => $id_medio_pago, + 'utiliza_credito_disponible' => $utiliza_credito_disponible + ]; + $transaccion_cobranza = sq_uv::catalogo_transacciones()->cobranza(); + $transaccion_cobranza->set_datos($datos); + $transaccion_cobranza->procesar(); + //- tomar la cantidad disponible hasta el total de la deuda y guardarlo en la tabla de debitos + $venta->cambiar_estado( constantes::VENTA_COBRADA ); + $this->cantidad_ventas_cobradas++; + $this->importe_ventas_cobradas += $importe_total_cobranza; + //-- Guardar la venta. + $venta->guardar(); + } + + //----------------------------------------------------------- + // CONFIRMACION + //----------------------------------------------------------- + + public function cambiar_estado_ventas() + { + /* + * --------------------------------------------------------------------- + * ---------- TODA ESTA LOGICA SE RESOLVIO EN EL PROCESAMIENTO DE VENTAS + * POR CLIENTE --------------------------------------------------------- + * Cambiar el estado de las ventas de VENTA_ESTADO_PRE_FACT + * - si no hay movimiento de debito asociado pasarla a PENDIENTE + * - Si hay un movimiento de debito asociado con importe MENOR al total de la venta pasarla a PENDIENTE + * - Si hay un movimiento de debito asociado con importe IGUAL al total de la venta pasarla a COBRADA + * + * Atencion, las ventas sumarizadas pasan de estado a las ventas que la componen + */ + // Obtener las ventas en estado VENTA_ESTADO_PRE_FACT + $filtro['id_venta_estado'] = constantes::VENTA_ESTADO_PRE_FACT; + $filtro['id_periodo_facturacion'] = $this->id_reloj; + $ventas_prefacturadas = sq_uv::catalogo_daos()->ventas()->get_ventas($filtro, null, null, "cobranza_vence_en", null, null, true); + $ids_ventas_agrupadas_en_sumarizada = array_column(sq_uv::catalogo_daos()->ventas()->get_ventas_sumarizadas(),'id_venta_sumarizada'); + foreach($ventas_prefacturadas as $venta_prefact) + { + $venta = sq_uv::catalogo_modelo()->modelador_venta()->cargar($venta_prefact['id']); + $venta->cambiar_estado( constantes::VENTA_PENDIENTE ); + $venta->guardar(); + // Verificar si la venta no es parte de una sumarizada + if(!in_array($venta_prefact['id'], $ids_ventas_agrupadas_en_sumarizada)){ + $this->cantidad_ventas_generadas++; + $this->importe_ventas_generadas += $venta->get_importe_total(); + $this->ventas_generadas[] = $venta_prefact; + } + } + $this->proceso->agregar_notificacion("Cantidad de ventas generadas:" . $this->cantidad_ventas_generadas, 'info'); + } + + //----------------------------------------------------------- + // REVERTIR + //----------------------------------------------------------- + + protected function revertir_facturacion() + { + // Obtener tabla facturaciones_reversiones + $sql = "SELECT * FROM facturaciones_reversiones;"; + $prestaciones_pp = lib::entorno()->db()->consultar($sql); + + foreach($prestaciones_pp as $prestacion_pp){ + // Actualizar prestaciones pre-programadas + $prestacion = sq_uv::catalogo_modelo()->prestacion_preprogramada($prestacion_pp['id_servicio_prestacion_pre_programada']); + $prestacion->set_id_venta( $prestacion_pp['id_venta'] ); + $prestacion->guardar(); + // Actualizar estado de la venta + if(!is_null($prestacion_pp['id_venta'])){ + $venta = sq_uv::catalogo_modelo()->modelador_venta()->cargar($prestacion_pp['id_venta']); + $venta->cambiar_estado($prestacion_pp['id_venta_estado'], true); + $venta->guardar(); + } + } + + // Eliminar todas las ventas en prefacturacion + $filtro['id_venta_estado'] = constantes::VENTA_ESTADO_PRE_FACT; + $filtro['id_periodo_facturacion'] = $this->id_reloj; + $filtro['excluir_ventas_dependientes_de_sumarizada'] = false; + // El orden obtiene primero las sumarizadas (tipo 3) para que no falle por FK + $order = "ORDER BY id_tipo_venta DESC"; + $ventas = sq_uv::catalogo_daos()->ventas()->get_ventas($filtro, null, $order); + foreach(array_keys($ventas) as $id){ + $sql = ""; + $sql .= "DELETE FROM ventas_sumarizadas WHERE id_venta_sumariza_a = " . $ventas[$id]['id'].";"; + $sql .= "DELETE FROM ventas_articulos_convenios WHERE id_venta_articulo IN (SELECT id FROM ventas_articulos WHERE id_venta = " . $ventas[$id]['id'].");"; + $sql .= "DELETE FROM ventas_articulos WHERE id_venta = " . $ventas[$id]['id'].";"; + $sql .= "DELETE FROM ventas_convenios WHERE id_venta = " . $ventas[$id]['id'].";"; + $sql .= "DELETE FROM ventas_estados_log WHERE id_venta = " . $ventas[$id]['id'].";"; + $sql .= "DELETE FROM ventas_vencimientos WHERE id_venta = " . $ventas[$id]['id'].";"; + $sql .= "DELETE FROM facturaciones_alarmas_generadas_ventas WHERE id_venta = " . $ventas[$id]['id'].";"; + $sql .= "DELETE FROM ventas WHERE id = " . $ventas[$id]['id'].";"; + $this->entorno()->db()->ejecutar($sql); + } + $this->eliminar_log_reversion(); + } + + function eliminar_log_reversion() + { + // Eliminar tabla facturaciones_reversiones + $sql = "DELETE FROM facturaciones_reversiones;"; + $this->entorno()->db()->ejecutar($sql); + } + + function registrar_punto_reversion($id_sppp, $id_venta = null, $id_venta_estado = null) + { + $datos = array( + 'id_facturacion' => $this->id_facturacion, + 'id_servicio_prestacion_pre_programada' => $id_sppp, + 'id_venta' => $id_venta, + 'id_venta_estado' => $id_venta_estado + ); + $sql = sql::sql_array_a_insert('facturaciones_reversiones', $datos, $this->entorno()->db()); + lib::entorno()->db()->ejecutar($sql); + } + + function eliminar_cabecera_facturacion() + { + $sql = "DELETE FROM facturaciones WHERE id = " . $this->entorno()->db()->quote($this->id_facturacion) . ";"; + $this->entorno()->db()->ejecutar($sql); + } + + function eliminar_instancias_a_facturar() + { + $sql = null; +// foreach($this->datos['ids_servicios_instancias'] as $instancia){ +// $datos['id_servicio_instancia'] = $instancia; +// $datos['id_facturacion'] = $this->id_facturacion; + $sql .= "DELETE FROM facturaciones_servicios_instancias + WHERE id_facturacion = " . $this->entorno()->db()->quote($this->id_facturacion) . ";"; +// } + if($sql) { + $this->entorno()->db()->ejecutar($sql); + }else{ + throw new error_negocio("Proceso_revertir_facturacion: No se encontraron servicios instancias para procesar."); + } + } + + function eliminar_facturaciones_alarmas_configuradas() + { + $sql = "DELETE FROM facturaciones_alarmas_configuradas WHERE id_facturacion = " . $this->entorno()->db()->quote($this->id_facturacion) . ";"; + $this->entorno()->db()->ejecutar($sql); + } + + function eliminar_facturaciones_alarmas_generadas() + { + $sql = "DELETE FROM facturaciones_alarmas_generadas WHERE id_facturacion = " . $this->entorno()->db()->quote($this->id_facturacion) . ";"; + $this->entorno()->db()->ejecutar($sql); + } +} diff --git a/src/siu/sq/lib/modelo/facturador_alarma.php b/src/siu/sq/lib/modelo/facturador_alarma.php new file mode 100644 index 0000000..11b56b4 --- /dev/null +++ b/src/siu/sq/lib/modelo/facturador_alarma.php @@ -0,0 +1,178 @@ +datos['id_tipo_facturador_alarma']; + } + + function get_par_valor() + { + //supongo que siempre hay un solo registro en facturador_alarmas_parametros por registro en facturador_alarmas + return $this->datos['facturador_alarmas_parametros'][0]['par_valor']; + } + + protected function inicializar() + { + $this->datos['id'] = null; + $this->datos['codigo'] = null; + $this->datos['descripcion'] = null; + $this->datos['id_tipo_facturador_alarma'] = null; + $this->datos['descripcion_comportamiento'] = null; + $this->datos['estado'] = null; + $this->datos['estado_actualizado_en'] = null; + $this->datos['tipo_facturador_alarmas'] = []; + $this->datos['facturador_alarmas_parametros'] = []; + $this->alarma_generada = []; + $this->alarmas_generadas_ventas = []; + } + + protected function cargar($id) + { + $datos = lib::catalogo_daos()->alarmas()->obtener_alarma($id); + if(is_null($datos)){ + throw new error_not_found("Facturador alarma: La alarma de facturacion que intenta cargar no existe."); + }else{ + $this->datos = $datos; + } + } + + protected function validar() + { + // Controlar que esten cargados los datos basicos + $campos_obligatorios = [ + 'codigo', + 'descripcion', + 'id_tipo_facturador_alarma', + 'estado', + 'estado_actualizado_en' + ]; + $this->validar_campos_obligatorios($this->datos, $campos_obligatorios, __CLASS__); + } + + function guardar() + { + $this->validar(); + if(is_null($this->datos['id'])) { + $this->grabar(); + } else { + $this->actualizar(); + } + } + + protected function actualizar() + { + $datos = [ + 'codigo' => $this->datos['codigo'], + 'descripcion' => $this->datos['descripcion'], + 'id_tipo_facturador_alarma' => $this->datos['id_tipo_facturador_alarma'], + 'descripcion_comportamiento'=> $this->datos['descripcion_comportamiento'], + 'estado' => $this->datos['estado'], + 'estado_actualizado_en' => 'now()' + ]; + $where = ['id' => $this->datos['id']]; + $sql = sql::sql_array_a_update('facturador_alarmas', $datos, $where, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + $this->actualizar_facturador_alarmas_parametros(); + } + + function actualizar_facturador_alarmas_parametros() + { + $sql = ''; + foreach($this->datos['facturador_alarmas_parametros'] as $parametros){ + $datos = [ + 'par_rango_inferior'=> $parametros['par_rango_inferior'], + 'par_rango_superior'=> $parametros['par_rango_superior'], + 'par_valor' => $parametros['par_valor'], + 'par_lista' => $parametros['par_lista'] + ]; + $where = ['id_facturador_alarma' => $this->datos['id']]; + $sql .= sql::sql_array_a_update('facturador_alarmas_parametros', $datos, $where, $this->entorno()->db()); + } + if($sql != ''){ + $this->entorno()->db()->ejecutar($sql); + } + } + + protected function grabar() + { + $datos = [ + 'codigo' => $this->datos['codigo'], + 'descripcion' => $this->datos['descripcion'], + 'id_tipo_facturador_alarma' => $this->datos['id_tipo_facturador_alarma'], + 'descripcion_comportamiento'=> $this->datos['descripcion_comportamiento'], + 'estado' => $this->datos['estado'], + 'estado_actualizado_en' => 'now()' + ]; + $sql = sql::sql_array_a_insert('facturador_alarmas', $datos, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + $seq = $this->entorno()->db()->consultar_fila("SELECT currval('facturador_alarmas_id_seq') as seq;"); + $this->datos['id'] = $seq['seq']; + $this->grabar_facturador_alarmas_parametros(); + } + + protected function grabar_facturador_alarmas_parametros() + { + $sql = ''; + foreach($this->datos['facturador_alarmas_parametros'] as $parametros){ + $datos = [ + 'par_rango_inferior'=> $parametros['par_rango_inferior'], + 'par_rango_superior'=> $parametros['par_rango_superior'], + 'par_valor' => $parametros['par_valor'], + 'par_lista' => $parametros['par_lista'] + ]; + $sql .= sql::sql_array_a_insert('facturador_alarmas_parametros', $datos, $this->entorno()->db()); + } + if($sql != ''){ + $this->entorno()->db()->ejecutar($sql); + } + } + + function evaluar_generacion($id_facturacion, $venta) + { + switch ($this->get_id_tipo_facturador_alarma()){ + case constantes::ID_TIPO_FACTURADOR_ALARMA_VALOR: + if($venta->get_importe_total() > $this->get_par_valor()){ + $this->guardar_alarma($id_facturacion,$venta); + } + break; + case constantes::ID_TIPO_FACTURADOR_ALARMA_VENTA_ANULADA: + // LAS ALARMAS DE ESTE TIPO SE CREAN A MANO EN LA PANTALLA DE FACTURACION + break; + // AGREGAR OTROS CASE PARA LOS NUEVOS TIPOS DE ALARMAS DE FACTURACION QUE SE DEFINAN + } + } + + function guardar_alarma($id_facturacion, $venta,$motivos = null) + { + //solamente tengo que crear un registro en facturaciones_alarmas_generadas + if(empty($this->alarma_generada)){ + $datos_alarma_generada = [ + 'id_facturacion' => $id_facturacion, + 'id_facturador_alarma' => $this->get_id() + ]; + $sql = sql::sql_array_a_insert('facturaciones_alarmas_generadas', $datos_alarma_generada, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + $id = $this->entorno()->db()->ultimo_insert_id("facturaciones_alarmas_generadas_id_seq"); + $this->alarma_generada = [ + 'id' => $id, + 'id_facturacion' => $id_facturacion, + 'id_facturador_alarma' => $this->get_id() + ]; + } + $sql = "INSERT INTO facturaciones_alarmas_generadas_ventas (id_facturacion_alarma_generada, id_venta, motivos) " + . "VALUES (" . $this->alarma_generada['id'] . ", " . $venta->get_id() . ",'$motivos');"; + $this->entorno()->db()->ejecutar($sql); + } +} diff --git a/src/siu/sq/lib/modelo/reloj.php b/src/siu/sq/lib/modelo/reloj.php new file mode 100644 index 0000000..302eae8 --- /dev/null +++ b/src/siu/sq/lib/modelo/reloj.php @@ -0,0 +1,306 @@ +cargar_meses(); + $this->cargar_anios(); + $this->id_meses_excepciones = array(); + } + + + function __construct($entorno, $id = null, $id_periodo_fact_excepciones = null) + { + $this->entorno = $entorno; + $this->inicializar(); + if($id_periodo_fact_excepciones !== null){ + $this->cargar_periodos_facturacion_excepciones( $id_periodo_fact_excepciones ); + } + $this->datos = $this->cargar($id); + } + + private function cargar_periodos_facturacion_excepciones( $id_periodo_fact_excepciones ) + { + $sql = "SELECT id_mes + FROM meses_por_periodo_facturacion_excepciones fe + WHERE fe.id_periodo_facturacion_excepcion = " + . $this->entorno()->db()->quote($id_periodo_fact_excepciones) . ";"; + $excepciones = $this->entorno()->db()->consultar($sql); + foreach($excepciones as $mes){ + $this->id_meses_excepciones[] = $mes['id_mes']; + } + } + + protected function cargar($id_reloj = null, $filtro = array()) + { + if($id_reloj !== null){ + $filtro['id'] = $id_reloj; + }else{ + if( !isset($this->id_reloj) ){ + $filtro['id_reloj_facturador_estado'] = constantes::RELOJ_FACTURACION_ESTADO_ACTUAL; + } + } + $datos = lib::catalogo_daos()->reloj_faturador()->obtener_reloj($filtro); + if($datos){ + $this->id_reloj = $datos['id']; + }else{ + $error = implode(', ', array_map(function ($v, $k) { return $k . '=' . $v; }, $filtro, array_keys($filtro))); + throw new error_negocio("RELOJ: No se pudo cargar el reloj con el filtro: " . $error); + } + return $datos; + } + + function get_descripcion_anio() + { + foreach($this->anios as $anio){ + if($anio['id'] == $this->datos['id_anio']){ + return $anio['codigo']; + } + } + } + + function get_descripcion_mes() + { + foreach($this->meses as $mes){ + if($mes['id'] == $this->datos['id_mes']){ + return $mes['codigo']; + } + } + } + + function get_actual() + { + return $this->id_reloj; + } + + // El valor 'posicion' es cero o numero positivo. Si es cero devuelve el + // ID del reloj seteado en el constructor + function get_reloj( $posicion = 0, $id_frecuencia = null ) + { + // Obtengo el anio y mes del reloj ingresado al constructor + $id_mes = $this->datos['id_mes']; + $id_anio = $this->datos['id_anio']; + // Obtener la cantidad de meses que representa el $id_frecuencia + if($id_frecuencia){ + $frecuencia = $this->get_frecuencia_facturacion($id_frecuencia); + }else{ + $frecuencia = null; + } + // Si hay posicion seteada busco el id del mes + if($posicion != 0){ + for($p = 0 ; $p < $posicion ; $p++){ + // Si la frecuencia esta seteada afecta los meses + if($frecuencia){ + for($i = 0 ; $i < $frecuencia['cantidad_meses'] ; $i++){ + // Sumar un mes al mes y año del reloj actual. + $datos = $this->get_proximo_mes_y_anio($id_mes, $id_anio); + $id_mes = $datos['id_mes']; + $id_anio = $datos['id_anio']; + } + }else{ + $datos = $this->get_proximo_mes_y_anio($id_mes, $id_anio); + $id_mes = $datos['id_mes']; + $id_anio = $datos['id_anio']; + } + $datos = $this->cargar(null, array('id_mes' => $id_mes, 'id_anio' => $id_anio)); + } + $id = $datos['id']; + }else{ + $id = $this->id_reloj; + } + return $id; + } + + /* + * Actualiza el estado del reloj actual a EJECUTADO + * Obtiene el proximo reloj y lo deja como ACTUAL + */ + + function avanzar() + { + //validar que existen 24 relojes posteriores al actual + //y si no existen crear los que hagan falta. + if (!$this->existen_relojes_posteriores()) { + $this->crear_relojes_faltantes(); + } + + // Actualizar el reloj + $where = array("id" => $this->id_reloj); + $datos = array("id_reloj_facturador_estado" => constantes::RELOJ_FACTURACION_ESTADO_EJECUTADO); + $sql = sql::sql_array_a_update("reloj_facturador", $datos, $where, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + + $where = array("id" => $this->get_reloj(1)); + $datos = array("id_reloj_facturador_estado" => constantes::RELOJ_FACTURACION_ESTADO_ACTUAL); + $sql = sql::sql_array_a_update("reloj_facturador", $datos, $where, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + } + + /** + * Valida que existan los relojes de facturación necesarios para alcanzar + * el buffer deseado específicado en los parámetros de sistema. + */ + function existen_relojes_posteriores() + { + $cant_relojes_necesarios = lib::parametros_sistema()->get_valor(constantes::BUFFER_RELOJES); + $cant_relojes_posteriores = lib::catalogo_daos()->reloj_faturador() + ->obtener_cantidad_de_relojes_posteriores_al_actual(); + return $cant_relojes_posteriores >= $cant_relojes_necesarios; + } + + /** + * Genera los relojes de facturación necesarios para alcanzar + * el buffer deseado específicado en los parámetros de sistema. + */ + function crear_relojes_faltantes() + { + $cant_relojes_necesarios = lib::parametros_sistema()->get_valor(constantes::BUFFER_RELOJES); + $relojes_faltantes = $cant_relojes_necesarios - lib::catalogo_daos()->reloj_faturador() + ->obtener_cantidad_de_relojes_posteriores_al_actual(); + + $ultimo_reloj = lib::catalogo_daos()->reloj_faturador() + ->obtener_ultimo_reloj(); + + $ultimo = [ + 'mes' => $ultimo_reloj['numero_mes'], + 'anio' => $ultimo_reloj['numero_anio'], + ]; + + for ($i = 0; $i < ($relojes_faltantes); $i++) { + //si es el mes 12 tengo que comenzar a insertar los relojes para el siguiente año + if ($ultimo["mes"] == 12) { + $ultimo_anio = $ultimo['anio'] + 1; + $anio = lib::catalogo_daos()->reloj_faturador()->obtener_anio(['numero_anio' => $ultimo_anio]); + if(is_null($anio)) { // si no existe el año siguiente lo inserto en la base + $datos_anio = [ + 'codigo' => $ultimo_anio, + 'descripcion' => 'Año siguiente al ' . $ultimo['anio'], + 'anio' => $ultimo_anio + ]; + $sql = sql::sql_array_a_insert('anios', $datos_anio, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + $anio = lib::catalogo_daos()->reloj_faturador()->obtener_anio(['numero_anio' => $ultimo_anio]); + } + $mes = lib::catalogo_daos()->reloj_faturador()->obtener_mes(['numero_mes' => 1]); //obtengo el primer mes del año + } else { + $anio = lib::catalogo_daos()->reloj_faturador()->obtener_anio(['numero_anio' => $ultimo['anio']]); + $mes = lib::catalogo_daos()->reloj_faturador()->obtener_mes(['numero_mes' => $ultimo['mes'] + 1]); //obtengo el mes siguiente al último generado + } + + //insert row reloj facturador + $datos_reloj = [ + 'codigo' => substr($mes['descripcion'], 0, 3) . "." . $anio['anio'], + 'descripcion' => $mes['descripcion'] . " " . $anio['anio'], + 'id_anio' => $anio['id'], + 'id_mes' => $mes['id'], + 'dia_inicio_facturacion' => 1, + 'dia_limite_facturacion' => lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_FACTURACION_LIMITE), + 'id_reloj_facturador_estado' => constantes::RELOJ_FACTURACION_ESTADO_NOINICIADO, + 'creado_por' => constantes::USUARIO_INTERNO_SQ_UV, + 'creado_en' => 'now()' + ]; + $sql = sql::sql_array_a_insert('reloj_facturador', $datos_reloj, $this->entorno()->db()); + $this->entorno()->db()->ejecutar($sql); + + $ultimo['anio'] = $anio['anio']; + $ultimo['mes'] = $mes['numero_mes']; + } + } + + //-------------------- MESES Y ANIOS -------------------------------------- + + private function cargar_meses() + { + $sql = "SELECT id, numero_mes, codigo FROM meses ORDER BY numero_mes ASC;"; + $this->meses = $this->entorno()->db()->consultar($sql); + } + + private function cargar_anios() + { + $sql = "SELECT id, anio, codigo FROM anios ORDER BY anio ASC;"; + $this->anios = $this->entorno()->db()->consultar($sql); + } + + private function get_proximo_mes_y_anio($id_mes, $id_anio) + { + foreach(array_keys($this->meses) as $idm){ + if($id_mes == $this->meses[$idm]['id']){ + if($this->meses[$idm]['numero_mes'] == '12'){ + // Reinicio el mes + foreach($this->meses as $mes){ + if($mes['numero_mes'] == 1){ + $salida['id_mes'] = $mes['id']; + } + } + // Sumo un anio + foreach(array_keys($this->anios) as $ida){ + if($id_anio == $this->anios[$ida]['id']){ + if(isset($this->anios[$ida + 1])){ + $salida['id_anio'] = $this->anios[$ida + 1]['id']; + }else{ + throw new error_negocio( + "RELOJ: No se pudo determinar el proximo año."); + } + } + } + }else{ + // Sumo un mes y sigo en el mismo anio + if(isset($this->meses[$idm + 1])){ + $salida['id_mes'] = $this->meses[$idm + 1]['id']; + $salida['id_anio'] = $id_anio; + }else{ + throw new error_negocio( + "RELOJ: No se pudo determinar el proximo mes."); + } + } + } + } + // Evaluar si el mes obtenido se encuentra dentro de las excepciones + if( in_array($salida['id_mes'], $this->id_meses_excepciones) ){ + return $this->get_proximo_mes_y_anio($salida['id_mes'], $salida['id_anio']); + } + + return $salida; + } + + function get_frecuencia_facturacion($id_frecuencia) + { + $sql = "SELECT cantidad_meses FROM frecuencias_facturacion + WHERE id = " . $this->entorno()->db()->quote($id_frecuencia) . ";"; + return $this->entorno()->db()->consultar_fila($sql); + } + + function get_numero_mes() + { + foreach($this->meses as $mes){ + if($mes['id'] == $this->datos['id_mes']){ + return $mes['numero_mes']; + } + } + } + + function get_numero_anio() + { + foreach($this->anios as $anio){ + if($anio['id'] == $this->datos['id_anio']){ + return $anio['anio']; + } + } + } + + protected function validar(){} + public function guardar(){} +} diff --git a/src/siu/sq/lib/modelo/reloj_instancia.php b/src/siu/sq/lib/modelo/reloj_instancia.php new file mode 100644 index 0000000..bea48f4 --- /dev/null +++ b/src/siu/sq/lib/modelo/reloj_instancia.php @@ -0,0 +1,49 @@ +entorno; + } + + function __construct($entorno, $id_servicio_instancia) + { + $this->entorno = $entorno; + $this->id_servicio_instancia = $id_servicio_instancia; + $this->cargar_reloj(); + } + + private function cargar_reloj() + { + $sql = "SELECT id_reloj_facturador, id_periodo_facturacion_excepcion + FROM servicios_instancias + WHERE id = " . $this->entorno()->db()->quote($this->id_servicio_instancia) + . ";"; + $datos = $this->entorno()->db()->consultar_fila($sql); + $this->reloj = lib::catalogo_modelo()->reloj( $datos['id_reloj_facturador'], $datos['id_periodo_facturacion_excepcion']); + } + + public function get_reloj( $posicion = 0, $id_frecuencia = null ) + { + return $this->reloj->get_reloj( $posicion, $id_frecuencia ); + } + + function get_descripcion_anio() + { + return $this->reloj->get_descripcion_anio(); + } + + function get_descripcion_mes() + { + return $this->reloj->get_descripcion_mes(); + } +} diff --git a/src/siu/sq/lib/procesos/catalogo.php b/src/siu/sq/lib/procesos/catalogo.php index 257a9e8..532be1c 100644 --- a/src/siu/sq/lib/procesos/catalogo.php +++ b/src/siu/sq/lib/procesos/catalogo.php @@ -38,4 +38,19 @@ class catalogo extends \SIU\SQ\Lib\entorno\catalogo { return new proceso_reenviar_mensaje($topicName, $listenerName, $messageId, constantes::PROCESO_REENVIAR_MENSAJE, $this->entorno); } + + function generar_facturacion() + { + return new proceso_generar_facturacion(constantes::PROCESO_GENERAR_FACTURACION, $this->entorno); + } + + function revertir_facturacion() + { + return new proceso_revertir_facturacion(constantes::PROCESO_REVERTIR_FACTURACION, $this->entorno); + } + + function confirmar_facturacion() + { + return new proceso_confirmar_facturacion(constantes::PROCESO_CONFIRMAR_FACTURACION, $this->entorno); + } } diff --git a/src/siu/sq/lib/procesos/proceso_confirmar_facturacion.php b/src/siu/sq/lib/procesos/proceso_confirmar_facturacion.php new file mode 100644 index 0000000..00bd8e0 --- /dev/null +++ b/src/siu/sq/lib/procesos/proceso_confirmar_facturacion.php @@ -0,0 +1,62 @@ +facturador(); + $facturador->set_proceso($this); + $facturador->confirmar(); + + $this->cantidad_ventas_generadas = $facturador->get_cantidad_ventas_generadas(); + $this->importe_ventas_generadas = $facturador->get_importe_ventas_generadas(); + $this->ventas_generadas = $facturador->get_ventas_generadas(); + + $this->agregar_notificacion("Ha finalizado el proceso de confirmación de la facturación", 'info'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_OK); + } catch (error_db $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (error_negocio $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (error $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (\Exception $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } + } + + public function get_cantidad_ventas_generadas() + { + return $this->cantidad_ventas_generadas; + } + + public function get_importe_ventas_generadas() + { + return $this->importe_ventas_generadas; + } + + public function get_ventas_generadas() + { + return $this->ventas_generadas; + } +} diff --git a/src/siu/sq/lib/procesos/proceso_generar_facturacion.php b/src/siu/sq/lib/procesos/proceso_generar_facturacion.php new file mode 100644 index 0000000..624b441 --- /dev/null +++ b/src/siu/sq/lib/procesos/proceso_generar_facturacion.php @@ -0,0 +1,123 @@ +facturador(); + $facturador->set_proceso($this); + $facturador->set_dia_primer_vencimiento($this->id_reloj_1er_vencimiento, $this->dia_1er_vencimiento); + if(lib::parametros_sistema()->get_valor(constantes::PARAMETROS_SISTEMA_CANTIDAD_VENCIMIENTOS) == constantes::SEGUNDO_VENCIMIENTO) { + $facturador->set_dia_segundo_vencimiento($this->id_reloj_2do_vencimiento, $this->dia_2do_vencimiento); + } + $facturador->set_instancias_a_facturar($this->ids_servicios_instancias); + if(isset($this->ids_facturador_alarmas)){ + $facturador->set_ids_facturador_alarmas($this->ids_facturador_alarmas); + } + $facturador->generar(); + + $this->id_facturacion = $facturador->get_id_facturacion(); + $this->cantidad_ventas_pre_generadas = $facturador->get_cantidad_ventas_pre_generadas(); + $this->importe_ventas_pre_generadas = $facturador->get_importe_ventas_pre_generadas(); + $this->cantidad_ventas_cobradas = $facturador->get_cantidad_ventas_cobradas(); + $this->importe_ventas_cobradas = $facturador->get_importe_ventas_cobradas(); + + $this->agregar_notificacion("Ha finalizado el proceso de generación de la facturación", 'info'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_OK); + } catch (error_db $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (error_negocio $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (error $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (\Exception $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } + } + + public function set_dia_primer_vencimiento($id_reloj_1er_vencimiento, $dia_1er_vencimiento) + { + $this->id_reloj_1er_vencimiento = $id_reloj_1er_vencimiento; + $this->dia_1er_vencimiento = $dia_1er_vencimiento; + } + + public function set_dia_segundo_vencimiento($id_reloj_2do_vencimiento, $dia_2do_vencimiento) + { + $this->id_reloj_2do_vencimiento = $id_reloj_2do_vencimiento; + $this->dia_2do_vencimiento = $dia_2do_vencimiento; + } + + public function set_parametros_alarmas($parametros) + { + + } + + public function set_instancias_a_facturar($id_servicios_instancias) + { + $this->ids_servicios_instancias = $id_servicios_instancias; + } + + public function get_id_facturacion() + { + return $this->id_facturacion; + } + + public function get_ids_facturador_alarmas() + { + return $this->ids_facturador_alarmas; + } + + public function set_ids_facturador_alarmas(array $ids_alarmas) + { + $this->ids_facturador_alarmas = $ids_alarmas; + } + + public function get_cantidad_ventas_cobradas() + { + return $this->cantidad_ventas_cobradas; + } + + public function get_importe_ventas_cobradas() + { + return $this->importe_ventas_cobradas; + } + + public function get_cantidad_ventas_pre_generadas() + { + return $this->cantidad_ventas_pre_generadas; + } + + public function get_importe_ventas_pre_generadas() + { + return $this->importe_ventas_pre_generadas; + } +} diff --git a/src/siu/sq/lib/procesos/proceso_revertir_facturacion.php b/src/siu/sq/lib/procesos/proceso_revertir_facturacion.php new file mode 100644 index 0000000..e584327 --- /dev/null +++ b/src/siu/sq/lib/procesos/proceso_revertir_facturacion.php @@ -0,0 +1,54 @@ +facturador(); + $facturador->set_proceso($this); + $facturador->set_id_facturacion($this->id_facturacion); + $facturador->set_instancias_a_facturar($this->ids_servicios_instancias); + $facturador->revertir(); + + $this->agregar_notificacion("Ha finalizado el proceso de revertir la facturación", 'info'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_OK); + } catch (error_db $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (error_negocio $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (error $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } catch (\Exception $e) { + $this->agregar_notificacion($e->getMessage(), 'error'); + $this->finalizar_registro_ejecucion(proceso::ESTADO_ERROR); + throw $e; + } + } + + public function set_instancias_a_facturar($id_servicios_instancias) + { + $this->ids_servicios_instancias = $id_servicios_instancias; + } + + public function set_id_facturacion($id_facturacion) + { + $this->id_facturacion = $id_facturacion; + } +} -- GitLab