diff --git a/common/Alias/Model.php b/common/Alias/Model.php index 915a095..67189ab 100644 --- a/common/Alias/Model.php +++ b/common/Alias/Model.php @@ -90,7 +90,7 @@ abstract class Model extends BaseModel implements ModelInterface { protected static function parseInput($input): array { return array_intersect_key((array) $input, array_combine(static::$fields, static::$fields)); } - public static function add(ModelFactory $factory, $input): ?ModelInterface { + public static function add(ModelFactory $factory, $input): bool|ModelInterface { $data = static::parseInput($input); $class = get_called_class(); if (method_exists($class, 'find')) { @@ -103,7 +103,7 @@ abstract class Model extends BaseModel implements ModelInterface { $where = array_values($where); $obj = $factory->find($class)->where($where)->one(); } - if ($obj === null) { + if ($obj === false or $obj === null) { $obj = $factory->create($class, $data); } return $obj; diff --git a/common/Controller/Auth.php b/common/Controller/Auth.php new file mode 100644 index 0000000..3498b39 --- /dev/null +++ b/common/Controller/Auth.php @@ -0,0 +1,82 @@ +generate(); + return $this->withJson($response, compact('key')); + } + public function login(Request $request, Response $response, Service $service, Factory $factory): Response { + $post = json_decode($request->getBody()); + $user = $factory->find(User::class)->where([['name', $post->name]])->one(); + $output = [ + 'login' => false, + 'token' => '' + ]; + if ($user->enabled == 0) { + $this->withJson($response, $output); + } + if ($user->validate($post->password)) { + $token = $service->generateToken(); + $status = $user->setToken($token->selector, $token->token); + if ($status['logged_in']) { + $output['login'] = true; + $output['token'] = $token->full; + $output['expires'] = $status['expires']; + } + } + return $this->withJson($response, $output); + } + protected function getLogin(object $post, Factory $factory): bool|Login { + list($selector, $token) = explode(':', $post->token); //Token from the cookie + $login = $factory->find(Login::class)->where([['selector', $selector]])->one(); + if ($login === false or !password_verify($token, $login->token) or !$login->isValid()) { + return false; + } + return $login; + } + public function validate(Request $request, Response $response, Factory $factory): Response { + $post = json_decode($request->getBody()); + if (!$this->getLogin($post, $factory)) { + return $this->withJson($response, ['token' => $post->token, 'error' => 'Not authorized'], 401); + } + return $this->withJson($response, ['token' => $post->token, 'status' => 'Authorized']); + } + public function user(Request $request, Response $response, Factory $factory): Response { + $post = json_decode($request->getBody()); + $login = $this->getLogin($post, $factory); + if (!$login) { + return $this->withJson($response, ['token' => $post->token, 'error' => 'Not authorized'], 401); + } + $output = [ + 'token' => $post->token, + 'user' => $login->user()->name + ]; + return $this->withJson($response, $output); + } + public function logout(Request $request, Response $response, Factory $factory): Response { + $post = json_decode($request->getBody()); + list($selector, $token) = explode(':', $post->token); //Token from the cookie + $login = $factory->find(Login::class)->where([['selector', $selector]])->one(); + $output = [ + 'token' => $post->token, + 'logout' => false + ]; + if ($login !== false) { + $output['logout'] = $login->user()->logout(); + } else { + $output['logout'] = true; + } + return $this->withJson($response, $output); + } +} diff --git a/common/Controller/Configs.php b/common/Controller/Configs.php new file mode 100644 index 0000000..f7787b8 --- /dev/null +++ b/common/Controller/Configs.php @@ -0,0 +1,43 @@ +find(Config::class)->where((['name', $config_name]))->one(); + $output = [ + 'name' => $config_name, + 'valid' => $config !== false, + 'value' => $config->value + ]; + return $this->withJson($response, $output); + } + public function set(Request $request, Response $response, Factory $factory): Response { + $post = $request->getParsedBody(); + $config = $factory->find(Config::class)->where([['name', $post['name']]])->one(); + if (!$config) { + $config = Config::add($factory, $post); + } else { + $config->edit($post); + } + $output = [ + 'input' => $post, + 'config' => null + ]; + if ($config !== false) { + $config->save(); + $output['config'] = [ + 'name' => $config->name, + 'value' => $config->value + ]; + } + return $this->withJson($response, $output); + } +} diff --git a/common/Controller/Proyectos/Cierres.php b/common/Controller/Proyectos/Cierres.php new file mode 100644 index 0000000..40278b1 --- /dev/null +++ b/common/Controller/Proyectos/Cierres.php @@ -0,0 +1,31 @@ +find(Proyecto::class)->many(); + $cierres = []; + foreach ($proyectos as $proyecto) { + if (count($proyecto->cierres()) == 0) { + continue; + } + $cierres[$proyecto->descripcion] = [ + 'proyecto' => $proyecto->descripcion, + 'total' => count($proyecto->cierres()), + 'promesados' => count($proyecto->cierres(3)), + 'rechazados' => count($proyecto->cierres(-1)), + 'pendientes' => count($proyecto->cierres(2)), + 'ultimo_pendiente' => (count($proyecto->cierres(2)) > 0) ? $proyecto->cierres(2)[0]->periodo() : 0 + ]; + } + return $this->withJson($response, compact('cierres')); + } +} diff --git a/common/Controller/Proyectos/Cuotas.php b/common/Controller/Proyectos/Cuotas.php new file mode 100644 index 0000000..de76d07 --- /dev/null +++ b/common/Controller/Proyectos/Cuotas.php @@ -0,0 +1,63 @@ +find(Proyecto::class)->many(); + $cuotas = []; + foreach ($proyectos as $proyecto) { + foreach ($proyecto->cuotas() as $cuota) { + $cuotas []= $cuota->toArray(); + } + } + return $this->withJson($response, ['cuotas' => $cuotas]); + } + public function mes(Request $request, Response $response, Factory $factory): Response { + $proyectos = $factory->find(Proyecto::class)->many(); + $dias = []; + foreach ($proyectos as $proyecto) { + foreach ($proyecto->cuotasMes() as $cuota) { + $f = $cuota->pago()->fecha(); + if ($f->isoWeekday() == 6 or $f->isoWeekDay() == 7) { + $f = $f->copy()->addDays(2)->startOfWeek(); + } + $dia = $f->format('Y-m-d'); + if (!isset($dias[$dia])) { + $dias[$dia] = ['dia' => $dia, 'proyectos' => [$proyecto->descripcion => ['proyecto' => $proyecto->descripcion, 'cantidad' => 0]]]; + } + if (!isset($dias[$dia]['proyectos'][$proyecto->descripcion])) { + $dias[$dia]['proyectos'][$proyecto->descripcion] = ['proyecto' => $proyecto->descripcion, 'cantidad' => 0]; + } + $dias[$dia]['proyectos'][$proyecto->descripcion]['cantidad'] ++; + } + } + uksort($dias, function($a, $b) { + return strcmp($a, $b); + }); + return $this->withJson($response, ['proyecto' => $proyecto->toArray(), 'dias' => $dias]); + } + public function hoy(Request $request, Response $response, Factory $factory): Response { + $proyectos = $factory->find(Proyecto::class)->many(); + $hoy = 0; + foreach ($proyectos as $proyecto) { + $hoy += count($proyecto->cuotasHoy()); + } + return $this->withJson($response, ['hoy' => $hoy]); + } + public function pendientes(Request $request, Response $response, Factory $factory): Response { + $proyectos = $factory->find(Proyecto::class)->many(); + $pendientes = 0; + foreach ($proyectos as $proyecto) { + $pendientes += count($proyecto->cuotasPendientes()); + } + return $this->withJson($response, ['pendientes' => $pendientes]); + } +} diff --git a/common/Define/Model.php b/common/Define/Model.php index d01ebfa..ae7c41b 100644 --- a/common/Define/Model.php +++ b/common/Define/Model.php @@ -51,7 +51,7 @@ interface Model { * @param array $input Input data * @return Model|null */ - public static function add(ModelFactory $factory, array $input): ?Model; + public static function add(ModelFactory $factory, array $input): bool|Model; /** * Edit current model parsing {$input} data diff --git a/common/Factory/Model.php b/common/Factory/Model.php index 1798b45..27ac8fe 100644 --- a/common/Factory/Model.php +++ b/common/Factory/Model.php @@ -1,7 +1,7 @@ where([[$f, $v]]); } $model = $model->one(); - if ($model !== null) { + if ($model !== false and $model !== null) { return $model; } } @@ -142,13 +142,20 @@ class Model { 'full', 'full outer', 'full_outer', 'fullouter', 'outer' => 'fullOuterJoin' }; if (strtolower($join->type) == 'raw') { - if (isset($join->alias)) { - $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to], $join->alias, $join->params); + if (isset($join->params)) { + if (isset($join->alias)) { + $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to], $join->alias, $join->params); + } else { + $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to], $join->params); + } } else { - $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to], $join->params); + if (isset($join->alias)) { + $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to], $join->alias); + } else { + $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to]); + } } - } - if (isset($join->alias)) { + } elseif (isset($join->alias)) { $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to], $join->alias); } else { $orm = $orm->{$method}($join->table, [$join->from, $join->operator, $join->to]); @@ -267,14 +274,34 @@ class Model { return $this; } protected function addOrder($order) { + $map = [ + 'column' => ['column', 'col', 'c', 0], + 'direction' => ['direction', 'dir', 'd', 1] + ]; + if (!is_array($order)) { + $order = [$order]; + } $defaults = ['direction' => 'asc']; $required = ['column', 'direction']; $o = []; foreach ($order as $key => $val) { - $k = match (strtolower($key)) { + /*$k = match (strtolower($key)) { 'column', 'col', 'c', 0 => 'column', 'direction', 'dir', 'd', 1 => 'direction' - }; + };*/ + $k = -1; + foreach ($map as $i => $m) { + if (is_array($m)) { + if (in_array($key, $m)) { + $k = $i; + break; + } + continue; + } + if ($key == $m) { + $k = $i; + } + } $o[$k] = $val; } foreach ($defaults as $key => $val) { diff --git a/common/Service/Auth.php b/common/Service/Auth.php index b0ad30c..f87f517 100644 --- a/common/Service/Auth.php +++ b/common/Service/Auth.php @@ -4,48 +4,57 @@ namespace Incoviba\API\Common\Service; use Psr\Http\Message\ServerRequestInterface as Request; class Auth { - protected string $key; - public function __construct(string $key) { - $this->key = $key; - } - public function isValid(Request $request): bool { - $api_key = ''; - if ($request->hasHeader('Authorization')) { - $api_key = $request->getHeader('Authorization'); - if (is_array($api_key)) { - $api_key = $api_key[0]; - } - if (str_contains($api_key, 'Bearer')) { - $api_key = explode(' ', $api_key)[1]; - } - } elseif ($request->getParsedBody() !== null and in_array('API_KEY', $request->getParsedBody())) { - $api_key = $request->getParsedBody()['API_KEY']; - } elseif ($request->getQueryParams() !== null and in_array('API_KEY', array_keys($request->getQueryParams()))) { - $api_key = $request->getQueryParams()['API_KEY']; + protected string $key; + public function __construct(string $key) { + $this->key = $key; } - if ($this->key == $api_key) { - return true; + public function isValid(Request $request): bool { + $api_key = $this->getRequestKey($request); + if ($this->key == $api_key) { + return true; + } + return false; } - return false; - } - public function generate(int $length = 32, bool $removeSimilarCharacters = true): string { - $token = ""; - try { - $bytesWithMargin = random_bytes($length*3); - - $base64 = base64_encode($bytesWithMargin); - $purified = preg_replace("/[+=\/.]/", "", $base64); - - if ($removeSimilarCharacters){ - $purified = preg_replace("/[I1l0Oo]/", "", $purified); + protected function getRequestKey(Request $request) { + if ($request->hasHeader('Authorization')) { + return $this->getKeyFromHeader($request); + } elseif ($request->getParsedBody() !== null and in_array('API_KEY', $request->getParsedBody())) { + return $request->getParsedBody()['API_KEY']; + } elseif ($request->getQueryParams() !== null and in_array('API_KEY', array_keys($request->getQueryParams()))) { + return $request->getQueryParams()['API_KEY']; + } + return ''; + } + protected function getKeyFromHeader(Request $request) { + $api_key = $request->getHeader('Authorization'); + if (is_array($api_key)) { + $api_key = $api_key[0]; + } + if (str_contains($api_key, 'Bearer')) { + $api_key = explode(' ', $api_key)[1]; + } + return $api_key; + } + public function generate(int $length = 32, bool $removeSimilarCharacters = true): string { + $token = ""; + try { + $bytesWithMargin = random_bytes($length*3); + $base64 = base64_encode($bytesWithMargin); + $purified = preg_replace("/[+=\/.]/", "", $base64); + if ($removeSimilarCharacters) { + $purified = preg_replace("/[I1l0Oo]/", "", $purified); + } + $token = substr($purified, 0, $length); + } catch (\Exception $e){ + error_log(var_export($e, true)); + } + return $token; } - - $token = substr($purified, 0, $length); - - } catch (\Exception $e){ - echo $e->getMessage(); + public function generateToken(): object { + $selector = bin2hex(\random_bytes(12)); + $token = bin2hex(\random_bytes(20)); + $full = "{$selector}:{$token}"; + $token = password_hash($token, \PASSWORD_DEFAULT); + return (object) compact('selector', 'token', 'full'); } - - return $token; - } } diff --git a/docker-compose.yml b/docker-compose.yml index f28449f..95e5c7b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,8 @@ services: volumes: - ./:/code - ./nginx.conf:/etc/nginx/conf.d/default.conf + networks: + - incoviba api: build: dockerfile: Dockerfile @@ -32,6 +34,8 @@ services: - ./:/code - ./php.ini:/usr/local/etc/php/conf.d/docker.ini - ./logs/php/:/var/log/php/ + networks: + - incoviba db: container_name: db @@ -41,6 +45,8 @@ services: env_file: .db.env volumes: - incoviba_data:/var/lib/mysql + networks: + - incoviba adminer: container_name: adminer image: adminer:latest @@ -49,6 +55,12 @@ services: ports: - "8083:8080" env_file: .adminer.env + networks: + - incoviba volumes: incoviba_data: {} + +networks: + incoviba: + external: true diff --git a/nginx.conf b/nginx.conf index 03fa2d0..7353076 100644 --- a/nginx.conf +++ b/nginx.conf @@ -7,10 +7,24 @@ server { root /code/public; location / { - try_files $uri /index.php$is_args$args; + try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; + add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH'; + add_header 'Content-Type' 'application/json'; + add_header 'Content-Length' 0; + return 204; + } + + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; + add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH'; + try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; include fastcgi_params; diff --git a/resources/routes/auth.php b/resources/routes/auth.php index 83f7a11..efcfdee 100644 --- a/resources/routes/auth.php +++ b/resources/routes/auth.php @@ -1,12 +1,8 @@ get('/auth/generate', function(Request $request, Response $response, Service $service): Response { - $key = $service->generate(); - $response->getBody()->write(json_encode(['key' => $key])); - return $response - ->withStatus(200) - ->withHeader('content-type', 'application/json'); -}); +$app->post('/auth/login[/]', [Auth::class, 'login']); +$app->get('/auth/generate[/]', [Auth::class, 'generate']); +$app->post('/auth/validate[/]', [Auth::class, 'validate']); +$app->post('/auth/user[/]', [Auth::class, 'user']); +$app->post('/auth/logout[/]', [Auth::class, 'logout']); diff --git a/resources/routes/configs.php b/resources/routes/configs.php new file mode 100644 index 0000000..139f78f --- /dev/null +++ b/resources/routes/configs.php @@ -0,0 +1,5 @@ +get('/config/{config_name}', [Configs::class, 'get']); +$app->post('/config', [Configs::class, 'set']); diff --git a/resources/routes/proyectos.php b/resources/routes/proyectos.php index be72690..37938fd 100644 --- a/resources/routes/proyectos.php +++ b/resources/routes/proyectos.php @@ -1,8 +1,18 @@ group('/proyectos', function ($app) { $app->post('/add[/]', [Proyectos::class, 'add']); + $app->group('/cuotas', function($app) { + $app->get('/hoy[/]', [Cuotas::class, 'hoy']); + $app->get('/mes[/]', [Cuotas::class, 'mes']); + $app->get('/pendientes[/]', [Cuotas::class, 'pendientes']); + }); + $app->group('/cierres', function($app) { + $app->get('[/]', Cierres::class); + }); $app->get('[/]', Proyectos::class); }); $app->group('/proyecto/{proyecto_id}', function ($app) { diff --git a/setup/settings/01_env.php b/setup/settings/01_env.php index d59e03d..5526843 100644 --- a/setup/settings/01_env.php +++ b/setup/settings/01_env.php @@ -1,5 +1,5 @@ $_ENV['DEBUG'] ?? false, - 'AUTH_KEY' => $_ENV['API_KEY'] + 'API_KEY' => $_ENV['API_KEY'] ]; diff --git a/setup/setups/02_auth.php b/setup/setups/02_auth.php index 86304c6..5c18c18 100644 --- a/setup/setups/02_auth.php +++ b/setup/setups/02_auth.php @@ -3,7 +3,7 @@ use Psr\Container\ContainerInterface as Container; return [ Incoviba\API\Common\Service\Auth::class => function(Container $c) { - return new Incoviba\API\Common\Service\Auth($c->get('AUTH_KEY')); + return new Incoviba\API\Common\Service\Auth($c->get('API_KEY')); }, Incoviba\API\Common\Middleware\Auth::class => function(Container $c) { return new Incoviba\API\Common\Middleware\Auth( diff --git a/src/Admin/Config.php b/src/Admin/Config.php new file mode 100644 index 0000000..61a17fc --- /dev/null +++ b/src/Admin/Config.php @@ -0,0 +1,14 @@ +user === null) { + $this->user = $this->childOf(User::class, [Model::SELF_KEY => 'user_id']); + } + return $this->user; + } + public function time(DateTime $time = null) { + if ($time === null) { + return Carbon::parse($this->time); + } + $this->time = $time->format('Y-m-d H:m:s'); + return null; + } + public function isValid() { + if ($this->status == 0) { + return false; + } + $expiration = $this->factory->find(Config::class)->where([['name', 'cookie_expiration_time']])->one(); + if ($this->time()->diffInSeconds() > $expiration->value) { + $this->status = 0; + $this->save(); + return false; + } + return true; + } +} diff --git a/src/Auth/User.php b/src/Auth/User.php new file mode 100644 index 0000000..12951b9 --- /dev/null +++ b/src/Auth/User.php @@ -0,0 +1,79 @@ +logins === null) { + $this->logins = $this->parentOf(Login::class, [Model::CHILD_KEY => 'user_id']); + } + return $this->logins; + } + protected $login; + public function login() { + if ($this->login === null) { + $this->login = $this->factory->find(Login::class) + ->where([['user_id', $this->id]]) + ->order([['column' => 'time', 'direction' => 'desc']]) + ->one(1); + } + return $this->login; + } + + public function isIn(): bool { + return $this->login()->isValid(); + } + public function validate($password): bool { + return password_verify($password, $this->password); + } + public function validLogins(): bool|array { + return $this->factory->find(Login::class)->where([['user_id', $this->id], ['status', 1]])->many(); + } + public function logout() { + $logins = $this->validLogins(); + if ($logins === false) { + return true; + } + $bool = true; + foreach ($logins as $login) { + $login->status = 0; + $bool &= $login->save(); + } + return $bool; + } + public function setToken($selector, $token) { + $this->logout(); + $expiration = $this->factory->find(Config::class)->where([['name', 'cookie_expiration_time']])->one(); + $data = [ + 'user_id' => $this->id, + 'time' => Carbon::now()->format('Y-m-d H:i:s '), + 'selector' => $selector, + 'token' => $token, + 'status' => 1 + ]; + $output = [ + 'input' => $data, + 'login' => null, + 'logged_in' => false + ]; + $login = Login::add($this->factory, $data); + $output['login'] = $login; + if ($login !== false and $login->is_new()) { + $output['logged_in'] = $login->save(); + $output['expires'] = $login->time()->addSeconds($expiration->value)->timestamp; + } + return $output; + } +} diff --git a/src/Proyecto/Proyecto.php b/src/Proyecto/Proyecto.php index 2416eb7..9aa6ac4 100644 --- a/src/Proyecto/Proyecto.php +++ b/src/Proyecto/Proyecto.php @@ -1,9 +1,12 @@ direccion_o === null) { - $this->direccion_o = $this->childOf(Direccion::class, [Model::SELF_KEY => 'direccion']); + protected $direccion_o; + public function direccion() { + if ($this->direccion_o === null) { + $this->direccion_o = $this->childOf(Direccion::class, [Model::SELF_KEY => 'direccion']); + } + return $this->direccion_o; } - return $this->direccion_o; - } - protected $inmobiliaria_o; - public function inmobiliaria() { - if ($this->inmobiliaria_o === null) { - $this->inmobiliaria_o = $this->childOf(Inmobiliaria::class, [Model::SELF_KEY => 'inmobiliaria', Model::PARENT_KEY => 'rut']); + protected $inmobiliaria_o; + public function inmobiliaria() { + if ($this->inmobiliaria_o === null) { + $this->inmobiliaria_o = $this->childOf(Inmobiliaria::class, [Model::SELF_KEY => 'inmobiliaria', Model::PARENT_KEY => 'rut']); + } + return $this->inmobiliaria_o; + } + + protected $cuotas; + protected function buildCuotas() { + return $this->factory->find(Cuota::class) + ->join([ + ['venta', 'venta.pie', 'cuota.pie'], + ['JOIN (SELECT e1.* FROM estado_venta e1 JOIN (SELECT MAX(id) AS id, venta FROM estado_venta GROUP BY venta) e0 ON e0.id = e1.id)', 'ev.venta', 'venta.id', 'alias' => 'ev', 'type' => 'raw'], + ['tipo_estado_venta', 'tev.id', 'ev.estado', 'alias' => 'tev'], + ['propiedad', 'propiedad.id', 'venta.propiedad'], + ['propiedad_unidad', 'pu.propiedad', 'propiedad.id', 'alias' => 'pu'], + ['unidad', 'unidad.id', 'pu.unidad'], + ['proyecto_tipo_unidad', 'ptu.id', 'unidad.pt', 'alias' => 'ptu'], + ['proyecto', 'proyecto.id', 'ptu.proyecto'], + ['pago', 'pago.id', 'cuota.pago'], + ['JOIN (SELECT e1.* FROM estado_pago e1 JOIN (SELECT MAX(id) AS id, pago FROM estado_pago GROUP BY pago) e0 ON e0.id = e1.id)', 'ep.pago', 'pago.id', 'alias' => 'ep', 'type' => 'raw'], + ['tipo_estado_pago', 'tep.id', 'ep.estado', 'alias' => 'tep'] + ]) + ->where([ + ['proyecto.id', $this->id], + ['tev.activa', 1], + ['tep.active', 1] + ]); + } + public function cuotas() { + if ($this->cuotas === null or !isset($this->cuotas->total)) { + $cuotas = []; + if ($this->cuotas !== null) { + $cuotas = (array) $this->cuotas; + } + $cuotas['total'] = $this->$this->buildCuotas()->many(); + $this->cuotas = (object) $cuotas; + } + return $this->cuotas->total; + } + public function cuotasHoy() { + if ($this->cuotas === null or !isset($this->cuotas->hoy)) { + $cuotas = []; + if ($this->cuotas !== null) { + $cuotas = (array) $this->cuotas; + } + $f = Carbon::today(); + $cuotas['hoy'] = $this->buildCuotas() + ->where([ + ['pago.fecha', $f->format('Y-m-d')] + ]) + ->many(); + $this->cuotas = (object) $cuotas; + } + return $this->cuotas->hoy; + } + public function cuotasPendientes() { + if (!isset($this->cuotas) or !isset($this->cuotas->mes)) { + $cuotas = []; + if (isset($this->cuotas)) { + $cuotas = (array) $this->cuotas; + } + $f = Carbon::today(); + $cuotas['pendientes'] = $this->buildCuotas() + ->where([ + ['ep.estado', 1, '<'], + ['ep.estado', 0, '>='] + ]) + ->many(); + $this->cuotas = (object) $cuotas; + } + return $this->cuotas->pendientes; + } + public function cuotasMes() { + if (!isset($this->cuotas) or !isset($this->cuotas->mes)) { + $cuotas = []; + if (isset($this->cuotas)) { + $cuotas = (array) $this->cuotas; + } + $f = Carbon::today(); + error_log(var_export($this->buildCuotas(), true)); + $cuotas['mes'] = $this->buildCuotas() + ->where([ + ['pago.fecha', $f->format('Y-m-d'), 'operator' => '>'], + ['pago.fecha', $f->copy()->addMonth(1)->format('Y-m-d'), '<='] + ]) + ->many(); + $this->cuotas = (object) $cuotas; + } + return $this->cuotas->mes; + } + + protected $cierres; + public function cierres(int $vigentes = 0) + { + if (!isset($this->cierres[$vigentes]) or $this->cierres[$vigentes] == null) { + $orm = $this->factory->find(Cierre::class) + ->select([['cierre', '*']]) + ->join([ + ['join (select e1.* from estado_cierre e1 join (select cierre, max(id) as id from estado_cierre group by cierre) e0 on e0.id = e1.id)', + 'ec.cierre', 'cierre.id', 'alias' => 'ec', 'type' => 'raw'], + ['tipo_estado_cierre', 'tipo_estado_cierre.id', 'ec.tipo'], + ['proyecto', 'proyecto.id', 'cierre.proyecto'], + ['unidad_cierre', 'unidad_cierre.cierre', 'cierre.id'], + ['unidad', 'unidad.id', 'unidad_cierre.unidad'] + ]) + ->where([ + ['proyecto.id', $this->id], + ['unidad_cierre.principal', 1] + ]) + ->order([ + 'proyecto.descripcion', + 'tipo_estado_cierre.vigente', + 'cierre.fecha', + 'LPAD(unidad.descripcion, 4, "0")' + ]) + ->group(['cierre.id']); + switch ($vigentes) { + case Cierre::VIGENTES: + $orm = $orm->where([['tipo_estado_cierre.vigente', 1]]); + break; + case Cierre::NO_VIGENTES: + $orm = $orm->where([['tipo_estado_cierre.vigente', 0]]); + break; + case (Cierre::VIGENTES + 1): + $orm = $orm->where([ + ['tipo_estado_cierre.vigente', 1], + ['tipo_estado_cierre.descripcion', 'promesado', 'type' => 'not like'] + ]); + break; + case (Cierre::VIGENTES + 2): + $orm = $orm->where([ + ['tipo_estado_cierre.vigente', 1], + ['tipo_estado_cierre.descripcion', 'promesado', 'type' => 'like'] + ]); + break; + }; + error_log(var_export($orm, true)); + $this->cierres[$vigentes] = $orm->many(); + } + return $this->cierres[$vigentes]; } - return $this->inmobiliaria_o; - } } diff --git a/src/Venta/Cierre.php b/src/Venta/Cierre.php new file mode 100644 index 0000000..79bba6e --- /dev/null +++ b/src/Venta/Cierre.php @@ -0,0 +1,25 @@ +fecha); + } + $this->fecha = $fecha->format('Y-m-d'); + } + public function periodo() { + $today = Carbon::today(); + $dif = $today->diffInDays($this->fecha()); + return $dif; + } +} diff --git a/src/Venta/Cuota.php b/src/Venta/Cuota.php new file mode 100644 index 0000000..9899a8c --- /dev/null +++ b/src/Venta/Cuota.php @@ -0,0 +1,33 @@ +pago_o === null) { + $this->pago_o = $this->childOf(Pago::class, [Model::SELF_KEY => 'pago']); + } + return $this->pago_o; + } +} diff --git a/src/Venta/Pago.php b/src/Venta/Pago.php index 3928e20..bc62bd2 100644 --- a/src/Venta/Pago.php +++ b/src/Venta/Pago.php @@ -1,6 +1,38 @@ tipo_o === null) { + $this->tipo_o = $this->childOf(TipoPago::class, [Model::SELF_KEY => 'tipo']); + } + return $this->tipo_o; + } + + public function fecha(DateTimeInterface $fecha = null) { + if ($fecha === null) { + return Carbon::parse($this->fecha); + } + $this->fecha = $fecha->format('Y-m-d'); + } +} diff --git a/src/Venta/TipoPago.php b/src/Venta/TipoPago.php new file mode 100644 index 0000000..04ed9bb --- /dev/null +++ b/src/Venta/TipoPago.php @@ -0,0 +1,12 @@ +