From 1a7b10ce3c999f7654004d5fc8c87e293b7c4168 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Mon, 24 Jul 2023 20:55:26 -0400 Subject: [PATCH] Auth, Login, Home, Venta->Listados->Precios --- app/common/Alias/View.php | 9 + app/common/Define/Connection.php | 14 + app/common/Define/Database.php | 8 + app/common/Define/Model.php | 8 + app/common/Define/Repository.php | 11 + app/common/Ideal/Model.php | 16 + app/common/Ideal/Repository.php | 126 ++++ app/common/Implement/Connection.php | 61 ++ app/common/Implement/Database/MySQL.php | 24 + .../Implement/Exception/EmptyResult.php | 15 + app/composer.json | 31 + app/public/assets/images/Isotipo 16.png | Bin 0 -> 3552 bytes app/public/assets/images/Isotipo 32.png | Bin 0 -> 4572 bytes app/public/assets/images/Isotipo 64.png | Bin 0 -> 6361 bytes app/public/assets/images/logo_cabezal.png | Bin 0 -> 23004 bytes app/public/index.php | 7 + app/public/robots.txt | 2 + app/resources/routes/01_api.php | 13 + app/resources/routes/03_proyectos.php | 6 + app/resources/routes/04_ventas.php | 10 + app/resources/routes/98_login.php | 8 + app/resources/routes/99_base.php | 4 + app/resources/routes/api/proyectos.php | 6 + app/resources/routes/api/ventas.php | 13 + app/resources/routes/api/ventas/precios.php | 6 + app/resources/routes/ventas/cuotas.php | 9 + app/resources/routes/ventas/precios.php | 6 + app/resources/views/guest.blade.php | 7 + app/resources/views/home.blade.php | 26 + .../views/home/cierres_vigentes.blade.php | 24 + .../views/home/cuotas_por_vencer.blade.php | 22 + app/resources/views/layout/base.blade.php | 5 + app/resources/views/layout/body.blade.php | 5 + .../views/layout/body/footer.blade.php | 4 + .../views/layout/body/header.blade.php | 7 + .../views/layout/body/header/menu.blade.php | 17 + .../body/header/menu/contabilidad.blade.php | 8 + .../layout/body/header/menu/guest.blade.php | 1 + .../body/header/menu/herramientas.blade.php | 7 + .../body/header/menu/inmobiliarias.blade.php | 1 + .../body/header/menu/operadores.blade.php | 9 + .../body/header/menu/proyectos.blade.php | 8 + .../layout/body/header/menu/search.blade.php | 1 + .../layout/body/header/menu/user.blade.php | 31 + .../layout/body/header/menu/ventas.blade.php | 41 ++ .../views/layout/body/scripts.blade.php | 4 + .../layout/body/scripts/datatables.blade.php | 4 + app/resources/views/layout/head.blade.php | 10 + .../views/layout/head/styles.blade.php | 3 + .../layout/head/styles/datatables.blade.php | 3 + app/resources/views/login/form.blade.php | 60 ++ .../views/ventas/cuotas/pendientes.blade.php | 108 +++ .../views/ventas/precios/list.blade.php | 676 ++++++++++++++++++ app/setup/app.php | 45 ++ app/setup/composer.php | 6 + app/setup/middlewares/01_auth.php | 2 + app/setup/middlewares/98_logs.php | 2 + app/setup/middlewares/99_routes.php | 12 + app/setup/settings/env.php | 2 + app/setup/settings/folders.php | 12 + app/setup/settings/urls.php | 21 + app/setup/setups/database.php | 18 + app/setup/setups/logs.php | 32 + app/setup/setups/middlewares.php | 15 + app/setup/setups/services.php | 14 + app/setup/setups/views.php | 23 + app/src/Controller/Base.php | 90 +++ app/src/Controller/Login.php | 55 ++ app/src/Controller/Proyectos.php | 21 + app/src/Controller/Users.php | 11 + app/src/Controller/Ventas/Cuotas.php | 60 ++ app/src/Controller/Ventas/Precios.php | 24 + app/src/Middleware/Authentication.php | 48 ++ app/src/Model/Banco.php | 16 + app/src/Model/Comuna.php | 18 + app/src/Model/Direccion.php | 22 + app/src/Model/Inmobiliaria.php | 38 + app/src/Model/Inmobiliaria/TipoSociedad.php | 16 + app/src/Model/Login.php | 25 + app/src/Model/Provincia.php | 18 + app/src/Model/Proyecto.php | 32 + app/src/Model/Proyecto/ProyectoTipoUnidad.php | 42 ++ app/src/Model/Proyecto/Superficie.php | 8 + app/src/Model/Proyecto/Terreno.php | 8 + app/src/Model/Region.php | 20 + app/src/Model/Role.php | 7 + app/src/Model/Tipo.php | 16 + app/src/Model/User.php | 28 + app/src/Model/Venta/Cierre.php | 26 + app/src/Model/Venta/Cuota.php | 38 + app/src/Model/Venta/Datos.php | 27 + app/src/Model/Venta/EstadoPago.php | 22 + app/src/Model/Venta/EstadoPrecio.php | 21 + app/src/Model/Venta/Pago.php | 32 + app/src/Model/Venta/Pie.php | 27 + app/src/Model/Venta/Precio.php | 23 + app/src/Model/Venta/Propietario.php | 46 ++ app/src/Model/Venta/TipoEstadoPago.php | 8 + app/src/Model/Venta/TipoEstadoPrecio.php | 7 + app/src/Model/Venta/TipoPago.php | 8 + app/src/Model/Venta/TipoUnidad.php | 16 + app/src/Model/Venta/Unidad.php | 25 + app/src/Repository/Banco.php | 35 + app/src/Repository/Comuna.php | 51 ++ app/src/Repository/Direccion.php | 48 ++ app/src/Repository/Inmobiliaria.php | 55 ++ .../Repository/Inmobiliaria/TipoSociedad.php | 36 + app/src/Repository/Login.php | 77 ++ app/src/Repository/Provincia.php | 51 ++ app/src/Repository/Proyecto.php | 88 +++ .../Proyecto/ProyectoTipoUnidad.php | 54 ++ app/src/Repository/Region.php | 53 ++ app/src/Repository/User.php | 46 ++ app/src/Repository/Venta/Cierre.php | 82 +++ app/src/Repository/Venta/Cuota.php | 161 +++++ app/src/Repository/Venta/EstadoPago.php | 67 ++ app/src/Repository/Venta/EstadoPrecio.php | 65 ++ app/src/Repository/Venta/Pago.php | 73 ++ app/src/Repository/Venta/Pie.php | 60 ++ app/src/Repository/Venta/Precio.php | 55 ++ app/src/Repository/Venta/Propietario.php | 82 +++ app/src/Repository/Venta/TipoEstadoPago.php | 40 ++ app/src/Repository/Venta/TipoEstadoPrecio.php | 35 + app/src/Repository/Venta/TipoPago.php | 35 + app/src/Repository/Venta/TipoUnidad.php | 39 + app/src/Repository/Venta/Unidad.php | 47 ++ app/src/Service/Format.php | 24 + app/src/Service/Login.php | 135 ++++ app/src/Service/Ventas/Pago.php | 31 + app/src/Service/Ventas/Precio.php | 19 + 130 files changed, 4302 insertions(+) create mode 100644 app/common/Alias/View.php create mode 100644 app/common/Define/Connection.php create mode 100644 app/common/Define/Database.php create mode 100644 app/common/Define/Model.php create mode 100644 app/common/Define/Repository.php create mode 100644 app/common/Ideal/Model.php create mode 100644 app/common/Ideal/Repository.php create mode 100644 app/common/Implement/Connection.php create mode 100644 app/common/Implement/Database/MySQL.php create mode 100644 app/common/Implement/Exception/EmptyResult.php create mode 100644 app/composer.json create mode 100644 app/public/assets/images/Isotipo 16.png create mode 100644 app/public/assets/images/Isotipo 32.png create mode 100644 app/public/assets/images/Isotipo 64.png create mode 100644 app/public/assets/images/logo_cabezal.png create mode 100644 app/public/index.php create mode 100644 app/public/robots.txt create mode 100644 app/resources/routes/01_api.php create mode 100644 app/resources/routes/03_proyectos.php create mode 100644 app/resources/routes/04_ventas.php create mode 100644 app/resources/routes/98_login.php create mode 100644 app/resources/routes/99_base.php create mode 100644 app/resources/routes/api/proyectos.php create mode 100644 app/resources/routes/api/ventas.php create mode 100644 app/resources/routes/api/ventas/precios.php create mode 100644 app/resources/routes/ventas/cuotas.php create mode 100644 app/resources/routes/ventas/precios.php create mode 100644 app/resources/views/guest.blade.php create mode 100644 app/resources/views/home.blade.php create mode 100644 app/resources/views/home/cierres_vigentes.blade.php create mode 100644 app/resources/views/home/cuotas_por_vencer.blade.php create mode 100644 app/resources/views/layout/base.blade.php create mode 100644 app/resources/views/layout/body.blade.php create mode 100644 app/resources/views/layout/body/footer.blade.php create mode 100644 app/resources/views/layout/body/header.blade.php create mode 100644 app/resources/views/layout/body/header/menu.blade.php create mode 100644 app/resources/views/layout/body/header/menu/contabilidad.blade.php create mode 100644 app/resources/views/layout/body/header/menu/guest.blade.php create mode 100644 app/resources/views/layout/body/header/menu/herramientas.blade.php create mode 100644 app/resources/views/layout/body/header/menu/inmobiliarias.blade.php create mode 100644 app/resources/views/layout/body/header/menu/operadores.blade.php create mode 100644 app/resources/views/layout/body/header/menu/proyectos.blade.php create mode 100644 app/resources/views/layout/body/header/menu/search.blade.php create mode 100644 app/resources/views/layout/body/header/menu/user.blade.php create mode 100644 app/resources/views/layout/body/header/menu/ventas.blade.php create mode 100644 app/resources/views/layout/body/scripts.blade.php create mode 100644 app/resources/views/layout/body/scripts/datatables.blade.php create mode 100644 app/resources/views/layout/head.blade.php create mode 100644 app/resources/views/layout/head/styles.blade.php create mode 100644 app/resources/views/layout/head/styles/datatables.blade.php create mode 100644 app/resources/views/login/form.blade.php create mode 100644 app/resources/views/ventas/cuotas/pendientes.blade.php create mode 100644 app/resources/views/ventas/precios/list.blade.php create mode 100644 app/setup/app.php create mode 100644 app/setup/composer.php create mode 100644 app/setup/middlewares/01_auth.php create mode 100644 app/setup/middlewares/98_logs.php create mode 100644 app/setup/middlewares/99_routes.php create mode 100644 app/setup/settings/env.php create mode 100644 app/setup/settings/folders.php create mode 100644 app/setup/settings/urls.php create mode 100644 app/setup/setups/database.php create mode 100644 app/setup/setups/logs.php create mode 100644 app/setup/setups/middlewares.php create mode 100644 app/setup/setups/services.php create mode 100644 app/setup/setups/views.php create mode 100644 app/src/Controller/Base.php create mode 100644 app/src/Controller/Login.php create mode 100644 app/src/Controller/Proyectos.php create mode 100644 app/src/Controller/Users.php create mode 100644 app/src/Controller/Ventas/Cuotas.php create mode 100644 app/src/Controller/Ventas/Precios.php create mode 100644 app/src/Middleware/Authentication.php create mode 100644 app/src/Model/Banco.php create mode 100644 app/src/Model/Comuna.php create mode 100644 app/src/Model/Direccion.php create mode 100644 app/src/Model/Inmobiliaria.php create mode 100644 app/src/Model/Inmobiliaria/TipoSociedad.php create mode 100644 app/src/Model/Login.php create mode 100644 app/src/Model/Provincia.php create mode 100644 app/src/Model/Proyecto.php create mode 100644 app/src/Model/Proyecto/ProyectoTipoUnidad.php create mode 100644 app/src/Model/Proyecto/Superficie.php create mode 100644 app/src/Model/Proyecto/Terreno.php create mode 100644 app/src/Model/Region.php create mode 100644 app/src/Model/Role.php create mode 100644 app/src/Model/Tipo.php create mode 100644 app/src/Model/User.php create mode 100644 app/src/Model/Venta/Cierre.php create mode 100644 app/src/Model/Venta/Cuota.php create mode 100644 app/src/Model/Venta/Datos.php create mode 100644 app/src/Model/Venta/EstadoPago.php create mode 100644 app/src/Model/Venta/EstadoPrecio.php create mode 100644 app/src/Model/Venta/Pago.php create mode 100644 app/src/Model/Venta/Pie.php create mode 100644 app/src/Model/Venta/Precio.php create mode 100644 app/src/Model/Venta/Propietario.php create mode 100644 app/src/Model/Venta/TipoEstadoPago.php create mode 100644 app/src/Model/Venta/TipoEstadoPrecio.php create mode 100644 app/src/Model/Venta/TipoPago.php create mode 100644 app/src/Model/Venta/TipoUnidad.php create mode 100644 app/src/Model/Venta/Unidad.php create mode 100644 app/src/Repository/Banco.php create mode 100644 app/src/Repository/Comuna.php create mode 100644 app/src/Repository/Direccion.php create mode 100644 app/src/Repository/Inmobiliaria.php create mode 100644 app/src/Repository/Inmobiliaria/TipoSociedad.php create mode 100644 app/src/Repository/Login.php create mode 100644 app/src/Repository/Provincia.php create mode 100644 app/src/Repository/Proyecto.php create mode 100644 app/src/Repository/Proyecto/ProyectoTipoUnidad.php create mode 100644 app/src/Repository/Region.php create mode 100644 app/src/Repository/User.php create mode 100644 app/src/Repository/Venta/Cierre.php create mode 100644 app/src/Repository/Venta/Cuota.php create mode 100644 app/src/Repository/Venta/EstadoPago.php create mode 100644 app/src/Repository/Venta/EstadoPrecio.php create mode 100644 app/src/Repository/Venta/Pago.php create mode 100644 app/src/Repository/Venta/Pie.php create mode 100644 app/src/Repository/Venta/Precio.php create mode 100644 app/src/Repository/Venta/Propietario.php create mode 100644 app/src/Repository/Venta/TipoEstadoPago.php create mode 100644 app/src/Repository/Venta/TipoEstadoPrecio.php create mode 100644 app/src/Repository/Venta/TipoPago.php create mode 100644 app/src/Repository/Venta/TipoUnidad.php create mode 100644 app/src/Repository/Venta/Unidad.php create mode 100644 app/src/Service/Format.php create mode 100644 app/src/Service/Login.php create mode 100644 app/src/Service/Ventas/Pago.php create mode 100644 app/src/Service/Ventas/Precio.php diff --git a/app/common/Alias/View.php b/app/common/Alias/View.php new file mode 100644 index 0000000..80df0a9 --- /dev/null +++ b/app/common/Alias/View.php @@ -0,0 +1,9 @@ + $this->id + ]; + } +} diff --git a/app/common/Ideal/Repository.php b/app/common/Ideal/Repository.php new file mode 100644 index 0000000..9b4acc7 --- /dev/null +++ b/app/common/Ideal/Repository.php @@ -0,0 +1,126 @@ +table; + } + public function setTable(string $table): Repository + { + $this->table = $table; + return $this; + } + + public function load(array $data_row): Model + { + $model = $this->create($data_row); + $model->{$this->getKey()} = $data_row[$this->getKey()]; + return $model; + } + + public function remove(Model $model): void + { + $query = "DELETE FROM `{$this->getTable()}` WHERE `{$this->getKey()}` = ?"; + $this->connection->execute($query, [$model->getId()]); + } + + public function fetchById(int $id): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `{$this->getKey()}` = ?"; + return $this->fetchOne($query, [$id]); + } + public function fetchAll(): array + { + $query = "SELECT * FROM `{$this->getTable()}`"; + return $this->fetchMany($query); + } + + protected function getKey(): string + { + return 'id'; + } + protected function parseData(Define\Model $model, ?array $data, array $data_map): Define\Model + { + if ($data === null) { + return $model; + } + foreach ($data_map as $column => $settings) { + if (isset($data[$column])) { + $property = $column; + if (isset($settings['property'])) { + $property = $settings['property']; + } + $value = $data[$column]; + if (isset($settings['function'])) { + $value = $settings['function']($data); + } + $model->{$property} = $value; + } + } + return $model; + } + protected function saveNew(array $columns, array $values): int + { + $columns_string = implode(', ', array_map(function($column) {return "`{$column}`";}, $columns)); + $columns_questions = implode(', ', array_fill(0, count($columns), '?')); + $query = "INSERT INTO `{$this->getTable()}` ({$columns_string}) VALUES ($columns_questions)"; + $this->connection->execute($query, $values); + return $this->connection->getPDO()->lastInsertId(); + } + protected function update(Model $model, array $columns, array $data): Define\Model + { + $changes = []; + $values = []; + foreach ($columns as $column) { + if (isset($data[$column])) { + $changes []= $column; + $values []= $data[$column]; + } + } + if (count($changes) === 0) { + return $model; + } + $columns_string = implode(', ', array_map(function($property) {return "`{$property}` = ?";}, $changes)); + $query = "UPDATE `{$this->getTable()}` SET {$columns_string} WHERE `{$this->getKey()}` = ?"; + $values []= $model->id; + $this->connection->execute($query, $values); + $id = $model->id; + $model = $this->create($data); + $model->id = $id; + return $model; + } + protected function fetchOne(string $query, ?array $data = null): Define\Model + { + $result = $this->connection->execute($query, $data)->fetch(PDO::FETCH_ASSOC); + if ($result === false) { + throw new EmptyResult($query); + } + return $this->load($result); + } + protected function fetchMany(string $query, ?array $data = null): array + { + $results = $this->connection->execute($query, $data)->fetchAll(PDO::FETCH_ASSOC); + if ($results === false) { + throw new EmptyResult($query); + } + return array_map([$this, 'load'], $results); + } + protected function fetchAsArray(string $query, ?array $data = null): array + { + $results = $this->connection->execute($query, $data)->fetchAll(PDO::FETCH_ASSOC); + if ($results === false) { + throw new EmptyResult($query); + } + return $results; + } +} diff --git a/app/common/Implement/Connection.php b/app/common/Implement/Connection.php new file mode 100644 index 0000000..bcb6eab --- /dev/null +++ b/app/common/Implement/Connection.php @@ -0,0 +1,61 @@ +connection)) { + if ($this->database->needsUser()) { + $this->connection = new PDO($this->database->getDSN(), $this->database->user, $this->database->password); + } else { + $this->connection = new PDO($this->database->getDSN()); + } + } + return $this; + } + public function getPDO(): PDO + { + $this->connect(); + return $this->connection; + } + + public function query(string $query): PDOStatement + { + $this->connect(); + $statement = $this->connection->query($query); + if ($statement === false) { + throw new PDOException("Query failed: '{$query}'"); + } + return $statement; + } + public function prepare(string $query): PDOStatement + { + $this->connect(); + $statement = $this->connection->prepare($query); + if ($statement === false) { + throw new PDOException("Query failed: '{$query}'"); + } + return $statement; + } + public function execute(string $query, ?array $data = null): PDOStatement + { + if ($data === null) { + return $this->query($query); + } + $statement = $this->prepare($query); + $status = $statement->execute($data); + if ($status === false) { + throw new PDOException("Query could not be executed: '{$query}'"); + } + return $statement; + } +} diff --git a/app/common/Implement/Database/MySQL.php b/app/common/Implement/Database/MySQL.php new file mode 100644 index 0000000..b2bca28 --- /dev/null +++ b/app/common/Implement/Database/MySQL.php @@ -0,0 +1,24 @@ +host};dbname={$this->name}"; + if ($this->port !== 3306) { + $dsn .= ";port={$this->port}"; + } + return $dsn; + } + public function needsUser(): bool + { + return true; + } +} diff --git a/app/common/Implement/Exception/EmptyResult.php b/app/common/Implement/Exception/EmptyResult.php new file mode 100644 index 0000000..2a8e3ef --- /dev/null +++ b/app/common/Implement/Exception/EmptyResult.php @@ -0,0 +1,15 @@ +KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0009ENkl#t4#a7ZeDA&=~8sYk6z!e%rZS)9(N8>BZJ2)${&& zz8{|FYe=Hpw+Xx8Km`XX8c+b0tI(gfK34(LmhU-VRi0CEKOc_Qu(Pf}{6^JV$=Ud8 z4*o3(T-8C{%zSI-m-gGarG0jG?mJx7VXrA{5&ALf`eC~0copEk0afl6+nT(p#cYS? z$VF<7UBX$+wPQ~Y+otQ8)Piexx?nyMaw-GhTyq-WbnUg1KYxOAqsMdNB94ap0r2)t z@Wr!_wY+mR|5c6OL+vt2WsSgL0+o~Y)L6fxvCZQ-@zbgq;B@!bJbyXBqTB>L6>R-; zah0HQp84Nia6Whh*PfnLW2sc;@Zsb1_P&ol^bp<}59LxBuy5~76wV@z9M}H|HVGnv zdO=jMLBY_YT3`RTC25(!3{}r1)mZn@=?^%#zn!gHn;00l%*@OJ_q5#2mdy>c?`k6w zjxabl$Y*_n1OnkT0u8vW)Ri{{es#TgQj!#jY*t4{r(&_anwpxBB*jNYBuQ_aIxDER zB(0PB=4ZEZ+pcJGb>C017`6@Ya< zo_=x%iOCd0!y|-(URqlp=H}88iA0jl&V7uHO;V{CbRK*e(-~e1umu!~#A4gY<%)E7 zf60CKHnG0m$6vVup(gRPtA_!_?VR=Sh;4Xb1k4usk7P6A0 zuCCW*7>$-B3`vyEhsutIr%b}zNv1PgPiNV+t(m2{JX;R*5sr9i+!)2@^D{PY{g4tn`r6kVN@H^F+l;MRns=-W?8r%MZyE^J!?gv=eK75y-B5ubXfr>NZWR^z2a6 a-va=T>#R+iEvaAt0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000LENkl3&26~}+~Z8Kxzu|4Ar%w`CT?P6gQJ9ceIffN$dv{FN)s%Zr@ zN@y#Qh7V1kfhweGL|ZA9Kuw}bO{A143y72^h7z2xv{<1-OEEUsf!Y|b9Wb`>KHGcm z_QTj_29K9=q|waWckccF-`VatLP`mMxqksNBEeRXCP-6&Es!Q)ML*IKV2bGP==yuo z6b2SgN)s@n`YEcU<&MhQ<7wo%EQ(SrPT$cfN;1h(C7<8Xf9-Fp-3g#@e=-9&iV)vN zi1ondfM+6Q2_!2L0qzgF9JOZ$j2)xT?uWnlUsDu;WK!@Zyxt3mpKl0@L?nvDWQ zhq@g8YayjF%W3u9FM)M{AKh6XyRL)bLKw-|x2}TjTnPAqCt^3GYLe!F`nbPO_YauD zm!(T=iFrQ+wu3l=rn~IcI?&v>w$|W!;WI>X23B(&Xqtvl4kCHkp-9}Cq-jvw@m`1P z6A8|AU1CK;PK+L^O%wbr6!mE=8@r_eC9f3c=8qAnGXY3`)z9#2Phk500l7ZN6uV|b ztmIpRn$;0ewx>F5oXQw@oQNs1V3suHK`<&9?8my+hF0}7jvu_0kY(_XTX5FDLx;>| zeRT%^>e5L|wh$w+5F0*)x6vHf?TrY_g>!Q?s_$Eq`stm0AF=7BQ{b3OPFfh#n%cLCEbu#o z@;0i9eWL*-bpg&TXQu?;w(Vs$ZPxxgag=Z*HL}zFrM5G z`iuz=7#J92GgplaEhOX;`!{O*rLI8qLsGp(1W(>z1yk0MnNNBPw z5a@Gea^!C(m^;6c1$o6h^UTlVG>)IBWx>J{e7;pQHGN6W%p7d1gC<(z z-8hfOQ;N^`0HI(Q2$7qcg~#K@<8j9wIeT^%ON$Fxv}hjd*8LDi(2cGulhlF!0Sfc; zC@C)kX42gqpsDf7*yD!!MgZ2VsUkDeOaH(ipfNNQ7}I&-#b2>%^>=vXmAzwrf9$bu zP+7U0zTOUmn1p}~MRT~MI3EaMMmo88@h*Xjms$a+ta=b&%;&Lk>{u-)PgJvO*Yg}Y z^vT%z;>Ec@kk~{SN5C8@VauyZN2<8Zg@zUYnwziD)N}*LX64E#0o%665f~l{kL>l5 zl|5^0{c39`U=W)OI{{4*wo`>U--*avs2zDhSELE6QT@Z4@C;lsUZq z`Ujjkb(X%qAwZxA0Ro1GdO3HliN?l@qlU(KLsE>5MOJnekUc&+7OQQy({Mi#gL0000u literal 0 HcmV?d00001 diff --git a/app/public/assets/images/Isotipo 64.png b/app/public/assets/images/Isotipo 64.png new file mode 100644 index 0000000000000000000000000000000000000000..8b503d19529984b3b75b81357d953ac44e0bbfd6 GIT binary patch literal 6361 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 bI{n`OS|NRY86>_D00000NkvXXu0mjfU{o%_ literal 0 HcmV?d00001 diff --git a/app/public/assets/images/logo_cabezal.png b/app/public/assets/images/logo_cabezal.png new file mode 100644 index 0000000000000000000000000000000000000000..29fd0595347b55901e215dc2a07ac5e75426a69e GIT binary patch literal 23004 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 diff --git a/app/public/robots.txt b/app/public/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/app/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/app/resources/routes/01_api.php b/app/resources/routes/01_api.php new file mode 100644 index 0000000..a8f19fa --- /dev/null +++ b/app/resources/routes/01_api.php @@ -0,0 +1,13 @@ +group('/api', function($app) { + $folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'api']); + if (file_exists($folder)) { + $files = new FilesystemIterator($folder); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + include_once $file->getRealPath(); + } + } +}); diff --git a/app/resources/routes/03_proyectos.php b/app/resources/routes/03_proyectos.php new file mode 100644 index 0000000..02303aa --- /dev/null +++ b/app/resources/routes/03_proyectos.php @@ -0,0 +1,6 @@ +group('/proyectos', function($app) { + $app->get('[/]', Proyectos::class); +}); diff --git a/app/resources/routes/04_ventas.php b/app/resources/routes/04_ventas.php new file mode 100644 index 0000000..93a94c7 --- /dev/null +++ b/app/resources/routes/04_ventas.php @@ -0,0 +1,10 @@ +group('/ventas', function($app) { + $files = new FilesystemIterator(implode(DIRECTORY_SEPARATOR, [__DIR__, 'ventas'])); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + include_once $file->getRealPath(); + } +}); diff --git a/app/resources/routes/98_login.php b/app/resources/routes/98_login.php new file mode 100644 index 0000000..4521062 --- /dev/null +++ b/app/resources/routes/98_login.php @@ -0,0 +1,8 @@ +group('/login', function($app) { + $app->post('[/]', [Login::class, 'login']); + $app->get('[/]', [Login::class, 'form']); +}); +$app->get('/logout', [Login::class, 'logout']); diff --git a/app/resources/routes/99_base.php b/app/resources/routes/99_base.php new file mode 100644 index 0000000..c8c764e --- /dev/null +++ b/app/resources/routes/99_base.php @@ -0,0 +1,4 @@ +get('[/]', Base::class); diff --git a/app/resources/routes/api/proyectos.php b/app/resources/routes/api/proyectos.php new file mode 100644 index 0000000..a0bf995 --- /dev/null +++ b/app/resources/routes/api/proyectos.php @@ -0,0 +1,6 @@ +group('/proyectos', function($app) { + $app->get('[/]', [Proyectos::class, 'list']); +}); diff --git a/app/resources/routes/api/ventas.php b/app/resources/routes/api/ventas.php new file mode 100644 index 0000000..361f498 --- /dev/null +++ b/app/resources/routes/api/ventas.php @@ -0,0 +1,13 @@ +group('/ventas', function($app) { + $folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'ventas']); + if (file_exists($folder)) { + $files = new FilesystemIterator($folder); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + include_once $file->getRealPath(); + } + } +}); diff --git a/app/resources/routes/api/ventas/precios.php b/app/resources/routes/api/ventas/precios.php new file mode 100644 index 0000000..0ad9ba4 --- /dev/null +++ b/app/resources/routes/api/ventas/precios.php @@ -0,0 +1,6 @@ +group('/precios', function($app) { + $app->post('[/]', [Precios::class, 'proyecto']); +}); diff --git a/app/resources/routes/ventas/cuotas.php b/app/resources/routes/ventas/cuotas.php new file mode 100644 index 0000000..981dccd --- /dev/null +++ b/app/resources/routes/ventas/cuotas.php @@ -0,0 +1,9 @@ +group('/cuotas', function($app) { + $app->get('/pendientes[/]', [Cuotas::class, 'pendientes']); +}); +$app->group('/cuota', function($app) { + $app->post('/depositar[/]', [Cuotas::class, 'depositar']); +}); diff --git a/app/resources/routes/ventas/precios.php b/app/resources/routes/ventas/precios.php new file mode 100644 index 0000000..c035216 --- /dev/null +++ b/app/resources/routes/ventas/precios.php @@ -0,0 +1,6 @@ +group('/precios', function($app) { + $app->get('[/]', Precios::class); +}); diff --git a/app/resources/views/guest.blade.php b/app/resources/views/guest.blade.php new file mode 100644 index 0000000..8acdfe6 --- /dev/null +++ b/app/resources/views/guest.blade.php @@ -0,0 +1,7 @@ +@extends('layout.base') + +@section('page_content') +
+ Bienvenid@ a Incoviba +
+@endsection diff --git a/app/resources/views/home.blade.php b/app/resources/views/home.blade.php new file mode 100644 index 0000000..06597bf --- /dev/null +++ b/app/resources/views/home.blade.php @@ -0,0 +1,26 @@ +@extends('layout.base') + +@section('page_content') +
+

Bienvenid@ {{$user->name}}

+
+ @if ($cuotas_hoy > 0) + Existe{{$cuotas_hoy > 1 ? 'n' : ''}} {{$cuotas_hoy}} deposito{{$cuotas_hoy > 1 ? 's' : ''}} para hoy. +
+ @endif + @if ($cuotas_pendientes > 0) + + Existe{{$cuotas_pendientes > 1 ? 'n' : ''}} {{$cuotas_pendientes}} cuota{{$cuotas_pendientes > 1 ? 's' : ''}} pendiente{{$cuotas_pendientes > 1 ? 's' : ''}}. + + @endif +
+
+
+ @include('home.cuotas_por_vencer') +
+
+ @include('home.cierres_vigentes') +
+
+
+@endsection diff --git a/app/resources/views/home/cierres_vigentes.blade.php b/app/resources/views/home/cierres_vigentes.blade.php new file mode 100644 index 0000000..fe974e0 --- /dev/null +++ b/app/resources/views/home/cierres_vigentes.blade.php @@ -0,0 +1,24 @@ +

Cierres Vigentes

+
+ @foreach($cierres_vigentes as $proyecto => $estados) +
+
+
+ {{$proyecto}} [{{$estados['total']}}] +
+
+
Promesados
+
{{$estados['promesados']}}
+
+
+
Pendientes
+
{{$estados['pendientes']}}
+
+
+
Rechazados
+
{{$estados['rechazados']}}
+
+
+
+ @endforeach +
diff --git a/app/resources/views/home/cuotas_por_vencer.blade.php b/app/resources/views/home/cuotas_por_vencer.blade.php new file mode 100644 index 0000000..a0df444 --- /dev/null +++ b/app/resources/views/home/cuotas_por_vencer.blade.php @@ -0,0 +1,22 @@ +

Cuotas Por Vencer

+
+ @foreach ($cuotas_por_vencer as $date => $proyectos) +
+
+
+ {{$format->localDate($date, "EEE. dd 'de' MMMM 'de' yyyy", true)}} +
+ @foreach ($proyectos as $proyecto => $cuotas) +
+
+ + {{$proyecto}} + +
+
{{$cuotas}}
+
+ @endforeach +
+
+ @endforeach +
diff --git a/app/resources/views/layout/base.blade.php b/app/resources/views/layout/base.blade.php new file mode 100644 index 0000000..9deb8c1 --- /dev/null +++ b/app/resources/views/layout/base.blade.php @@ -0,0 +1,5 @@ + + +@include('layout.head') +@include('layout.body') + diff --git a/app/resources/views/layout/body.blade.php b/app/resources/views/layout/body.blade.php new file mode 100644 index 0000000..fb0cc48 --- /dev/null +++ b/app/resources/views/layout/body.blade.php @@ -0,0 +1,5 @@ + + @include('layout.body.header') + @yield('page_content') + @include('layout.body.footer') + diff --git a/app/resources/views/layout/body/footer.blade.php b/app/resources/views/layout/body/footer.blade.php new file mode 100644 index 0000000..c3b1007 --- /dev/null +++ b/app/resources/views/layout/body/footer.blade.php @@ -0,0 +1,4 @@ +
+ +
+@include('layout.body.scripts') diff --git a/app/resources/views/layout/body/header.blade.php b/app/resources/views/layout/body/header.blade.php new file mode 100644 index 0000000..48d779d --- /dev/null +++ b/app/resources/views/layout/body/header.blade.php @@ -0,0 +1,7 @@ +
+ + logo + + @include('layout.body.header.menu') +
+
diff --git a/app/resources/views/layout/body/header/menu.blade.php b/app/resources/views/layout/body/header/menu.blade.php new file mode 100644 index 0000000..392dc36 --- /dev/null +++ b/app/resources/views/layout/body/header/menu.blade.php @@ -0,0 +1,17 @@ + diff --git a/app/resources/views/layout/body/header/menu/contabilidad.blade.php b/app/resources/views/layout/body/header/menu/contabilidad.blade.php new file mode 100644 index 0000000..7ed6b0c --- /dev/null +++ b/app/resources/views/layout/body/header/menu/contabilidad.blade.php @@ -0,0 +1,8 @@ + diff --git a/app/resources/views/layout/body/header/menu/guest.blade.php b/app/resources/views/layout/body/header/menu/guest.blade.php new file mode 100644 index 0000000..8c7399b --- /dev/null +++ b/app/resources/views/layout/body/header/menu/guest.blade.php @@ -0,0 +1 @@ +Ingresar diff --git a/app/resources/views/layout/body/header/menu/herramientas.blade.php b/app/resources/views/layout/body/header/menu/herramientas.blade.php new file mode 100644 index 0000000..45b0b52 --- /dev/null +++ b/app/resources/views/layout/body/header/menu/herramientas.blade.php @@ -0,0 +1,7 @@ + diff --git a/app/resources/views/layout/body/header/menu/inmobiliarias.blade.php b/app/resources/views/layout/body/header/menu/inmobiliarias.blade.php new file mode 100644 index 0000000..9aa161c --- /dev/null +++ b/app/resources/views/layout/body/header/menu/inmobiliarias.blade.php @@ -0,0 +1 @@ +Inmobiliarias diff --git a/app/resources/views/layout/body/header/menu/operadores.blade.php b/app/resources/views/layout/body/header/menu/operadores.blade.php new file mode 100644 index 0000000..c30b9c0 --- /dev/null +++ b/app/resources/views/layout/body/header/menu/operadores.blade.php @@ -0,0 +1,9 @@ + diff --git a/app/resources/views/layout/body/header/menu/proyectos.blade.php b/app/resources/views/layout/body/header/menu/proyectos.blade.php new file mode 100644 index 0000000..c72eb69 --- /dev/null +++ b/app/resources/views/layout/body/header/menu/proyectos.blade.php @@ -0,0 +1,8 @@ + diff --git a/app/resources/views/layout/body/header/menu/search.blade.php b/app/resources/views/layout/body/header/menu/search.blade.php new file mode 100644 index 0000000..cd4315e --- /dev/null +++ b/app/resources/views/layout/body/header/menu/search.blade.php @@ -0,0 +1 @@ + diff --git a/app/resources/views/layout/body/header/menu/user.blade.php b/app/resources/views/layout/body/header/menu/user.blade.php new file mode 100644 index 0000000..e2bd013 --- /dev/null +++ b/app/resources/views/layout/body/header/menu/user.blade.php @@ -0,0 +1,31 @@ + + +@push('page_scripts') + +@endpush diff --git a/app/resources/views/layout/body/header/menu/ventas.blade.php b/app/resources/views/layout/body/header/menu/ventas.blade.php new file mode 100644 index 0000000..7d669cc --- /dev/null +++ b/app/resources/views/layout/body/header/menu/ventas.blade.php @@ -0,0 +1,41 @@ + diff --git a/app/resources/views/layout/body/scripts.blade.php b/app/resources/views/layout/body/scripts.blade.php new file mode 100644 index 0000000..9b8faa6 --- /dev/null +++ b/app/resources/views/layout/body/scripts.blade.php @@ -0,0 +1,4 @@ + + + +@stack('page_scripts') diff --git a/app/resources/views/layout/body/scripts/datatables.blade.php b/app/resources/views/layout/body/scripts/datatables.blade.php new file mode 100644 index 0000000..980cdf7 --- /dev/null +++ b/app/resources/views/layout/body/scripts/datatables.blade.php @@ -0,0 +1,4 @@ +@push('page_scripts') + + +@endpush diff --git a/app/resources/views/layout/head.blade.php b/app/resources/views/layout/head.blade.php new file mode 100644 index 0000000..98233b9 --- /dev/null +++ b/app/resources/views/layout/head.blade.php @@ -0,0 +1,10 @@ + + + @hasSection('page_title') + Incoviba - @yield('page_title') + @else + Incoviba + @endif + + @include('layout.head.styles') + diff --git a/app/resources/views/layout/head/styles.blade.php b/app/resources/views/layout/head/styles.blade.php new file mode 100644 index 0000000..60ed4f9 --- /dev/null +++ b/app/resources/views/layout/head/styles.blade.php @@ -0,0 +1,3 @@ + + +@stack('page_styles') diff --git a/app/resources/views/layout/head/styles/datatables.blade.php b/app/resources/views/layout/head/styles/datatables.blade.php new file mode 100644 index 0000000..a5dcca3 --- /dev/null +++ b/app/resources/views/layout/head/styles/datatables.blade.php @@ -0,0 +1,3 @@ +@push('page_styles') + +@endpush diff --git a/app/resources/views/login/form.blade.php b/app/resources/views/login/form.blade.php new file mode 100644 index 0000000..31e38ff --- /dev/null +++ b/app/resources/views/login/form.blade.php @@ -0,0 +1,60 @@ +@extends('layout.base') + +@section('page_content') +
+
+
+ + +
+
+ + +
+ +
+
+@endsection + +@push('page_scripts') + +@endpush diff --git a/app/resources/views/ventas/cuotas/pendientes.blade.php b/app/resources/views/ventas/cuotas/pendientes.blade.php new file mode 100644 index 0000000..00a4ba1 --- /dev/null +++ b/app/resources/views/ventas/cuotas/pendientes.blade.php @@ -0,0 +1,108 @@ +@extends('layout.base') + +@section('page_content') +
+

Cuotas Pendientes

+
+
Total
+
{{count($cuotas_pendientes)}}
+
{{$format->pesos(array_reduce($cuotas_pendientes, function($sum, $cuota) {return $sum + $cuota['Valor'];}, 0))}}
+
+ + + + + + + + + + + + + + + + + + @foreach($cuotas_pendientes as $cuota) + + + + + + + + + + + + + + @endforeach + +
ProyectoDepartamentoDepartamento SortValorDíaCuotaPropietarioBancoFecha Cheque (Días)Fecha ISODepositar
{{$cuota['Proyecto']}} + + {{$cuota['Departamento']}} + + {{str_pad($cuota['Departamento'], 4, '0', STR_PAD_LEFT)}}{{$format->pesos($cuota['Valor'])}}{{$cuota['Dia']}}{{$cuota['Numero']}}{{$cuota['Propietario']}}{{$cuota['Banco']}}{{$cuota['Fecha Cheque']}} ({{$cuota['Vencida']}}){{$cuota['Fecha ISO']}} + +
+
+@endsection + +@include('layout.head.styles.datatables') +@include('layout.body.scripts.datatables') + +@push('page_scripts') + +@endpush diff --git a/app/resources/views/ventas/precios/list.blade.php b/app/resources/views/ventas/precios/list.blade.php new file mode 100644 index 0000000..101e4de --- /dev/null +++ b/app/resources/views/ventas/precios/list.blade.php @@ -0,0 +1,676 @@ +@extends('layout.base') + + +@section('page_title') + Precios - Listado +@endsection + +@section('page_content') +
+

Listado de Precios

+
+

+
+
+
+
+ + +
+ +
+
+

+ +
+
+
+ +@endsection + +@push('page_styles') + +@endpush + +@push('page_scripts') + +@endpush diff --git a/app/setup/app.php b/app/setup/app.php new file mode 100644 index 0000000..a17667e --- /dev/null +++ b/app/setup/app.php @@ -0,0 +1,45 @@ +isDir()) { + continue; + } + $builder->addDefinitions($file->getRealPath()); + } + } + $app = Bridge::create($builder->build()); + $folder = implode(DIRECTORY_SEPARATOR, [ + __DIR__, + 'middlewares' + ]); + if (file_exists($folder)) { + $files = new FilesystemIterator($folder); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + require_once $file->getRealPath(); + } + } + return $app; +} +return buildApp(); diff --git a/app/setup/composer.php b/app/setup/composer.php new file mode 100644 index 0000000..b451f96 --- /dev/null +++ b/app/setup/composer.php @@ -0,0 +1,6 @@ +add($app->getContainer()->get(Incoviba\Middleware\Authentication::class)); diff --git a/app/setup/middlewares/98_logs.php b/app/setup/middlewares/98_logs.php new file mode 100644 index 0000000..b81e521 --- /dev/null +++ b/app/setup/middlewares/98_logs.php @@ -0,0 +1,2 @@ +getContainer()->get(Psr\Log\LoggerInterface::class)); diff --git a/app/setup/middlewares/99_routes.php b/app/setup/middlewares/99_routes.php new file mode 100644 index 0000000..a08c17c --- /dev/null +++ b/app/setup/middlewares/99_routes.php @@ -0,0 +1,12 @@ +getContainer()->get('folders')->get('routes'); + $files = new FilesystemIterator($folder); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + include_once $file->getRealPath(); + } +} +loadRoutes($app); diff --git a/app/setup/settings/env.php b/app/setup/settings/env.php new file mode 100644 index 0000000..580b775 --- /dev/null +++ b/app/setup/settings/env.php @@ -0,0 +1,2 @@ + function() { + return new DI\Container([ + 'base' => dirname(__FILE__, 3), + 'resources' => DI\String('{base}/resources'), + 'routes' => DI\String('{resources}/routes'), + 'cache' => DI\String('{base}/cache'), + 'templates' => DI\String('{resources}/views') + ]); + } +]; diff --git a/app/setup/settings/urls.php b/app/setup/settings/urls.php new file mode 100644 index 0000000..d07f846 --- /dev/null +++ b/app/setup/settings/urls.php @@ -0,0 +1,21 @@ + function() { + $urls = [ + 'base' => $_ENV['APP_URL'] ?? '', + ]; + $urls['api'] = implode('/', [ + $urls['base'], + 'api' + ]); + $urls['assets'] = implode('/', [ + $urls['base'], + 'assets' + ]); + $urls['images'] = implode('/', [ + $urls['assets'], + 'images' + ]); + return (object) $urls; + } +]; diff --git a/app/setup/setups/database.php b/app/setup/setups/database.php new file mode 100644 index 0000000..3cfd071 --- /dev/null +++ b/app/setup/setups/database.php @@ -0,0 +1,18 @@ + function(ContainerInterface $container) { + return new Incoviba\Common\Implement\Database\MySQL( + $container->has('MYSQL_HOST') ? $container->get('MYSQL_HOST') : 'db', + $container->get('MYSQL_DATABASE'), + $container->get('MYSQL_USER'), + $container->get('MYSQL_PASSWORD') + ); + }, + Incoviba\Common\Define\Connection::class => function(ContainerInterface $container) { + return new Incoviba\Common\Implement\Connection( + $container->get(Incoviba\Common\Define\Database::class) + ); + } +]; diff --git a/app/setup/setups/logs.php b/app/setup/setups/logs.php new file mode 100644 index 0000000..0ccd933 --- /dev/null +++ b/app/setup/setups/logs.php @@ -0,0 +1,32 @@ + function(ContainerInterface $container) { + return new Monolog\Logger('incoviba', [ + new Monolog\Handler\FilterHandler( + (new Monolog\Handler\RotatingFileHandler('/logs/debug.log')) + ->setFormatter(new Monolog\Formatter\LineFormatter(null, null, false, false, true)), + Monolog\Level::Debug, + Monolog\Level::Notice + ), + new Monolog\Handler\FilterHandler( + (new Monolog\Handler\RotatingFileHandler('/logs/error.log')) + ->setFormatter(new Monolog\Formatter\LineFormatter(null, null, false, false, true)), + Monolog\Level::Warning, + Monolog\Level::Error + ), + new Monolog\Handler\FilterHandler( + (new Monolog\Handler\RotatingFileHandler('/logs/critical.log')) + ->setFormatter(new Monolog\Formatter\LineFormatter(null, null, false, false, true)), + Monolog\Level::Critical + ) + ], [ + $container->get(Monolog\Processor\PsrLogMessageProcessor::class), + $container->get(Monolog\Processor\WebProcessor::class), + $container->get(Monolog\Processor\IntrospectionProcessor::class), + $container->get(Monolog\Processor\MemoryUsageProcessor::class), + $container->get(Monolog\Processor\MemoryPeakUsageProcessor::class) + ]); + } +]; diff --git a/app/setup/setups/middlewares.php b/app/setup/setups/middlewares.php new file mode 100644 index 0000000..5c5f23f --- /dev/null +++ b/app/setup/setups/middlewares.php @@ -0,0 +1,15 @@ + function(ContainerInterface $container) { + return $container->get(Nyholm\Psr7\Factory\Psr17Factory::class); + }, + Incoviba\Middleware\Authentication::class => function(ContainerInterface $container) { + return new Incoviba\Middleware\Authentication( + $container->get(Psr\Http\Message\ResponseFactoryInterface::class), + $container->get(Incoviba\Service\Login::class), + implode('/', [$container->get('APP_URL'), 'login']) + ); + } +]; diff --git a/app/setup/setups/services.php b/app/setup/setups/services.php new file mode 100644 index 0000000..3f4906f --- /dev/null +++ b/app/setup/setups/services.php @@ -0,0 +1,14 @@ + function(ContainerInterface $container) { + return new Incoviba\Service\Login( + $container->get(Incoviba\Repository\Login::class), + $container->get('COOKIE_NAME'), + $container->get('MAX_LOGIN_HOURS'), + $container->has('COOKIE_DOMAIN') ? $container->get('COOKIE_DOMAIN') : '', + $container->has('COOKIE_PATH') ? $container->get('COOKIE_PATH') : '' + ); + } +]; diff --git a/app/setup/setups/views.php b/app/setup/setups/views.php new file mode 100644 index 0000000..41cf638 --- /dev/null +++ b/app/setup/setups/views.php @@ -0,0 +1,23 @@ + function(ContainerInterface $container) { + $folders = $container->get('folders'); + $global_variables = [ + 'urls' => $container->get('urls'), + 'money_url' => '', + 'login' => $container->get(Incoviba\Service\Login::class), + 'format' => $container->get(Incoviba\Service\Format::class), + ]; + if ($global_variables['login']->isIn()) { + $global_variables['user'] = $global_variables['login']->getUser(); + } + return new Incoviba\Common\Alias\View( + $folders->get('templates'), + $folders->get('cache'), + null, + $global_variables + ); + } +]; diff --git a/app/src/Controller/Base.php b/app/src/Controller/Base.php new file mode 100644 index 0000000..2cd225d --- /dev/null +++ b/app/src/Controller/Base.php @@ -0,0 +1,90 @@ +isIn()) { + return $this->home($response, $view, $cuotaRepository, $cierreRepository); + } + return $this->login($response, $view); + } + + protected function home(ResponseInterface $response, View $view, Repository\Venta\Cuota $cuotaRepository, Repository\Venta\Cierre $cierreRepository): ResponseInterface + { + $cuotas_hoy = count($cuotaRepository->fetchHoy()) ?? 0; + $cuotas_pendientes = count($cuotaRepository->fetchPendientes()) ?? 0; + $cuotas_por_vencer = $this->getCuotasPorVencer($cuotaRepository); + $cierres_vigentes = $this->getCierresVigentes($cierreRepository); + return $view->render($response, 'home', compact('cuotas_hoy', 'cuotas_pendientes', 'cuotas_por_vencer', 'cierres_vigentes')); + } + protected function login(ResponseInterface $response, View $view): ResponseInterface + { + return $view->render($response, 'guest'); + } + protected function getCuotasPorVencer(Repository\Venta\Cuota $cuotaRepository): array + { + $cuotas = $cuotaRepository->fetchDatosPorVencer(); + $output = []; + foreach ($cuotas as $row) { + $fecha = $row['Fecha']; + $date = new DateTimeImmutable($fecha); + if (($weekday = $date->format('N')) > 5) { + $day_diff = 7 - $weekday + 1; + $date = $date->add(new DateInterval("P{$day_diff}D")); + $fecha = $date->format('Y-m-d'); + } + if (!isset($output[$fecha])) { + $output[$fecha] = []; + } + if (!isset($output[$fecha][$row['Proyecto']])) { + $output[$fecha][$row['Proyecto']] = 0; + } + $output[$fecha][$row['Proyecto']] += $row['Cantidad']; + } + foreach ($output as $fecha => $day) { + uksort($day, function($a, $b) { + return strcmp($a, $b); + }); + $output[$fecha] = $day; + } + return $output; + } + protected function getCierresVigentes(Repository\Venta\Cierre $cierreRepository): array + { + $cierres = $cierreRepository->fetchDatosVigentes(); + $output = []; + $estados = [ + 'revisado' => 'pendientes', + 'rechazado' => 'rechazados', + 'aprobado' => 'pendientes', + 'vendido' => 'promesados', + 'abandonado' => 'rechazados', + 'promesado' => 'promesados', + 'resciliado' => 'rechazados' + ]; + foreach ($cierres as $row) { + if (!isset($output[$row['Proyecto']])) { + $output[$row['Proyecto']] = [ + 'promesados' => 0, + 'pendientes' => 0, + 'rechazados' => 0, + 'total' => 0 + ]; + } + $estado = $estados[$row['Estado']]; + $output[$row['Proyecto']][$estado] += $row['Cantidad']; + $output[$row['Proyecto']]['total'] += $row['Cantidad']; + } + return $output; + } +} diff --git a/app/src/Controller/Login.php b/app/src/Controller/Login.php new file mode 100644 index 0000000..70467f5 --- /dev/null +++ b/app/src/Controller/Login.php @@ -0,0 +1,55 @@ +hasHeader('Referer') ? $request->getHeaderLine('Referer') : $view->get('urls')->base; + if ($service->isIn()) { + $redirect_uri = str_replace('/login', '', $redirect_uri); + return $response->withStatus(301)->withHeader('Location', $redirect_uri); + } + if ($request->hasHeader('X-Redirect-URI')) { + $redirect_uri = $request->getHeaderLine('X-Redirect-URI'); + } + return $view->render($response, 'login.form', compact('redirect_uri')); + } + public function login(ServerRequestInterface $request, ResponseInterface $response, Repository\User $userRepository, Service\Login $service): ResponseInterface + { + $body = $request->getParsedBody(); + $user = $userRepository->fetchByName($body['name']); + $output = [ + 'name' => $user->name, + 'login' => false + ]; + if ($user->validate($body['password'])) { + $output['login'] = $service->login($user); + } + $response->getBody()->write(json_encode($output)); + return $response->withHeader('Content-Type', 'application/json'); + } + public function logout(ServerRequestInterface $request, ResponseInterface $response, Repository\Login $loginRepository, Service\Login $service): ResponseInterface + { + $output = [ + 'name' => '', + 'logout' => false + ]; + try { + $user = $service->getUser(); + $output = [ + 'name' => $user->name, + 'logout' => $service->logout($user) + ]; + } catch (PDOException) {} + $response->getBody()->write(json_encode($output)); + return $response->withHeader('Content-Type', 'application/json'); + } +} diff --git a/app/src/Controller/Proyectos.php b/app/src/Controller/Proyectos.php new file mode 100644 index 0000000..9ef8c47 --- /dev/null +++ b/app/src/Controller/Proyectos.php @@ -0,0 +1,21 @@ +render($response, 'proyectos.list'); + } + public function list(ServerRequestInterface $request, ResponseInterface $response, Repository\Proyecto $proyectoRepository): ResponseInterface + { + $proyectos = $proyectoRepository->fetchAllActive(); + $response->getBody()->write(json_encode(['proyectos' => $proyectos, 'total' => count($proyectos)])); + return $response->withHeader('Content-Type', 'application/json'); + } +} diff --git a/app/src/Controller/Users.php b/app/src/Controller/Users.php new file mode 100644 index 0000000..cfa82ff --- /dev/null +++ b/app/src/Controller/Users.php @@ -0,0 +1,11 @@ +fetchPendientes(); + $cuotas_pendientes = []; + $today = new DateTimeImmutable(); + $formatter = new IntlDateFormatter('es_ES'); + $formatter->setPattern('EEEE dd'); + foreach ($cuotas as $cuota) { + $date = new DateTimeImmutable($cuota['fecha']); + $day = clone $date; + $weekday = $date->format('N'); + if ($weekday > 5) { + $diff = 7 - $weekday + 1; + $day = $day->add(new DateInterval("P{$diff}D")); + } + $cuotas_pendientes []= [ + 'id' => $cuota['cuota_id'], + 'venta_id' => $cuota['venta_id'], + 'Proyecto' => $cuota['Proyecto'], + 'Departamento' => $cuota['Departamento'], + 'Valor' => $cuota['Valor'], + 'Dia' => $formatter->format($day), + 'Numero' => $cuota['Numero'], + 'Propietario' => $cuota['Propietario'], + 'Banco' => $cuota['Banco'], + 'Fecha Cheque' => $date->format('d-m-Y'), + 'Vencida' => $today->diff($date)->days, + 'Fecha ISO' => $date->format('Y-m-d') + ]; + } + return $view->render($response, 'ventas.cuotas.pendientes', compact('cuotas_pendientes')); + } + public function depositar(ServerRequestInterface $request, ResponseInterface $response, Repository\Venta\Cuota $cuotaRepository, Service\Ventas\Pago $pagoService): ResponseInterface + { + $body = $request->getBody(); + $json = json_decode($body->getContents()); + $cuota_id = $json->cuota_id; + $cuota = $cuotaRepository->fetchById($cuota_id); + $output = [ + 'cuota_id' => $cuota_id, + 'depositada' => $pagoService->depositar($cuota->pago) + ]; + $response->getBody()->write(json_encode($output)); + return $response->withHeader('Content-Type', 'application/json'); + } +} diff --git a/app/src/Controller/Ventas/Precios.php b/app/src/Controller/Ventas/Precios.php new file mode 100644 index 0000000..c4ef615 --- /dev/null +++ b/app/src/Controller/Ventas/Precios.php @@ -0,0 +1,24 @@ +render($response, 'ventas.precios.list'); + } + public function proyecto(ServerRequestInterface $request, ResponseInterface $response, Service\Ventas\Precio $precioService): ResponseInterface + { + $body = $request->getBody(); + $json = json_decode($body->getContents()); + $proyecto_id = $json->proyecto_id; + $precios = $precioService->getByProyecto($proyecto_id); + $response->getBody()->write(json_encode(['precios' => $precios, 'total' => count($precios)])); + return $response->withHeader('Content-Type', 'application/json'); + } +} diff --git a/app/src/Middleware/Authentication.php b/app/src/Middleware/Authentication.php new file mode 100644 index 0000000..32d3e9f --- /dev/null +++ b/app/src/Middleware/Authentication.php @@ -0,0 +1,48 @@ +service->isIn() or $this->isValid($request)) { + return $handler->handle($request); + } + $response = $this->responseFactory->createResponse(301, 'Not logged in'); + return $response->withHeader('Location', $this->login_url) + ->withHeader('X-Redirected-URI', (string) $request->getUri()); + } + + protected function isValid(ServerRequestInterface $request): bool + { + $uri = $request->getUri(); + $current_path = $uri->getPath(); + $current_url = implode('', [ + "{$uri->getScheme()}://", + $uri->getHost() . ($uri->getPort() !== null ? ":{$uri->getPort()}" : ''), + $uri->getPath() + ]); + + $valid_paths = [ + '/', + ]; + if (in_array($current_path, $valid_paths, true)) { + return true; + } + $valid_uris = [ + $this->login_url, + ]; + if (in_array($current_url, $valid_uris, true)) { + return true; + } + return false; + } +} diff --git a/app/src/Model/Banco.php b/app/src/Model/Banco.php new file mode 100644 index 0000000..1852ff2 --- /dev/null +++ b/app/src/Model/Banco.php @@ -0,0 +1,16 @@ + $this->nombre ?? '' + ]); + } +} diff --git a/app/src/Model/Comuna.php b/app/src/Model/Comuna.php new file mode 100644 index 0000000..3bf746d --- /dev/null +++ b/app/src/Model/Comuna.php @@ -0,0 +1,18 @@ + $this->descripcion, + 'provincia' => $this->provincia + ]); + } +} diff --git a/app/src/Model/Direccion.php b/app/src/Model/Direccion.php new file mode 100644 index 0000000..7795ad5 --- /dev/null +++ b/app/src/Model/Direccion.php @@ -0,0 +1,22 @@ + $this->calle, + 'numero' => $this->numero, + 'extra' => $this->extra, + 'comuna' => $this->comuna + ]); + } +} diff --git a/app/src/Model/Inmobiliaria.php b/app/src/Model/Inmobiliaria.php new file mode 100644 index 0000000..cc7778c --- /dev/null +++ b/app/src/Model/Inmobiliaria.php @@ -0,0 +1,38 @@ +rut, 0, ',', '.'), + $this->dv + ]); + } + + public function jsonSerialize(): mixed + { + return [ + 'rut' => $this->rut, + 'dv' => $this->dv ?? '', + 'rut_formateado' => $this->rut(), + 'razon' => $this->razon ?? '', + 'abreviacion' => $this->abreviacion ?? '', + 'cuenta' => $this->cuenta ?? '', + 'banco' => $this->banco ?? '', + 'tipo_sociedad' => $this->tipoSociedad ?? '' + ]; + } +} diff --git a/app/src/Model/Inmobiliaria/TipoSociedad.php b/app/src/Model/Inmobiliaria/TipoSociedad.php new file mode 100644 index 0000000..dc71047 --- /dev/null +++ b/app/src/Model/Inmobiliaria/TipoSociedad.php @@ -0,0 +1,16 @@ + $this->abreviacion + ]); + } +} diff --git a/app/src/Model/Login.php b/app/src/Model/Login.php new file mode 100644 index 0000000..fb66590 --- /dev/null +++ b/app/src/Model/Login.php @@ -0,0 +1,25 @@ + $this->user->id, + 'selector' => $this->selector, + 'token' => $this->token, + 'date_time' => $this->dateTime->format('Y-m-d H:i:s'), + 'status' => $this->status + ]); + } +} diff --git a/app/src/Model/Provincia.php b/app/src/Model/Provincia.php new file mode 100644 index 0000000..1f2f387 --- /dev/null +++ b/app/src/Model/Provincia.php @@ -0,0 +1,18 @@ + $this->descripcion, + 'region' => $this->region + ]); + } +} diff --git a/app/src/Model/Proyecto.php b/app/src/Model/Proyecto.php new file mode 100644 index 0000000..f9f21d2 --- /dev/null +++ b/app/src/Model/Proyecto.php @@ -0,0 +1,32 @@ + $this->inmobiliaria, + 'descripcion' => $this->descripcion, + 'direccion' => $this->direccion, + 'terreno' => $this->terreno, + 'superficie' => $this->superficie, + 'corredor' => $this->corredor, + 'pisos' => $this->pisos, + 'subterraneos' => $this->subterraneos + ]); + } +} diff --git a/app/src/Model/Proyecto/ProyectoTipoUnidad.php b/app/src/Model/Proyecto/ProyectoTipoUnidad.php new file mode 100644 index 0000000..a755bb7 --- /dev/null +++ b/app/src/Model/Proyecto/ProyectoTipoUnidad.php @@ -0,0 +1,42 @@ +util, $this->logia, $this->terraza], function($sum, $item) {return $sum + $item;}, 0); + } + public function vendible(): float + { + return array_reduce([$this->util, $this->logia, $this->terraza / 2], function($sum, $item) {return $sum + $item;}, 0); + } + + public function jsonSerialize(): mixed + { + return array_merge(parent::jsonSerialize(), [ + 'proyecto' => $this->proyecto, + 'tipo_unidad' => $this->tipoUnidad, + 'nombre' => $this->nombre, + 'abreviacion' => $this->abreviacion, + 'util' => $this->util, + 'logia' => $this->logia, + 'terraza' => $this->terraza, + 'superficie' => $this->superficie(), + 'vendible' => $this->vendible(), + 'descripcion' => $this->descripcion + ]); + } +} diff --git a/app/src/Model/Proyecto/Superficie.php b/app/src/Model/Proyecto/Superficie.php new file mode 100644 index 0000000..72d6e5f --- /dev/null +++ b/app/src/Model/Proyecto/Superficie.php @@ -0,0 +1,8 @@ + $this->descripcion, + 'numeral' => $this->numeral, + 'numeracion' => $this->numeracion ?? 0 + ]); + } +} diff --git a/app/src/Model/Role.php b/app/src/Model/Role.php new file mode 100644 index 0000000..e27c6a9 --- /dev/null +++ b/app/src/Model/Role.php @@ -0,0 +1,7 @@ + $this->descripcion + ]); + } +} diff --git a/app/src/Model/User.php b/app/src/Model/User.php new file mode 100644 index 0000000..2647b88 --- /dev/null +++ b/app/src/Model/User.php @@ -0,0 +1,28 @@ +password); + } + public function isAdmin(): bool + { + return false; + } + + public function jsonSerialize(): mixed + { + return array_merge(parent::jsonSerialize(), [ + 'name' => $this->name + ]); + } +} diff --git a/app/src/Model/Venta/Cierre.php b/app/src/Model/Venta/Cierre.php new file mode 100644 index 0000000..25d3009 --- /dev/null +++ b/app/src/Model/Venta/Cierre.php @@ -0,0 +1,26 @@ + $this->proyecto->id, + 'precio' => $this->precio, + 'date_time' => $this->dateTime->format('Y-m-d H:i:s'), + 'relacionado' => $this->relacionado, + 'propietario' => $this->propietario->rut + ]); + } +} diff --git a/app/src/Model/Venta/Cuota.php b/app/src/Model/Venta/Cuota.php new file mode 100644 index 0000000..188f4c0 --- /dev/null +++ b/app/src/Model/Venta/Cuota.php @@ -0,0 +1,38 @@ + $this->pie->id, + 'fecha' => $this->fecha->format('Y-m-d H:i:s'), + 'valor' => $this->valor, + 'estado' => $this->estado ?? false, + 'banco' => $this->banco, + 'fecha_pago' => $this->fechaPago->format('Y-m-d H:i:s') ?? '', + 'abonado' => $this->abonado ?? false, + 'fecha_abonado' => $this->fechaAbonado->format('Y-m-d H:i:s') ?? '', + 'uf' => $this->uf ?? 1, + 'pago' => $this->pago ?? '', + 'numero' => $this->numero ?? '' + ]); + } +} diff --git a/app/src/Model/Venta/Datos.php b/app/src/Model/Venta/Datos.php new file mode 100644 index 0000000..b9a7ebd --- /dev/null +++ b/app/src/Model/Venta/Datos.php @@ -0,0 +1,27 @@ + $this->sexo ?? '', + 'estado_civil' => $this->estado_civil ?? '', + 'profesion' => $this->profesion ?? '', + 'direccion' => $this->direccion ?? '', + 'telefono' => $this->telefono ?? '', + 'email' => $this->email ?? '' + ]; + } +} diff --git a/app/src/Model/Venta/EstadoPago.php b/app/src/Model/Venta/EstadoPago.php new file mode 100644 index 0000000..e94f8d8 --- /dev/null +++ b/app/src/Model/Venta/EstadoPago.php @@ -0,0 +1,22 @@ + $this->pago->id, + 'fecha' => $this->fecha->format('Y-m-d'), + 'tipo_estado_pago_id' => $this->tipoEstadoPago->id + ]); + } + +} diff --git a/app/src/Model/Venta/EstadoPrecio.php b/app/src/Model/Venta/EstadoPrecio.php new file mode 100644 index 0000000..815793f --- /dev/null +++ b/app/src/Model/Venta/EstadoPrecio.php @@ -0,0 +1,21 @@ + $this->precio->id, + 'tipo_estado_precio' => $this->tipoEstadoPrecio, + 'fecha' => $this->fecha->format('Y-m-d') + ]); + } +} diff --git a/app/src/Model/Venta/Pago.php b/app/src/Model/Venta/Pago.php new file mode 100644 index 0000000..08f5da9 --- /dev/null +++ b/app/src/Model/Venta/Pago.php @@ -0,0 +1,32 @@ + $this->valor, + 'banco' => $this->banco ?? '', + 'tipo_pago' => $this->tipoPago ?? '', + 'identificador' => $this->identificador ?? '', + 'fecha' => $this->fecha->format('Y-m-d H:i:S') ?? '', + 'uf' => $this->uf ?? 1, + 'pagador' => $this->pagador ?? '', + 'asociado' => $this->asociado ?? '' + ]); + } +} diff --git a/app/src/Model/Venta/Pie.php b/app/src/Model/Venta/Pie.php new file mode 100644 index 0000000..3a1f4ad --- /dev/null +++ b/app/src/Model/Venta/Pie.php @@ -0,0 +1,27 @@ + $this->fecha->format('Y-m-d H:i:s'), + 'valor' => $this->valor, + 'uf' => $this->uf ?? 1, + 'cuotas' => $this->cuotas, + 'asociado' => $this->asociado ?? '', + 'reajuste' => $this->reajuste ?? '' + ]); + } +} diff --git a/app/src/Model/Venta/Precio.php b/app/src/Model/Venta/Precio.php new file mode 100644 index 0000000..0b2094e --- /dev/null +++ b/app/src/Model/Venta/Precio.php @@ -0,0 +1,23 @@ + $this->unidad, + 'valor' => $this->valor, + 'estado_precio' => $this->current ?? [], + 'estados' => $this->estados ?? null + ]); + } +} diff --git a/app/src/Model/Venta/Propietario.php b/app/src/Model/Venta/Propietario.php new file mode 100644 index 0000000..77300eb --- /dev/null +++ b/app/src/Model/Venta/Propietario.php @@ -0,0 +1,46 @@ +rut, 0, ',', '.'), + $this->dv + ]); + } + public function nombreCompleto(): string + { + return implode(' ', [ + $this->nombres, + implode(' ', $this->apellidos) + ]); + } + + public function jsonSerialize(): mixed + { + return array_merge([ + 'rut' => $this->rut, + 'dv' => $this->dv, + 'rut_formateado' => $this->rut(), + 'nombres' => $this->nombres, + 'apellidos' => $this->apellidos, + 'nombre_completo' => $this->nombreCompleto(), + ], $this->datos->jsonSerialize(), [ + 'representante' => $this->representante ?? '', + 'otro' => $this->otro ?? '' + ]); + } +} diff --git a/app/src/Model/Venta/TipoEstadoPago.php b/app/src/Model/Venta/TipoEstadoPago.php new file mode 100644 index 0000000..2d00621 --- /dev/null +++ b/app/src/Model/Venta/TipoEstadoPago.php @@ -0,0 +1,8 @@ + $this->orden + ]); + } +} diff --git a/app/src/Model/Venta/Unidad.php b/app/src/Model/Venta/Unidad.php new file mode 100644 index 0000000..3040504 --- /dev/null +++ b/app/src/Model/Venta/Unidad.php @@ -0,0 +1,25 @@ + $this->subtipo, + 'piso' => $this->piso, + 'descripcion' => $this->descripcion, + 'orientacion' => $this->orientacion, + 'proyecto_tipo_unidad' => $this->proyectoTipoUnidad + ]); + } +} diff --git a/app/src/Repository/Banco.php b/app/src/Repository/Banco.php new file mode 100644 index 0000000..7a35492 --- /dev/null +++ b/app/src/Repository/Banco.php @@ -0,0 +1,35 @@ +setTable('banco'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'nombre' => [] + ]; + return $this->parseData(new Model\Banco(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['nombre'], + [$model->nombre] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['nombre'], $new_data); + } +} diff --git a/app/src/Repository/Comuna.php b/app/src/Repository/Comuna.php new file mode 100644 index 0000000..ddb54e1 --- /dev/null +++ b/app/src/Repository/Comuna.php @@ -0,0 +1,51 @@ +setTable('comuna'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [], + 'provincia' => [ + 'function' => function($data) { + return $this->provinciaRepository->fetchById($data['provincia']); + } + ] + ]; + return $this->parseData(new Model\Comuna(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion', 'provincia'], + [$model->descripcion, $model->provincia->id] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion', 'provincia'], $new_data); + } + + public function fetchByDescripcion(string $descripcion): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `descripcion` = ?"; + return $this->fetchOne($query, [$descripcion]); + } + public function fetchByProvincia(int $provincia_id): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `provincia` = ?"; + return $this->fetchMany($query, [$provincia_id]); + } +} diff --git a/app/src/Repository/Direccion.php b/app/src/Repository/Direccion.php new file mode 100644 index 0000000..87bd955 --- /dev/null +++ b/app/src/Repository/Direccion.php @@ -0,0 +1,48 @@ +setTable('direccion'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'calle' => [], + 'numero' => [], + 'extra' => [], + 'comuna' => [ + 'function' => function($data) { + return $this->comunaRepository->fetchById($data['comuna']); + } + ] + ]; + return $this->parseData(new Model\Direccion(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['calle', 'numero', 'extra', 'comuna'], + [$model->calle, $model->numero, $model->extra, $model->comuna->id] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['calle', 'numero', 'extra', 'comuna'], $new_data); + } + + public function fetchByCalleAndNumero(string $calle, int $numero): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `calle` = ? AND `numero` = ?"; + return $this->fetchMany($query, [$calle, $numero]); + } +} diff --git a/app/src/Repository/Inmobiliaria.php b/app/src/Repository/Inmobiliaria.php new file mode 100644 index 0000000..7ae90c6 --- /dev/null +++ b/app/src/Repository/Inmobiliaria.php @@ -0,0 +1,55 @@ +setTable('inmobiliaria'); + } + + protected function getKey(): string + { + return 'rut'; + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'dv' => [], + 'razon' => [], + 'abreviacion' => [], + 'cuenta' => [], + 'banco' => [ + 'function' => function($data) { + return $this->bancoRepository->fetchById($data['banco']); + } + ], + 'sociedad' => [ + 'property' => 'tipoSociedad', + 'function' => function($data) { + return $this->tipoSociedadRepository->fetchById($data['sociedad']); + } + ] + ]; + return $this->parseData(new Model\Inmobiliaria(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->rut = $this->saveNew( + ['dv', 'razon', 'abreviacion', 'cuenta', 'banco', 'sociedad'], + [$model->dv, $model->razon, $model->abreviacion, $model->cuenta, $model->banco->id, $model->tipoSociedad->id] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['dv', 'razon', 'abreviacion', 'cuenta', 'banco', 'sociedad'], $new_data); + } +} diff --git a/app/src/Repository/Inmobiliaria/TipoSociedad.php b/app/src/Repository/Inmobiliaria/TipoSociedad.php new file mode 100644 index 0000000..0d2a05f --- /dev/null +++ b/app/src/Repository/Inmobiliaria/TipoSociedad.php @@ -0,0 +1,36 @@ +setTable('tipo_sociedad'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [], + 'abreviacion' => [] + ]; + return $this->parseData(new Model\Inmobiliaria\TipoSociedad(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion', 'abreviacion'], + [$model->descripcion, $model->abreviacion] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion', 'abreviacion'], $new_data); + } +} diff --git a/app/src/Repository/Login.php b/app/src/Repository/Login.php new file mode 100644 index 0000000..0cfa3e4 --- /dev/null +++ b/app/src/Repository/Login.php @@ -0,0 +1,77 @@ +setTable('logins'); + } + + + public function create(?array $data = null): Define\Model + { + $map = [ + 'user_id' => [ + 'property' => 'user', + 'function' => function($data) { + return $this->userRepository->fetchById($data['user_id']); + } + ], + 'selector' => [], + 'token' => [], + 'time' => [ + 'property' => 'dateTime', + 'function' => function($data) { + return new DateTimeImmutable($data['time']); + } + ], + 'status' => [ + 'function' => function($data) { + return $data['status'] != 0; + } + ] + ]; + return $this->parseData(new Model\Login(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['user_id', 'selector', 'token', 'time', 'status'], + [$model->user->id, $model->selector, $model->token, $model->dateTime->format('Y-m-d H:i:s'), $model->status ? 1 : 0]); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['user_id', 'selector', 'token', 'time', 'status'], $new_data); + } + + public function fetchByUser(int $user_id): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `user_id` = ?"; + return $this->fetchMany($query, [$user_id]); + } + public function fetchActiveByUser(int $user_id): Model\Login + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `user_id` = ? AND `status` = 1"; + return $this->fetchOne($query, [$user_id]); + } + public function fetchBySelectorAndToken(string $selector, string $token): Model\Login + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `selector` = ? AND `token` = ?"; + return $this->fetchOne($query, [$selector, $token]); + } + public function fetchActiveBySelector(string $selector): Model\Login + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `selector` = ? AND `status` = 1"; + return $this->fetchOne($query, [$selector]); + } +} diff --git a/app/src/Repository/Provincia.php b/app/src/Repository/Provincia.php new file mode 100644 index 0000000..3e8406e --- /dev/null +++ b/app/src/Repository/Provincia.php @@ -0,0 +1,51 @@ +setTable('provincia'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [], + 'region' => [ + 'function' => function($data) { + return $this->regionRepository->fetchById($data['region']); + } + ] + ]; + return $this->parseData(new Model\Provincia(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion', 'region'], + [$model->descripcion, $model->region->id] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion', 'region'], $new_data); + } + + public function fetchByDescripcion(string $description): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `descripcion` = ?"; + return $this->fetchOne($query, [$description]); + } + public function fetchByRegion(int $region_id): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `region` = ?"; + return $this->fetchMany($query, [$region_id]); + } +} diff --git a/app/src/Repository/Proyecto.php b/app/src/Repository/Proyecto.php new file mode 100644 index 0000000..0c8d4e8 --- /dev/null +++ b/app/src/Repository/Proyecto.php @@ -0,0 +1,88 @@ +setTable('proyecto'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'inmobiliaria' => [ + 'function' => function($data) { + return $this->inmobiliariaRepository->fetchById($data['inmobiliaria']); + } + ], + 'descripcion' => [], + 'direccion' => [ + 'function' => function($data) { + return $this->direccionRepository->fetchById($data['direccion']); + } + ], + 'superficie_terreno' => [ + 'property' => 'terreno', + 'function' => function($data) { + $terreno = new Model\Proyecto\Terreno(); + $terreno->superficie = $data['superficie_terreno']; + $terreno->valor = $data['valor_terreno']; + return $terreno; + } + ], + 'superficie_sobre_nivel' => [ + 'property' => 'superficie', + 'function' => function($data) { + $superficie = new Model\Proyecto\Superficie(); + $superficie->sobre_nivel = $data['superficie_sobre_nivel']; + $superficie->bajo_nivel = $data['superficie_bajo_nivel']; + return $superficie; + } + ], + 'corredor' => [], + 'pisos' => [], + 'subterraneos' => [] + ]; + return $this->parseData(new Model\Proyecto(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['inmobiliaria', 'descripcion', 'direccion', 'superficie_terreno', 'valor_terreno', 'corredor', + 'superficie_sobre_nivel', 'superficie_bajo_nivel', 'pisos', 'subterraneos'], + [$model->inmobiliaria->rut, $model->descripcion, $model->direccion->id, $model->terreno->superficie, + $model->terreno->valor, $model->corredor, $model->superficie->sobre_nivel, + $model->superficie->bajo_nivel, $model->pisos, $model->subterraneos] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['inmobiliaria', 'descripcion', 'direccion', 'superficie_terreno', + 'valor_terreno', 'corredor', 'superficie_sobre_nivel', 'superficie_bajo_nivel', 'pisos', + 'subterraneos'], $new_data); + } + public function fetchByName(string $name): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `name` = ?"; + return $this->fetchOne($query, [$name]); + } + public function fetchAllActive(): array + { + $query = "SELECT a.* +FROM `{$this->getTable()}` a + JOIN (SELECT e1.* FROM `estado_proyecto` e1 JOIN (SELECT MAX(`id`) AS 'id', `proyecto` FROM `estado_proyecto` GROUP BY `proyecto`) e0 ON e0.`id` = e1.`id`) ep ON ep.`proyecto` = a.`id` + JOIN `tipo_estado_proyecto` tep ON tep.`id` = ep.`estado` + JOIN `etapa_proyecto` et ON et.`id` = tep.`etapa` +WHERE et.`orden` BETWEEN 4 AND 8 +ORDER BY a.`descripcion`"; + return $this->fetchMany($query); + } +} diff --git a/app/src/Repository/Proyecto/ProyectoTipoUnidad.php b/app/src/Repository/Proyecto/ProyectoTipoUnidad.php new file mode 100644 index 0000000..1500f26 --- /dev/null +++ b/app/src/Repository/Proyecto/ProyectoTipoUnidad.php @@ -0,0 +1,54 @@ +setTable('proyecto_tipo_unidad'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'proyecto' => [ + 'function' => function($data) { + return $this->proyectoRepository->fetchById($data['proyecto']); + } + ], + 'tipo' => [ + 'property' => 'tipoUnidad', + 'function' => function($data) { + return $this->tipoUnidadRepository->fetchById($data['tipo']); + } + ], + 'nombre' => [], + 'abreviacion' => [], + 'm2' => [ + 'property' => 'util' + ], + 'logia' => [], + 'terraza' => [], + 'descripcion' => [] + ]; + return $this->parseData(new Model\Proyecto\ProyectoTipoUnidad(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['proyecto', 'tipo', 'nombre', 'abreviacion', 'm2', 'logia', 'terraza', 'descripcion'], + [$model->proyecto->id, $model->tipoUnidad->id, $model->nombre, $model->abreviacion, $model->util, $model->logia, $model->terraza, $model->descripcion] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['proyecto', 'tipo', 'nombre', 'abreviacion', 'util', 'logia', 'terraza', 'descripcion'], $new_data); + } +} diff --git a/app/src/Repository/Region.php b/app/src/Repository/Region.php new file mode 100644 index 0000000..34287e6 --- /dev/null +++ b/app/src/Repository/Region.php @@ -0,0 +1,53 @@ +setTable('region'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [], + 'numeral' => [], + 'numeracion' => [] + ]; + return $this->parseData(new Model\Region(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion', 'numeral', 'numeracion'], + [$model->descripcion, $model->numeral, $model->numeracion] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion', 'numeral', 'numeracion'], $new_data); + } + + public function fetchByDescripcion(string $descripcion): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `descripcion` = ?"; + return $this->fetchOne($query, [$descripcion]); + } + public function fetchByNumeral(string $numeral): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `numeral` = ?"; + return $this->fetchOne($query, [$numeral]); + } + public function fetchByNumeracion(int $numeracion): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `numeracion` = ?"; + return $this->fetchOne($query, [$numeracion]); + } +} diff --git a/app/src/Repository/User.php b/app/src/Repository/User.php new file mode 100644 index 0000000..97ae151 --- /dev/null +++ b/app/src/Repository/User.php @@ -0,0 +1,46 @@ +setTable('users'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'name' => [], + 'password' => [], + 'enabled' => [ + 'function' => function($data) { + return $data['enabled'] != 0; + } + ] + ]; + return $this->parseData(new Model\User(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew(['name', 'password', 'enabled'], [$model->name, $model->password, $model->enabled ? 1 : 0]); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['name', 'password', 'enabled'], $new_data); + } + + public function fetchByName(string $name): Model\User + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `name` = ?"; + return $this->fetchOne($query, [$name]); + } +} diff --git a/app/src/Repository/Venta/Cierre.php b/app/src/Repository/Venta/Cierre.php new file mode 100644 index 0000000..6366347 --- /dev/null +++ b/app/src/Repository/Venta/Cierre.php @@ -0,0 +1,82 @@ +setTable('cierre'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'proyecto' => [ + 'function' => function($data) { + return $this->proyectoRepository->fetchById($data['proyecto']); + } + ], + 'precio' => [], + 'fecha' => [ + 'property' => 'dateTime', + 'function' => function($data) { + return new DateTimeImmutable($data['fecha']); + } + ], + 'relacionado' => [ + 'function' => function($data) { + return $data['relacionado'] !== 0; + } + ], + 'propietario' => [ + 'function' => function($data) { + return $this->propietarioRepository->fetchById($data['propietario']); + } + ] + ]; + return $this->parseData(new Model\Venta\Cierre(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['proyecto', 'precio', 'fecha', 'relacionado', 'propietario'], + [$model->proyecto->id, $model->precio, $model->fecha->format('Y-m-d H:i:s'), $model->relacionado ? 1 : 0, $model->propietario->rut] + ); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['proyecto', 'precio', 'fecha', 'relacionado', 'propietario'], $new_data); + } + + public function fetchDatosVigentes(): array + { + $query = " +SELECT `proyecto`.`descripcion` AS 'Proyecto', tec.`descripcion` AS 'Estado', COUNT(a.`id`) AS 'Cantidad' +FROM `{$this->getTable()}` a + JOIN (SELECT e1.* + FROM `estado_cierre` e1 + JOIN (SELECT MAX(`id`) AS id, `cierre` FROM `estado_cierre` GROUP BY `cierre`) e0 ON e0.`id` = e1.`id`) ec ON ec.`cierre` = a.`id` + JOIN `tipo_estado_cierre` tec ON tec.`id` = ec.`tipo` + JOIN `proyecto` ON `proyecto`.`id` = a.`proyecto` +GROUP BY `proyecto`.`descripcion`, tec.`descripcion`"; + $results = $this->connection->execute($query)->fetchAll(PDO::FETCH_ASSOC); + if ($results === false) { + throw new EmptyResult($query); + } + return $results; + } +} diff --git a/app/src/Repository/Venta/Cuota.php b/app/src/Repository/Venta/Cuota.php new file mode 100644 index 0000000..341d554 --- /dev/null +++ b/app/src/Repository/Venta/Cuota.php @@ -0,0 +1,161 @@ +setTable('cuota'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'pie' => [ + 'function' => function($data) { + return $this->pieRepository->fetchById($data['pie']); + } + ], + 'fecha' => [ + 'function' => function($data) { + return new DateTimeImmutable($data['fecha']); + } + ], + 'valor' => [], + 'estado' => [ + 'function' => function($data) { + return $data['estado'] !== 0; + } + ], + 'banco' => [ + 'function' => function($data) { + if ($data['banco'] === null or $data['banco'] === '') { + return null; + } + return $this->bancoRepository->fetchById($data['banco']); + } + ], + 'fecha_pago' => [ + 'property' => 'fechaPago', + 'function' => function($data) { + if ($data['fecha_pago'] === null) { + return null; + } + return new DateTimeImmutable($data['fecha_pago']); + } + ], + 'abonado' => [ + 'function' => function($data) { + if ($data['abonado'] === null) { + return null; + } + return $data['abonado'] !== 0; + } + ], + 'fecha_abonado' => [ + 'property' => 'fechaAbonado', + 'function' => function($data) { + if ($data['fecha_abonado'] === null) { + return null; + } + return new DateTimeImmutable($data['fecha_abonado']); + } + ], + 'uf' => [], + 'pago' => [ + 'function' => function($data) { + if ($data['pago'] === null) { + return null; + } + return $this->pagoRepository->fetchById($data['pago']); + } + ], + 'numero' => [] + ]; + return $this->parseData(new Model\Venta\Cuota(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['pie', 'fecha', 'valor', 'estado', 'banco', 'fecha_pago', 'abonado', 'fecha_abonado', 'uf', 'pago', 'numero'], + [$model->pie->id, $model->fecha->format('Y-m-d H:i:s'), $model->valor, $model->estado ? 1 : 0, $model?->banco->id, + $model?->fechaPago->format('Y-m-d H:i:s'), $model?->abonado ? 1 : 0, $model?->fechaAbonado->format('Y-m-d H:i:s'), + $model?->uf, $model?->pago->id, $model?->numero] + ); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['pie', 'fecha', 'valor', 'estado', 'banco', 'fecha_pago', 'abonado', 'fecha_abonado', 'uf', 'pago', 'numero'], $new_data); + } + + public function fetchHoy(): array + { + $query = "SELECT a.* +FROM `{$this->getTable()}` a + JOIN `pago` ON `pago`.`id` = a.`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 ON ep.`pago` = `pago`.`id` + JOIN `tipo_estado_pago` tep ON tep.`id` = ep.`estado` + JOIN `venta` ON `venta`.`pie` = a.`pie` + JOIN (SELECT ev1.* FROM `estado_venta` ev1 JOIN (SELECT MAX(`id`) AS 'id', `venta` FROM `estado_venta` GROUP BY `venta`) ev0 ON ev0.`id` = ev1.`id`) ev ON ev.`venta` = `venta`.`id` + JOIN `tipo_estado_venta` tev ON tev.`id` = ev.`estado` +WHERE tep.`descripcion` = 'no pagado' AND `pago`.`fecha` = CURDATE() + AND tev.`descripcion` IN ('vigente', 'escriturando', 'firmado por inmobiliaria')"; + return $this->fetchMany($query); + } + public function fetchPendientes(): array + { + $query = "SELECT a.`id` AS 'cuota_id', `venta`.`id` AS 'venta_id', `proyecto`.`descripcion` AS 'Proyecto', `unidad`.`descripcion` AS 'Departamento', + `pago`.`valor` AS 'Valor', `pago`.`fecha`, CONCAT_WS(' - ', a.`numero`, `pie`.`cuotas`) AS 'Numero', `banco`.`nombre` AS 'Banco', + CONCAT_WS(' ', `propietario`.`nombres`, `propietario`.`apellido_paterno`, `propietario`.`apellido_materno`) AS 'Propietario' +FROM `{$this->getTable()}` a + JOIN `pago` ON `pago`.`id` = a.`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 ON ep.`pago` = `pago`.`id` + JOIN `tipo_estado_pago` tep ON tep.`id` = ep.`estado` + JOIN `pie` ON `pie`.`id` = a.`pie` + JOIN `venta` ON `venta`.`pie` = a.`pie` + JOIN (SELECT ev1.* FROM `estado_venta` ev1 JOIN (SELECT MAX(`id`) AS 'id', `venta` FROM `estado_venta` GROUP BY `venta`) ev0 ON ev0.`id` = ev1.`id`) ev ON ev.`venta` = `venta`.`id` + JOIN `tipo_estado_venta` tev ON tev.`id` = ev.`estado` + JOIN `propietario` ON `propietario`.`rut` = `venta`.`propietario` + JOIN `propiedad_unidad` pu ON pu.`propiedad` = `venta`.`propiedad` + JOIN `unidad` ON `unidad`.`id` = pu.`unidad` AND pu.`principal` = 1 + JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt` + JOIN `proyecto` ON `proyecto`.`id` = ptu.`proyecto` + JOIN `banco` ON `banco`.`id` = `pago`.`banco` +WHERE tep.`descripcion` = 'no pagado' AND `pago`.`fecha` < CURDATE() + AND tev.`descripcion` IN ('vigente', 'escriturando', 'firmado por inmobiliaria') +ORDER BY `pago`.`fecha` DESC"; + return $this->fetchAsArray($query); + } + public function fetchDatosPorVencer(): array + { + $query = "SELECT `pago`.`fecha` AS 'Fecha', `proyecto`.`descripcion` AS 'Proyecto', COUNT(a.`id`) AS 'Cantidad' +FROM `{$this->getTable()}` a + JOIN `pago` ON `pago`.`id` = a.`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 ON ep.`pago` = `pago`.`id` + JOIN `tipo_estado_pago` tep ON tep.`id` = ep.`estado` + JOIN `venta` ON `venta`.`pie` = a.`pie` + JOIN (SELECT ev1.* FROM `estado_venta` ev1 JOIN (SELECT MAX(`id`) AS 'id', `venta` FROM `estado_venta` GROUP BY `venta`) ev0 ON ev0.`id` = ev1.`id`) ev ON ev.`venta` = `venta`.`id` + JOIN `tipo_estado_venta` tev ON tev.`id` = ev.`estado` + JOIN `propiedad_unidad` pu ON pu.`propiedad` = `venta`.`propiedad` AND pu.`principal` = 1 + JOIN `unidad` ON `unidad`.`id` = pu.`unidad` + JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt` + JOIN `proyecto` ON `proyecto`.`id` = ptu.`proyecto` +WHERE tep.`descripcion` = 'no pagado' AND `pago`.`fecha` BETWEEN DATE_ADD(CURDATE(), INTERVAL 1 DAY) AND DATE_ADD(CURDATE(), INTERVAL 1 MONTH) + AND tev.`descripcion` IN ('vigente', 'escriturando', 'firmado por inmobiliaria') +GROUP BY `pago`.`fecha`, `proyecto`.`descripcion` +ORDER BY `pago`.`fecha`, `proyecto`.`descripcion`"; + return $this->fetchAsArray($query); + } +} diff --git a/app/src/Repository/Venta/EstadoPago.php b/app/src/Repository/Venta/EstadoPago.php new file mode 100644 index 0000000..406647f --- /dev/null +++ b/app/src/Repository/Venta/EstadoPago.php @@ -0,0 +1,67 @@ +setTable('estado_pago'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'pago' => [ + 'function' => function($data) { + return $this->pagoRepository->fetchById($data['pago']); + } + ], + 'estado' => [ + 'property' => 'tipoEstadoPago', + 'function' => function($data) { + return $this->tipoEstadoPagoRepository->fetchById($data['estado']); + } + ], + 'fecha' => [ + 'function' => function($data) { + return new DateTimeImmutable($data['fecha']); + } + ] + ]; + return $this->parseData(new Model\Venta\EstadoPago(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['pago', 'estado', 'fecha'], + [$model->pago->id, $model->tipoEstadoPago->id, $model->fecha->format('Y-m-d')] + ); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['pago', 'estado', 'fecha'], $new_data); + } + + public function fetchByPago(int $pago_id): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `pago` = ?"; + return $this->fetchMany($query, [$pago_id]); + } + public function fetchByPagoAndEstado(int $pago_id, int $estado_id): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `pago` = ? AND `estado` = ?"; + return $this->fetchOne($query, [$pago_id, $estado_id]); + } +} diff --git a/app/src/Repository/Venta/EstadoPrecio.php b/app/src/Repository/Venta/EstadoPrecio.php new file mode 100644 index 0000000..c223e30 --- /dev/null +++ b/app/src/Repository/Venta/EstadoPrecio.php @@ -0,0 +1,65 @@ +setTable('estado_precio'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'precio' => [ + 'function' => function($data) { + return $this->precioRepository->fetchById($data['precio']); + } + ], + 'estado' => [ + 'property' => 'tipoEstadoPrecio', + 'function' => function($data) { + return $this->tipoEstadoPrecioRepository->fetchById($data['estado']); + } + ], + 'fecha' => [ + 'function' => function($data) { + return new DateTimeImmutable($data['fecha']); + } + ] + ]; + return $this->parseData(new Model\Venta\EstadoPrecio(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['precio', 'estado', 'fecha'], + [$model->precio->id, $model->tipoEstadoPrecio->id, $model->fecha->format('Y-m-d')] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['precio', 'estado', 'fecha'], $new_data); + } + + public function fetchByPrecio(int $precio_id): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `precio` = ?"; + return $this->fetchMany($query, [$precio_id]); + } + public function fetchCurrentByPrecio(int $precio_id): Define\Model + { + $query = "SELECT e1.* +FROM `{$this->getTable()}` e1 JOIN (SELECT MAX(`id`) AS 'id', `precio` FROM `{$this->getTable()}` GROUP BY `precio`) e0 ON e0.`id` = e1.`id` +WHERE e1.`precio` = ?"; + return $this->fetchOne($query, [$precio_id]); + } +} diff --git a/app/src/Repository/Venta/Pago.php b/app/src/Repository/Venta/Pago.php new file mode 100644 index 0000000..ec9b6e6 --- /dev/null +++ b/app/src/Repository/Venta/Pago.php @@ -0,0 +1,73 @@ +setTable('pago'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'valor' => [], + 'banco' => [ + 'function' => function($data) { + if ($data['banco'] === null or $data['banco'] === 0) { + return null; + } + return $this->bancoRepository->fetchById($data['banco']); + } + ], + 'tipo' => [ + 'property' => 'tipoPago', + 'function' => function($data) { + if ($data['tipo'] === null) { + return null; + } + return $this->tipoPagoRepository->fetchById($data['tipo']); + } + ], + 'identificador' => [], + 'fecha' => [ + 'function' => function($data) { + if ($data['fecha'] === null) { + return null; + } + return new DateTimeImmutable($data['fecha']); + } + ], + 'uf' => [], + 'pagador' => [], + 'asociado' => [ + 'function' => function($data) { + if ($data['asociado'] === null) { + return null; + } + return $this->fetchById($data['asociado']); + } + ] + ]; + return $this->parseData(new Model\Venta\Pago(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['valor', 'banco', 'tipo', 'identificador', 'fecha', 'uf', 'pagador', 'asociado'], + [$model->valor, $model?->banco->id, $model?->tipoPago->id, $model?->identificador, $model?->fecha->format('Y-m-d H:i:s'), $model?->uf, $model?->pagador, $model?->asociado->id] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['valor', 'banco', 'tipo', 'identificador', 'fecha', 'uf', 'pagador', 'asociado'], $new_data); + } +} diff --git a/app/src/Repository/Venta/Pie.php b/app/src/Repository/Venta/Pie.php new file mode 100644 index 0000000..37b8d2c --- /dev/null +++ b/app/src/Repository/Venta/Pie.php @@ -0,0 +1,60 @@ +setTable('pie'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'fecha' => [ + 'function' => function($data) { + return new DateTimeImmutable($data['fecha']); + } + ], + 'valor' => [], + 'uf' => [], + 'cuotas' => [], + 'asociado' => [ + 'function' => function($data) { + if ($data['asociado'] === null or $data['asociado'] === 0) { + return null; + } + return $this->fetchById($data['asociado']); + } + ], + 'reajuste' => [ + 'function' => function($data) { + if ($data['reajuste'] === null or $data['reajuste'] === 0) { + return null; + } + return $this->pagoRepository->fetchById($data['reajuste']); + } + ] + ]; + return $this->parseData(new Model\Venta\Pie(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['fecha', 'valor', 'uf', 'cuotas', 'asociado', 'reajuste'], + [$model->fecha->format('Y-m-d H:i:s'), $model->valor, $model?->uf, $model->cuotas, $model?->asociado->id, $model?->reajuste->id] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['fecha', 'valor', 'uf', 'cuotas', 'asociado', 'reajuste'], $new_data); + } +} diff --git a/app/src/Repository/Venta/Precio.php b/app/src/Repository/Venta/Precio.php new file mode 100644 index 0000000..f7bb16d --- /dev/null +++ b/app/src/Repository/Venta/Precio.php @@ -0,0 +1,55 @@ +setTable('precio'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'unidad' => [ + 'function' => function($data) { + return $this->unidadRepository->fetchById($data['unidad']); + } + ], + 'valor' => [] + ]; + return $this->parseData(new Model\Venta\Precio(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['unidad', 'valor'], + [$model->unidad->id, $model->valor] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['unidad', 'valor'], $new_data); + } + + public function fetchByProyecto(int $proyecto_id): array + { + $query = "SELECT a.* +FROM `{$this->getTable()}` a + JOIN (SELECT e1.* FROM `estado_precio` e1 JOIN (SELECT MAX(`id`) AS 'id', `precio` FROM `estado_precio` GROUP BY `precio`) e0 ON e0.`id` = e1.`id`) ep ON ep.`precio` = a.`id` + JOIN `tipo_estado_precio` tep ON tep.`id` = ep.`estado` + JOIN `unidad` ON `unidad`.`id` = a.`unidad` + JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt` + JOIN `tipo_unidad` tu ON tu.`id` = ptu.`tipo` +WHERE ptu.`proyecto` = ? AND tep.`descripcion` = 'vigente' +ORDER BY tu.`orden`, ptu.`nombre`, `unidad`.`subtipo`, LPAD(`unidad`.`descripcion`, 4, '0')"; + return $this->fetchMany($query, [$proyecto_id]); + } +} diff --git a/app/src/Repository/Venta/Propietario.php b/app/src/Repository/Venta/Propietario.php new file mode 100644 index 0000000..8e0dce6 --- /dev/null +++ b/app/src/Repository/Venta/Propietario.php @@ -0,0 +1,82 @@ +setTable('propietario'); + } + + protected function getKey(): string + { + return 'rut'; + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'dv' => [], + 'nombres' => [], + 'apellido_paterno' => [ + 'property' => 'apellidos', + 'function' => function($data) { + $arr = [ + 'paterno' => $data['apellido_paterno'] + ]; + if ($data['apellido_materno'] !== '') { + $arr['materno'] = $data['apellido_materno']; + } + return $arr; + } + ], + 'direccion' => [ + 'property' => 'datos', + 'function' => function($data) { + $datos = new Model\Venta\Datos(); + if ($data['direccion'] !== null and $data['direccion'] !== 0) { + $datos->direccion = $this->direccionRepository->fetchById($data['direccion']); + } + return $datos; + } + ], + 'representante' => [ + 'function' => function($data) { + if ($data['representante'] === null or $data['representante'] === 0) { + return null; + } + return $this->fetchById($data['representante']); + } + ], + 'otro' => [ + 'function' => function($data) { + if ($data['otro'] === null) { + return null; + } + return $data['otro'] !== 0; + } + ] + ]; + return $this->parseData(new Model\Venta\Propietario(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->rut = $this->saveNew( + ['dv', 'nombres', 'apellido_paterno', 'apellido_materno'], + [$model->dv, $model->nombres, $model->apellidos['paterno'], $model->apellidos['materno']] + ); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['dv', 'nombres', 'apellido_paterno', 'apellido_materno'], $new_data); + } +} diff --git a/app/src/Repository/Venta/TipoEstadoPago.php b/app/src/Repository/Venta/TipoEstadoPago.php new file mode 100644 index 0000000..80f8a0b --- /dev/null +++ b/app/src/Repository/Venta/TipoEstadoPago.php @@ -0,0 +1,40 @@ +setTable('tipo_estado_pago'); + } + + public function create(?array $data = null): Define\Model + { + $map = ['descripcion' => []]; + return $this->parseData(new Model\Venta\TipoEstadoPago(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion'], + [$model->descripcion] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion'], $new_data); + } + + public function fetchByDescripcion(string $descripcion): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `descripcion` = ?"; + return $this->fetchOne($query, [$descripcion]); + } +} diff --git a/app/src/Repository/Venta/TipoEstadoPrecio.php b/app/src/Repository/Venta/TipoEstadoPrecio.php new file mode 100644 index 0000000..1955b2e --- /dev/null +++ b/app/src/Repository/Venta/TipoEstadoPrecio.php @@ -0,0 +1,35 @@ +setTable('tipo_estado_precio'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [] + ]; + return $this->parseData(new Model\Venta\TipoEstadoPrecio(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion'], + [$model->descripcion] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion'], $new_data); + } +} diff --git a/app/src/Repository/Venta/TipoPago.php b/app/src/Repository/Venta/TipoPago.php new file mode 100644 index 0000000..dda6865 --- /dev/null +++ b/app/src/Repository/Venta/TipoPago.php @@ -0,0 +1,35 @@ +setTable('tipo_pago'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [] + ]; + return $this->parseData(new Model\Venta\TipoPago(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion'], + [$model->descripcion] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion'], $new_data); + } +} diff --git a/app/src/Repository/Venta/TipoUnidad.php b/app/src/Repository/Venta/TipoUnidad.php new file mode 100644 index 0000000..13474bb --- /dev/null +++ b/app/src/Repository/Venta/TipoUnidad.php @@ -0,0 +1,39 @@ +setTable('tipo_unidad'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'descripcion' => [], + 'orden' => [] + ]; + return $this->parseData(new Model\Venta\TipoUnidad(), $data, $map); + } + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['descripcion', 'orden'], + [$model->descripcion, $model->orden] + ); + return $model; + } + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['descripcion', 'orden'], $new_data); + } +} + + diff --git a/app/src/Repository/Venta/Unidad.php b/app/src/Repository/Venta/Unidad.php new file mode 100644 index 0000000..4783c40 --- /dev/null +++ b/app/src/Repository/Venta/Unidad.php @@ -0,0 +1,47 @@ +setTable('unidad'); + } + + public function create(?array $data = null): Define\Model + { + $map = [ + 'subtipo' => [], + 'piso' => [], + 'descripcion' => [], + 'orientacion' => [], + 'pt' => [ + 'property' => 'proyectoTipoUnidad', + 'function' => function($data) { + return $this->proyectoTipoUnidadRepository->fetchById($data['pt']); + } + ] + ]; + return $this->parseData(new Model\Venta\Unidad(), $data, $map); + } + + public function save(Define\Model $model): Define\Model + { + $model->id = $this->saveNew( + ['subtipo', 'piso', 'descripcion', 'orientacion', 'pt'], + [$model->subtipo, $model->piso, $model->descripcion, $model->orientacion, $model->proyectoTipoUnidad->id] + ); + return $model; + } + + public function edit(Define\Model $model, array $new_data): Define\Model + { + return $this->update($model, ['subtipo', 'piso', 'descripcion', 'orientacion', 'pt'], $new_data); + } +} diff --git a/app/src/Service/Format.php b/app/src/Service/Format.php new file mode 100644 index 0000000..ec9177b --- /dev/null +++ b/app/src/Service/Format.php @@ -0,0 +1,24 @@ +setPattern($format); + return $formatter->format($date); + + } + public function pesos(string $valor): string + { + return '$' . number_format($valor, 0, ',', '.'); + } +} diff --git a/app/src/Service/Login.php b/app/src/Service/Login.php new file mode 100644 index 0000000..175be0d --- /dev/null +++ b/app/src/Service/Login.php @@ -0,0 +1,135 @@ +loadCookie(); + } + + protected string $selector = ''; + protected string $token = ''; + + public function isIn(): bool + { + try { + $login = $this->repository->fetchActiveBySelector($this->selector); + if (!$this->validToken($login)) { + return false; + } + $now = new DateTimeImmutable(); + if ($login->dateTime->add(new DateInterval("PT{$this->max_login_time}H")) > $now) { + return true; + } + } catch (PDOException|EmptyResult) { + } + return false; + } + public function getUser(): Model\User + { + $login = $this->repository->fetchActiveBySelector($this->selector); + if (!$this->validToken($login)) { + throw new Exception('User not found'); + } + return $login->user; + } + + public function login(Model\User $user): bool + { + try { + $login = $this->repository->fetchActiveByUser($user->id); + $this->logout($login->user); + } catch (PDOException|EmptyResult) { + } + + try { + $now = new DateTimeImmutable(); + $login = $this->repository->create([ + 'user_id' => $user->id, + 'time' => $now->format('Y-m-d H:i:s'), + 'status' => 1 + ]); + list('selector' => $selector, 'token' => $token) = $this->generateToken($login); + $login->selector = $selector; + $login->token = password_hash($token, PASSWORD_DEFAULT); + $this->repository->save($login); + $this->saveCookie($selector, $token, $login->dateTime->add(new DateInterval("PT{$this->max_login_time}H"))); + return true; + } catch (PDOException|Exception) { + return false; + } + } + public function logout(Model\User $user): bool + { + $this->removeCookie(); + try { + $logins = $this->repository->fetchByUser($user->id); + } catch (PDOException) { + return true; + } + try { + foreach ($logins as $login) { + $this->repository->edit($login, ['status' => 0]); + } + return true; + } catch (PDOException) { + return false; + } + } + + protected function loadCookie(): void + { + if (!isset($_COOKIE[$this->cookie_name])) { + return; + } + $cookie = $_COOKIE[$this->cookie_name]; + list($this->selector, $this->token) = explode($this->cookie_separator, $cookie); + } + protected function saveCookie(string $selector, string $token, DateTimeInterface $expires): void + { + setcookie( + $this->cookie_name, + implode($this->cookie_separator, [$selector, $token]), + $expires->getTimestamp(), + $this->path, + $this->domain + ); + $this->selector = $selector; + $this->token = $token; + } + protected function removeCookie(): void + { + setcookie( + $this->cookie_name, + '', + (new DateTimeImmutable())->getTimestamp(), + $this->path, + $this->domain + ); + } + + protected function validToken(Model\Login $login): bool + { + return password_verify($this->token, $login->token); + } + protected function generateToken(Model\Login $login) + { + $selector = bin2hex(random_bytes(12)); + $token = bin2hex(random_bytes(20)); + return ['selector' => $selector, 'token' => $token]; + } +} diff --git a/app/src/Service/Ventas/Pago.php b/app/src/Service/Ventas/Pago.php new file mode 100644 index 0000000..f931f38 --- /dev/null +++ b/app/src/Service/Ventas/Pago.php @@ -0,0 +1,31 @@ +tipoEstadoPagoRepository->fetchByDescripcion('depositado'); + $data = [ + 'pago' => $pago->id, + 'estado' => $tipo_estado->id, + 'fecha' => (new DateTimeImmutable())->format('Y-m-d') + ]; + try { + $estado = $this->estadoPagoRepository->create($data); + $this->estadoPagoRepository->save($estado); + return true; + } catch (PDOException) { + return false; + } + } +} diff --git a/app/src/Service/Ventas/Precio.php b/app/src/Service/Ventas/Precio.php new file mode 100644 index 0000000..b03174a --- /dev/null +++ b/app/src/Service/Ventas/Precio.php @@ -0,0 +1,19 @@ +precioRepository->fetchByProyecto($proyecto_id); + foreach ($precios as &$precio) { + $precio->estados = $this->estadoPrecioRepository->fetchByPrecio($precio->id); + $precio->current = $this->estadoPrecioRepository->fetchCurrentByPrecio($precio->id); + } + return $precios; + } +}