Files
oficial/app/src/Service/Venta.php
Juan Pablo Vial f7af93b815 Throw Read
2025-04-21 19:40:26 -04:00

531 lines
19 KiB
PHP

<?php
namespace Incoviba\Service;
use Exception;
use DateTimeImmutable;
use DateMalformedStringException;
use Incoviba\Exception\ServiceAction\{Read, Update};
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal\Service;
use Incoviba\Common\Implement;
use Incoviba\Repository;
use Incoviba\Model;
class Venta extends Service
{
public function __construct(
LoggerInterface $logger,
protected Repository\Venta $ventaRepository,
protected Repository\Venta\EstadoVenta $estadoVentaRepository,
protected Repository\Venta\TipoEstadoVenta $tipoEstadoVentaRepository,
protected Repository\Venta\Credito $creditoRepository,
protected Repository\Venta\Escritura $escrituraRepository,
protected Repository\Venta\Pago $pagoRepository,
protected Repository\Venta\EstadoPago $estadoPagoRepository,
protected Venta\FormaPago $formaPagoService,
protected Venta\Propietario $propietarioService,
protected Venta\Propiedad $propiedadService,
protected Venta\Pie $pieService,
protected Venta\Subsidio $subsidioService,
protected Venta\Credito $creditoService,
protected Venta\BonoPie $bonoPieService,
protected Venta\Pago $pagoService,
protected Proyecto\Terreno $terrenoService,
protected Money $moneyService,
protected Valor $valorService
) {
parent::__construct($logger);
}
/**
* @param int $venta_id
* @return Model\Venta
* @throws Read
*/
public function getById(int $venta_id): Model\Venta
{
try {
return $this->process($this->ventaRepository->fetchById($venta_id));
} catch (Implement\Exception\EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}
}
public function getByProyecto(int $proyecto_id): array
{
$ventas = $this->ventaRepository->fetchByProyecto($proyecto_id);
return array_map([$this, 'process'], $ventas);
}
public function getActivaByProyecto(int $proyecto_id): array
{
$ventas = $this->ventaRepository->fetchActivaByProyecto($proyecto_id);
return array_map([$this, 'process'], $ventas);
}
public function getIdsByProyecto(int $proyecto_id): array
{
return $this->ventaRepository->fetchIdsByProyecto($proyecto_id);
}
public function getByProyectoAndUnidad(string $proyecto_nombre, int $unidad_descripcion): Model\Venta
{
$venta = $this->ventaRepository->fetchByProyectoAndUnidad($proyecto_nombre, $unidad_descripcion);
return $this->process($venta);
}
/**
* @param string $unidad
* @param string $tipo
* @return array
* @throws Read
*/
public function getByUnidad(string $unidad, string $tipo): array
{
try {
$ventas = $this->ventaRepository->fetchByUnidad($unidad, $tipo);
return array_map([$this, 'process'], $ventas);
} catch (Implement\Exception\EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}
}
/**
* @param int $unidad_id
* @return Model\Venta
* @throws Read
*/
public function getByUnidadId(int $unidad_id): Model\Venta
{
try {
return $this->process($this->ventaRepository->fetchByUnidadId($unidad_id));
} catch (Implement\Exception\EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}
}
public function getByPropietario(string $propietario): array
{
$ventas = $this->ventaRepository->fetchByPropietario($propietario);
return array_map([$this, 'process'], $ventas);
}
public function getByPrecio(string $precio): array
{
$ventas = $this->ventaRepository->fetchByPrecio($precio);
return array_map([$this, 'process'], $ventas);
}
public function getEscriturasByProyecto(int $proyecto_id): array
{
$ventas = $this->ventaRepository->fetchEscriturasByProyecto($proyecto_id);
return array_map([$this, 'process'], $ventas);
}
public function getByIdForSearch(int $venta_id): array
{
return $this->ventaRepository->fetchByIdForSearch($venta_id);
}
public function getByIdForList(int $venta_id): array
{
return $this->ventaRepository->fetchByIdForList($venta_id);
}
public function getByPie(int $pie_id): Model\Venta
{
return $this->process($this->ventaRepository->fetchByPie($pie_id));
}
public function getFacturacionById(int $venta_id): array
{
$venta = $this->getById($venta_id);
$escritura = (in_array($venta->currentEstado()->tipoEstadoVenta->descripcion, ['escriturando', 'firmado por inmobiliaria'])) ? $venta->currentEstado()->fecha : $venta->fecha;
$data = [
'id' => $venta->id,
'fecha' => $venta->fecha->format('Y-m-d'),
'valor' => $venta->valor,
'escritura' => $escritura->format('Y-m-d'),
'unidades' => [],
];
foreach ($venta->propiedad()->unidades as $unidad) {
$data['unidades'] []= [
'id' => $unidad->id,
'tipo' => ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion),
'descripcion' => $unidad->descripcion,
'prorrateo' => $unidad->prorrateo,
'valor' => $unidad->valor,
'precio' => (isset($unidad->currentPrecio)) ? $unidad->currentPrecio->valor : 0
];
}
$principal = $venta->propiedad()->principal();
$data['principal'] = [
'id' => $principal->id,
'descripcion' => $principal->descripcion
];
return $data;
}
protected function process(Model\Venta $venta): Model\Venta
{
$venta->addFactory('formaPago', (new Implement\Repository\Factory())
->setCallable([$this->formaPagoService, 'getByVenta'])
->setArgs(['venta_id' => $venta->id]));
$venta->addFactory('estados', (new Implement\Repository\Factory())
->setCallable([$this->estadoVentaRepository, 'fetchByVenta'])
->setArgs([$venta->id]));
$venta->addFactory('currentEstado', (new Implement\Repository\Factory())
->setCallable([$this->estadoVentaRepository, 'fetchCurrentByVenta'])
->setArgs([$venta->id]));
return $venta;
}
/**
* @throws Exception
*/
public function add(array $data): Model\Venta
{
$fecha = new DateTimeImmutable($data['fecha_venta']);
$data['uf'] = $this->moneyService->getUF($fecha);
$propietario = $this->addPropietario($data);
$propiedad = $this->addPropiedad($data);
$formaPago = $this->addFormaPago($data);
$venta_data = [
'propietario' => $propietario->rut,
'propiedad' => $propiedad->id,
'fecha' => $fecha->format('Y-m-d'),
'valor_uf' => $this->valorService->clean($data['valor']),
'fecha_ingreso' => (new DateTimeImmutable())->format('Y-m-d'),
'uf' => $data['uf']
];
$map = ['pie', 'subsidio', 'credito', 'bono_pie'];
foreach ($map as $field) {
$name = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $field))));
if (isset($formaPago->{$name})) {
$venta_data[$field] = $formaPago->{$name}->id;
}
}
try {
return $this->ventaRepository->fetchByPropietarioAndPropiedad($propietario->rut, $propiedad->id);
} catch (Implement\Exception\EmptyResult) {
$venta = $this->ventaRepository->create($venta_data);
$venta->setFormaPago($formaPago);
$venta = $this->ventaRepository->save($venta);
$tipoEstado = $this->tipoEstadoVentaRepository->fetchByDescripcion('vigente');
$estado = $this->estadoVentaRepository->create([
'venta' => $venta->id,
'estado' => $tipoEstado->id,
'fecha' => $venta->fecha->format('Y-m-d')
]);
$this->estadoVentaRepository->save($estado);
return $venta;
}
}
protected function addPropietario(array $data): Model\Venta\Propietario
{
if (isset($data['natural_uno'])) {
if (isset($data['natural_multiple'])) {
return $this->addDosPropietarios($data);
}
return $this->addUnPropietario($data);
}
return $this->addSociedad($data);
}
protected function addUnPropietario(array $data): Model\Venta\Propietario
{
$fields = array_fill_keys([
'rut',
'nombres',
'apellido_paterno',
'apellido_materno',
'calle',
'numero',
'extra',
'comuna'
], 0);
$filtered_data = array_intersect_key($data, $fields);
return $this->propietarioService->addPropietario($filtered_data);
}
protected function addDosPropietarios(array $data): Model\Venta\Propietario
{
$fields = array_fill_keys([
'rut_otro',
'nombres_otro',
'apellido_paterno_otro',
'apellido_materno_otro',
'calle_otro',
'numero_otro',
'extra_otro',
'comuna_otro'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$mapped_data = array_combine([
'rut',
'nombres',
'apellido_paterno',
'apellido_materno',
'calle',
'numero',
'extra',
'comuna'
], $filtered_data);
$otro = $this->propietarioService->addPropietario($mapped_data);
$data['otro'] = $otro->rut;
$fields = array_fill_keys([
'rut',
'nombres',
'apellido_paterno',
'apellido_materno',
'calle',
'numero',
'extra',
'comuna',
'otro'
], 0);
$filtered_data = array_intersect_key($data, $fields);
return $this->propietarioService->addPropietario($filtered_data);
}
protected function addSociedad(array $data): Model\Venta\Propietario
{
$representante = $this->addUnPropietario($data);
$data['representante'] = $representante->rut;
$fields = array_fill_keys([
'rut_sociedad',
'razon_social',
'calle_comercial',
'numero_comercial',
'extra_comercial',
'comuna_comercial',
'representante'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$mapped_data = array_combine([
'rut',
'razon_social',
'calle',
'numero',
'extra',
'comuna',
'representante'
], $filtered_data);
return $this->propietarioService->addSociedad($mapped_data);
}
protected function addPropiedad(array $data): Model\Venta\Propiedad
{
$ids = array_filter($data, function($key) {
return str_starts_with($key, 'unidad');
}, ARRAY_FILTER_USE_KEY);
return $this->propiedadService->addPropiedad($ids);
}
protected function addFormaPago(array $data): Model\Venta\FormaPago
{
return $this->formaPagoService->add($data);
}
/**
* @param Model\Venta $venta
* @param array $data
* @return Model\Venta
* @throws Update
*/
public function edit(Model\Venta $venta, array $data): Model\Venta
{
try {
$filteredData = $this->ventaRepository->filterData($data);
return $this->ventaRepository->edit($venta, $filteredData);
} catch (Implement\Exception\EmptyResult $exception) {
throw new Update(__CLASS__, $exception);
}
}
protected function addEstado(Model\Venta $venta, Model\Venta\TipoEstadoVenta $tipoEstadoVenta, array $data): void
{
try {
$fecha = new DateTimeImmutable($data['fecha']);
} catch (DateMalformedStringException) {
$fecha = new DateTimeImmutable();
}
$estadoData = [
'venta' => $venta->id,
'estado' => $tipoEstadoVenta->id,
'fecha' => $fecha->format('Y-m-d')
];
$estado = $this->estadoVentaRepository->create($estadoData);
$this->estadoVentaRepository->save($estado);
}
public function escriturar(Model\Venta $venta, array $data): bool
{
if (in_array($venta->currentEstado()->tipoEstadoVenta->descripcion, ['escriturando', 'firmado por inmobiliaria', 'archivado'])) {
return true;
}
try {
if ($this->validarData($data, ['fecha_reajuste', 'valor_reajuste'])) {
$this->reajustarEscritura($venta, $data);
}
if ($this->validarData($data, ['fecha_pago'], ['valor_pago_pesos', 'valor_pago_ufs'])) {
$this->abonoEscritura($venta, $data);
}
if ($this->validarData($data, ['banco_credito'], ['valor_credito'])) {
$this->editCredito($venta, $data);
}
if ($this->validarData($data, ['valor_subsidio', 'valor_ahorro', 'fecha'])) {
$this->subsidioEscritura($venta, $data);
}
$tipoEstado = $this->tipoEstadoVentaRepository->fetchByDescripcion('escriturando');
$this->addEstado($venta, $tipoEstado, ['fecha' => $data['fecha']]);
return true;
} catch (Implement\Exception\EmptyResult) {
return false;
}
}
protected function validarData(array $data, array $keys, array $optionals = []): bool
{
foreach ($keys as $key) {
if (!isset($data[$key])) {
return false;
}
if ($data[$key] === '') {
return false;
}
}
foreach ($optionals as $key) {
if (isset($data[$key]) and $data[$key] !== '') {
break;
}
}
return true;
}
protected function reajustarEscritura(Model\Venta $venta, array $data): void
{
try {
$fecha = new DateTimeImmutable($data['fecha_reajuste']);
} catch (DateMalformedStringException) {
$fecha = new DateTimeImmutable();
}
$reajusteData = [
'valor' => $this->valorService->clean($data['valor_reajuste']),
'fecha' => $fecha->format('Y-m-d')
];
$pie = $venta->formaPago()->pie;
$this->pieService->reajustar($pie, $reajusteData);
}
protected function abonoEscritura(Model\Venta $venta, array $data): void
{
try {
$fecha = new DateTimeImmutable($data['fecha_pago']);
} catch (DateMalformedStringException) {
$fecha = new DateTimeImmutable();
}
$uf = $this->moneyService->getUF($fecha);
$valor = $data['valor_pago_ufs'] !== '' ? $this->valorService->clean($data['valor_pago_ufs']) * $uf : $this->valorService->clean($data['valor_pago_pesos']);
$pagoData = [
'valor' => $valor,
'fecha' => $fecha->format('Y-m-d'),
'uf' => $uf
];
$pago = $this->pagoService->add($pagoData);
$escrituraData = [
'valor' => $valor,
'fecha' => $fecha->format('Y-m-d'),
'uf' => $uf,
'pago' => $pago->id
];
$escritura = $this->escrituraRepository->create($escrituraData);
$escritura = $this->escrituraRepository->save($escritura);
$this->ventaRepository->edit($venta, ['escritura' => $escritura->id]);
}
protected function subsidioEscritura(Model\Venta $venta, array $data): void
{
try {
$fecha = new DateTimeImmutable($data['fecha']);
} catch (DateMalformedStringException) {
$fecha = new DateTimeImmutable();
}
$uf = $this->moneyService->getUF($fecha);
$subsidioData = [
'fecha_venta' => $fecha->format('Y-m-d'),
'ahorro' => $this->valorService->clean($data['valor_ahorro']),
'subsidio' => $this->valorService->clean($data['valor_subsidio']),
'uf' => $uf
];
$formaPago = $this->addFormaPago($subsidioData);
$this->ventaRepository->edit($venta, ['subsidio' => $formaPago->subsidio->id]);
}
protected function editCredito(Model\Venta $venta, array $data): void
{
try {
$fecha = new DateTimeImmutable($data['fecha']);
} catch (DateMalformedStringException) {
$fecha = new DateTimeImmutable();
}
$uf = $this->moneyService->getUF($fecha);
$valor = $this->valorService->clean($data['valor_credito']) * $uf;
if ($venta->formaPago()->credito === null) {
if ($data['valor_credito'] === 0) {
return;
}
$pagoData = [
'valor' => $valor,
'fecha' => $fecha->format('Y-m-d'),
'uf' => $uf
];
$pago = $this->pagoService->add($pagoData);
$creditoData = [
'banco' => $data['banco_credito'],
'valor' => $valor,
'pago' => $pago->id
];
$credito = $this->creditoRepository->create($creditoData);
$credito = $this->creditoRepository->save($credito);
$this->ventaRepository->edit($venta, ['credito' => $credito->id]);
return;
}
if ($data['valor_credito'] === 0) {
$this->pagoRepository->remove($venta->formaPago()->credito->pago);
$this->creditoRepository->remove($venta->formaPago()->credito);
$this->ventaRepository->edit($venta, ['credito' => null]);
return;
}
$this->pagoRepository->edit($venta->formaPago()->credito->pago, [
'valor' => $valor,
'banco' => $data['banco_credito'],
'uf' => $uf
]);
$this->estadoPagoRepository->edit($venta->formaPago()->credito->pago->currentEstado, [
'fecha' => $fecha->format('Y-m-d')
]);
$this->creditoRepository->edit($venta->formaPago()->credito, [
'valor' => $valor,
'fecha' => $fecha->format('Y-m-d'),
'uf' => $uf
]);
}
public function desistir(Model\Venta $venta, array $data): bool
{
try {
if ($this->validarData($data, ['fecha', 'devolucion'])) {
$pago = $this->pagoService->add(['fecha' => $data['fecha'], 'valor' => $this->valorService->clean($data['devolucion'])]);
$this->ventaRepository->edit($venta, ['resciliacion' => $pago->id]);
}
$tipoEstado = $this->tipoEstadoVentaRepository->fetchByDescripcion('desistida');
$this->addEstado($venta, $tipoEstado, ['fecha' => $data['fecha']]);
return true;
} catch (Implement\Exception\EmptyResult) {
return false;
}
}
public function insistir(Model\Venta $venta): bool
{
try {
$pago = $venta->resciliacion();
$this->pagoService->delete($pago);
$estado = $venta->currentEstado();
$this->estadoVentaRepository->remove($estado);
$this->ventaRepository->edit($venta, ['resciliacion' => null]);
return true;
} catch (Implement\Exception\EmptyResult) {
return false;
}
}
}