diff --git a/.adminer.env b/.adminer.env new file mode 100644 index 0000000..7e78794 --- /dev/null +++ b/.adminer.env @@ -0,0 +1,2 @@ +ADMINER_DESIGN=dracula +ADMINER_PLUGINS=dump-json diff --git a/.db.env b/.db.env new file mode 100644 index 0000000..b88ed98 --- /dev/null +++ b/.db.env @@ -0,0 +1,4 @@ +MYSQL_DATABASE=incoviba +MYSQL_PASSWORD=5GQYFvRjVw2A4KcD +MYSQL_ROOT_PASSWORD=password +MYSQL_USER=incoviba diff --git a/.gitignore b/.gitignore index d51536b..5a16a81 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ package-lock.json Pipfile.lock /logs/ /cache/ +/modules/ diff --git a/app/Controller/Informes.php b/app/Controller/Informes.php index 6a65fe9..67cb000 100644 --- a/app/Controller/Informes.php +++ b/app/Controller/Informes.php @@ -410,6 +410,8 @@ class Informes public static function ventas() { if (get('proyecto')) { + ini_set('memory_limit', "1G"); + ini_set('max_execution_time', '3600'); $id = get('proyecto'); $proyecto = model(Proyecto::class)->findOne($id); $ventas = $proyecto->ventas(); @@ -434,7 +436,7 @@ class Informes $columnas = [ 'Propietario', - ['name' => 'Departamento', 'style' => 'number'], + 'Departamento', ['name' => 'Estacionamientos', 'style' => 'number'], ['name' => 'Bodegas', 'style' => 'number'], 'Fecha Venta', @@ -457,7 +459,8 @@ class Informes ['name' => 'Valor Neto', 'style' => 'amount'], ['name' => 'UF/m²*', 'style' => 'amount'], ['name' => 'Comision', 'style' => 'amount'], - ['name' => 'Venta s/Comision', 'style' => 'amount'] + ['name' => 'Venta s/Comision', 'style' => 'amount']. + ['name' => 'Precio', 'style' => 'Amount'] ]; //$informe->addColumns($columnas); @@ -465,28 +468,14 @@ class Informes foreach ($ventas as $venta) { $info = []; $info['Propietario'] = mb_strtoupper($venta->propietario()->nombreCompleto()); - $info['Departamento'] = trim(array_reduce($venta->propiedad()->departamentos(), function($carry, $item) { - return implode(' - ', [$carry, $item->descripcion]); - }), ' -'); - //$ests = []; + $info['Departamento'] = implode(' - ', array_map(function($item) { + return $item->descripcion; + }, $venta->propiedad()->departamentos())); $es = $venta->propiedad()->estacionamientos(); - /*if (count($es) > 0) { - foreach ($es as $e) { - $ests []= $e->descripcion; - } - } - $info['Estacionamientos'] = implode(', ', $ests);*/ $info['Estacionamientos'] = implode(', ', array_map(function($item) { return $item->descripcion; }, $es)); - //$bods = []; $bs = $venta->propiedad()->bodegas(); - /*if (count($bs) > 0) { - foreach ($bs as $b) { - $bods []= $b->descripcion; - } - } - $info['Bodegas'] = implode(', ', $bods);*/ $info['Bodegas'] = implode(', ', array_map(function($item) { return $item->descripcion; }, $bs)); @@ -495,7 +484,7 @@ class Informes $info['Tipo'] = $venta->unidad()->abreviacion; $info['m² Ponderados'] = $venta->unidad()->m2('vendible'); $info['Valor Promesa'] = $venta->valor_uf; - $info['Pie'] = $venta->pie()->valor; + $info['Pie'] = $venta->pie()->valor; $info['Pie Pagado'] = 0; $info['% Pie Pagado'] = 0; if ($venta->pie()) { @@ -508,12 +497,6 @@ class Informes $info['Valor Operador'] = $venta->valorComision(); //$promos = 0; $ps = $venta->promociones(); - /*if (count($ps) > 0) { - foreach ($ps as $promo) { - $promos += $promo->valor; - } - } - $info['Premios'] = $promos;*/ $info['Premios'] = array_reduce($ps, function($sum, $item) { return $sum + $item->valor; }); @@ -536,31 +519,17 @@ class Informes $info['UF/m²*'] = $venta->uf_m2(); $info['Comision'] = $venta->valorFinal() * $comision; $info['Venta s/Comision'] = $venta->valorFinal() - $info['Comision']; + $fecha = $venta->fecha(); + $info['Precio'] = 0; + try { + $info['Precio'] = array_reduce($venta->propiedad()->departamentos(), function($sum, $item) use ($fecha) { + return $sum + $item->precio($fecha)->valor; + }); + } catch (\Exception $e) { + } $data []= $info; } - /*$informe->addData($data); - - $totals = [ - 'Propietario' => 'TOTAL', - 'Departamento' => 'count', - 'Estacionamientos' => 'count', - 'Bodegas' => 'count', - 'm² Ponderados' => 'sum', - 'Valor Promesa' => 'sum', - 'Bono Pie' => 'sum', - 'Subsidio' => 'sum', - 'Ahorro' => 'sum', - 'Credito' => 'sum', - 'Valor Operador' => 'sum', - 'Premios' => 'sum', - 'Valor Ests & Bods' => 'sum', - 'Valor Neto' => 'sum', - 'Comision' => 'sum' - ]; - $informe->addTotals($totals); - - return $informe->informe();*/ $body = [ "Proyecto" => $proyecto->descripcion, diff --git a/app/Controller/Ventas.php b/app/Controller/Ventas.php index e66faf2..8bd60b3 100644 --- a/app/Controller/Ventas.php +++ b/app/Controller/Ventas.php @@ -163,53 +163,25 @@ class Ventas $unis = json_decode(post('unidades')); $id_principal = array_shift($unis); $principal = model(Unidad::class)->findOne(post('unidad' . $id_principal)); - $propiedad = model(Propiedad::class) - ->select('propiedad.*') - ->join('unidad', ['unidad.id', '=', 'propiedad.unidad_principal']) - ->where('propiedad.unidad_principal', $principal->id) - ->where('unidad.proyecto', post('proyecto')) - ->orderByDesc('propiedad.id') - ->findOne(); // Revisar si existe la propiedad y si está vigente. - if (!$propiedad or ($propiedad->venta() and $propiedad->venta()->estado() and $propiedad->venta()->estado()->tipo()->descripcion != 'vigente')) { - if (!$propiedad) { - $propiedad = model(Propiedad::class)->create(); - } - $propiedad->unidad_principal = $principal->id; - $propiedad->save(); + $propiedad = model(Propiedad::class)->create(); + $propiedad->unidad_principal = $principal->id; + $propiedad->save(); + $data = [ + 'propiedad' => $propiedad->id, + 'unidad' => $principal->id, + 'principal' => 1 + ]; + $pu = model(PropiedadUnidad::class)->create($data); + $pu->save(); + foreach ($unis as $id_unidad) { $data = [ 'propiedad' => $propiedad->id, - 'unidad' => $principal->id, - 'principal' => 1 + 'unidad' => post('unidad' . $id_unidad), + 'principal' => 0 ]; $pu = model(PropiedadUnidad::class)->create($data); $pu->save(); - foreach ($unis as $id_unidad) { - $data = [ - 'propiedad' => $propiedad->id, - 'unidad' => post('unidad' . $id_unidad), - 'principal' => 0 - ]; - $pu = model(PropiedadUnidad::class)->create($data); - $pu->save(); - } - /*$ests = []; - $bods = []; - foreach ($unis as $id_unidad) { - $unidad = model(Unidad::class)->findOne(post('unidad' . $id_unidad)); - if ($unidad->tipo == 2) { - $ests []= $unidad->id; - } - if ($unidad->tipo == 3) { - $bods []= $unidad->id; - } - } - $propiedad->estacionamientos = implode(';', $ests); - $propiedad->bodegas = implode(';', $bods); - $propiedad->save();*/ - } elseif ($propiedad->venta() and $propiedad->venta()->estado()->tipo()->descripcion == 'vigente') { - // Existe la propiedad en este proyecto y está vigente. Error, no se debiese vender si está vigente. - throw new \Exception('Existe la propiedad en este proyecto y está vigente. Error, no se debiese vender si está vigente.'); } $venta = model(Venta::class)->create(); diff --git a/bin/informes/controllers/ventas.py b/bin/informes/controllers/ventas.py index 8b059ce..5078423 100644 --- a/bin/informes/controllers/ventas.py +++ b/bin/informes/controllers/ventas.py @@ -1,141 +1,141 @@ -from flask import Blueprint, request, Response -import io -import json -import pandas as pd -import numpy as np -import datetime - - -ventas_blueprint = Blueprint('ventas', __name__,) - - -def format_data(data): - df = pd.DataFrame(data) - int_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] - for col in int_columns: - df[col] = pd.to_numeric(df[col], errors='ignore') - float_columns = ['Valor Promesa', 'Pie', 'Pie Pagado', '% Pie Pagado', 'Bono Pie', 'Valor Operador', - 'Premios', 'Subsidio', 'Ahorro', 'Credito', 'Valor Ests & Bods', 'Valor Neto', - 'UF/m²*', 'Comision', 'Venta s/Comision'] - for col in float_columns: - if col in df.columns: - df[col] = pd.to_numeric(df[col], errors='coerce') - df = df.replace(np.nan, 0, regex=True) - - df['Fecha Venta'] = pd.to_datetime(df['Fecha Venta'], format='%Y-%m-%d') - df = df.sort_values(by=['Fecha Venta', 'Departamento']) - df['Fecha Venta'] = df['Fecha Venta'].dt.strftime('%d.%m.%Y') - - return df - - -def format_columns(workbook, columns): - column_settings = [{'header': column} for column in columns] - - center_format = workbook.add_format({'align': 'center'}) - amount_format = workbook.add_format({'num_format': '#,##0.00'}) - percent_format = workbook.add_format({'num_format': '0.00%'}) - - amount_columns = ['m² Ponderados', 'Valor Promesa', 'Pie', 'Pie Pagado', 'Bono Pie', 'Valor Operador', 'Premios', - 'Subsidio', 'Ahorro', 'Credito', 'Valor Ests & Bods', 'Valor Neto', 'UF/m²*', 'Comision', - 'Venta s/Comision'] - center_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] - sum_columns = ['m² Ponderados', 'Valor Promesa', 'Pie', 'Pie Pagado', 'Bono Pie', 'Subsidio', 'Ahorro', 'Credito', - 'Valor Operador', 'Premios', 'Valor Ests & Bods', 'Valor Neto', 'Comision'] - count_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] - - for (k, col) in enumerate(column_settings): - if col['header'] in amount_columns: - column_settings[k]['format'] = amount_format - if col['header'] in center_columns: - column_settings[k]['format'] = center_format - if col['header'] in sum_columns: - column_settings[k]['total_function'] = 'sum' - if col['header'] in count_columns: - column_settings[k]['total_function'] = 'count' - if col['header'] == '% Pie Pagado': - column_settings[k]['format'] = percent_format - if col['header'] == 'Propietario': - column_settings[k]['total_string'] = 'TOTAL' - - return column_settings - - -def format_excel(workbook, worksheet, data): - center_format = workbook.add_format({'align': 'center'}) - amount_format = workbook.add_format({'num_format': '#,##0.00'}) - percent_format = workbook.add_format({'num_format': '0.00%'}) - - amount_columns = ['m² Ponderados', 'Valor Promesa', 'Pie', 'Pie Pagado', 'Bono Pie', 'Valor Operador', 'Premios', - 'Subsidio', 'Ahorro', 'Credito', 'Valor Ests & Bods', 'Valor Neto', 'UF/m²*', 'Comision', - 'Venta s/Comision'] - center_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] - - for col in amount_columns: - if col not in data.columns: - continue - k = data.columns.get_loc(col) + 1 - worksheet.set_column(k, k, cell_format=amount_format) - for col in center_columns: - if col not in data.columns: - continue - k = data.columns.get_loc(col) + 1 - worksheet.set_column(k, k, cell_format=center_format) - col = '% Pie Pagado' - k = data.columns.get_loc(col) + 1 - worksheet.set_column(k, k, cell_format=percent_format) - - return worksheet - - -@ventas_blueprint.route('', methods=['POST']) -def ventas(): - data = json.loads(request.data) - output = io.BytesIO() - writer = pd.ExcelWriter('tmp.xlsx', engine='xlsxwriter') - df = format_data(data['data']) - - start_row = 4 - if 'Compañía' in data: - start_row += 1 - df.to_excel(writer, startrow=start_row+1, startcol=1, header=False, index=False, sheet_name='Ventas') - wb = writer.book - wb.filename = output - - title_format = wb.add_format({'font_size': 16, 'bold': True}) - ws = writer.sheets['Ventas'] - (max_row, max_col) = df.shape - - if 'Compañía' in data: - ws.merge_range(1, 1, 1, max_col, data['Compañía']) - ws.merge_range(start_row-3, 1, start_row-3, max_col, data['Proyecto'], cell_format=title_format) - ws.write_string(start_row-2, 1, 'Ventas') - column_settings = format_columns(wb, df.columns) - ws.add_table(start_row, 1, max_row+start_row+1, max_col, {'name': 'Ventas', 'total_row': 1, - 'columns': column_settings, - 'style': 'Table Style Medium 1'}) - ws = format_excel(wb, ws, df) - - column_widths = { - 'B': 46, - 'C': 16, - 'D': 18, - 'E': 10, - 'F': 13, - 'G': 7, - 'H': 8, - 'I': 16, - 'J': 16, - 'K': 13, - 'L': 14 - } - for (col, wd) in column_widths.items(): - ws.set_column('{0}:{0}'.format(col), wd) - - writer.save() - output.seek(0) - date = datetime.date.today() - filename = "Informe de Ventas - {0} - {1}.xlsx".format(data['Proyecto'], date.strftime('%Y-%m-%d')) - return Response(output, mimetype="application/ms-excel", - headers={"Content-Disposition": "attachment;filename={0}".format(filename), - "Content-Type": 'application/octet-stream; charset=utf-8'}) +from flask import Blueprint, request, Response +import io +import json +import pandas as pd +import numpy as np +import datetime + + +ventas_blueprint = Blueprint('ventas', __name__,) + + +def format_data(data): + df = pd.DataFrame(data) + int_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] + for col in int_columns: + df[col] = pd.to_numeric(df[col], errors='ignore') + float_columns = ['Valor Promesa', 'Pie', 'Pie Pagado', '% Pie Pagado', 'Bono Pie', 'Valor Operador', + 'Premios', 'Subsidio', 'Ahorro', 'Credito', 'Valor Ests & Bods', 'Valor Neto', + 'UF/m²*', 'Comision', 'Venta s/Comision', 'Precio'] + for col in float_columns: + if col in df.columns: + df[col] = pd.to_numeric(df[col], errors='coerce') + df = df.replace(np.nan, 0, regex=True) + + df['Fecha Venta'] = pd.to_datetime(df['Fecha Venta'], format='%Y-%m-%d') + df = df.sort_values(by=['Fecha Venta', 'Departamento']) + df['Fecha Venta'] = df['Fecha Venta'].dt.strftime('%d.%m.%Y') + + return df + + +def format_columns(workbook, columns): + column_settings = [{'header': column} for column in columns] + + center_format = workbook.add_format({'align': 'center'}) + amount_format = workbook.add_format({'num_format': '#,##0.00'}) + percent_format = workbook.add_format({'num_format': '0.00%'}) + + amount_columns = ['m² Ponderados', 'Valor Promesa', 'Pie', 'Pie Pagado', 'Bono Pie', 'Valor Operador', 'Premios', + 'Subsidio', 'Ahorro', 'Credito', 'Valor Ests & Bods', 'Valor Neto', 'UF/m²*', 'Comision', + 'Venta s/Comision', 'Precio'] + center_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] + sum_columns = ['m² Ponderados', 'Valor Promesa', 'Pie', 'Pie Pagado', 'Bono Pie', 'Subsidio', 'Ahorro', 'Credito', + 'Valor Operador', 'Premios', 'Valor Ests & Bods', 'Valor Neto', 'Comision'] + count_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] + + for (k, col) in enumerate(column_settings): + if col['header'] in amount_columns: + column_settings[k]['format'] = amount_format + if col['header'] in center_columns: + column_settings[k]['format'] = center_format + if col['header'] in sum_columns: + column_settings[k]['total_function'] = 'sum' + if col['header'] in count_columns: + column_settings[k]['total_function'] = 'count' + if col['header'] == '% Pie Pagado': + column_settings[k]['format'] = percent_format + if col['header'] == 'Propietario': + column_settings[k]['total_string'] = 'TOTAL' + + return column_settings + + +def format_excel(workbook, worksheet, data): + center_format = workbook.add_format({'align': 'center'}) + amount_format = workbook.add_format({'num_format': '#,##0.00'}) + percent_format = workbook.add_format({'num_format': '0.00%'}) + + amount_columns = ['m² Ponderados', 'Valor Promesa', 'Pie', 'Pie Pagado', 'Bono Pie', 'Valor Operador', 'Premios', + 'Subsidio', 'Ahorro', 'Credito', 'Valor Ests & Bods', 'Valor Neto', 'UF/m²*', 'Comision', + 'Venta s/Comision', 'Precio'] + center_columns = ['Departamento', 'Estacionamientos', 'Bodegas'] + + for col in amount_columns: + if col not in data.columns: + continue + k = data.columns.get_loc(col) + 1 + worksheet.set_column(k, k, cell_format=amount_format) + for col in center_columns: + if col not in data.columns: + continue + k = data.columns.get_loc(col) + 1 + worksheet.set_column(k, k, cell_format=center_format) + col = '% Pie Pagado' + k = data.columns.get_loc(col) + 1 + worksheet.set_column(k, k, cell_format=percent_format) + + return worksheet + + +@ventas_blueprint.route('', methods=['POST']) +def ventas(): + data = json.loads(request.data) + output = io.BytesIO() + writer = pd.ExcelWriter('tmp.xlsx', engine='xlsxwriter') + df = format_data(data['data']) + + start_row = 4 + if 'Compañía' in data: + start_row += 1 + df.to_excel(writer, startrow=start_row+1, startcol=1, header=False, index=False, sheet_name='Ventas') + wb = writer.book + wb.filename = output + + title_format = wb.add_format({'font_size': 16, 'bold': True}) + ws = writer.sheets['Ventas'] + (max_row, max_col) = df.shape + + if 'Compañía' in data: + ws.merge_range(1, 1, 1, max_col, data['Compañía']) + ws.merge_range(start_row-3, 1, start_row-3, max_col, data['Proyecto'], cell_format=title_format) + ws.write_string(start_row-2, 1, 'Ventas') + column_settings = format_columns(wb, df.columns) + ws.add_table(start_row, 1, max_row+start_row+1, max_col, {'name': 'Ventas', 'total_row': 1, + 'columns': column_settings, + 'style': 'Table Style Medium 1'}) + ws = format_excel(wb, ws, df) + + column_widths = { + 'B': 46, + 'C': 16, + 'D': 18, + 'E': 10, + 'F': 13, + 'G': 7, + 'H': 8, + 'I': 16, + 'J': 16, + 'K': 13, + 'L': 14 + } + for (col, wd) in column_widths.items(): + ws.set_column('{0}:{0}'.format(col), wd) + + writer.save() + output.seek(0) + date = datetime.date.today() + filename = "Informe de Ventas - {0} - {1}.xlsx".format(data['Proyecto'], date.strftime('%Y-%m-%d')) + return Response(output, mimetype="application/ms-excel", + headers={"Content-Disposition": "attachment;filename={0}".format(filename), + "Content-Type": 'application/octet-stream; charset=utf-8'}) diff --git a/config/app.php b/config/app.php index ce713ab..5ed5da7 100644 --- a/config/app.php +++ b/config/app.php @@ -7,4 +7,4 @@ return [ 'benchmark' => false, 'login_hours' => 5*24 ]; -?> \ No newline at end of file +?> diff --git a/docker-compose.yml b/docker-compose.yml index ba91d4a..0fc3e18 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,12 +23,8 @@ services: image: mariadb:latest restart: unless-stopped ports: - - 3307:3306 - environment: - MYSQL_ROOT_PASSWORD: 'password' - MYSQL_DATABASE: 'incoviba' - MYSQL_USER: 'incoviba' - MYSQL_PASSWORD: '5GQYFvRjVw2A4KcD' + - 3308:3306 + env_file: .db.env volumes: - dbdata:/var/lib/mysql @@ -37,10 +33,8 @@ services: image: adminer:latest restart: unless-stopped ports: - - 8082:8080 - environment: - ADMINER_DESIGN: 'dracula' - ADMINER_PLUGINS: 'dump-json' + - 8083:8080 + env_file: .adminer.env volumes: dbdata: diff --git a/incoviba/modelos/src/old/Proyecto/Proyecto.php b/incoviba/modelos/src/old/Proyecto/Proyecto.php index 5611ef6..aac09ae 100644 --- a/incoviba/modelos/src/old/Proyecto/Proyecto.php +++ b/incoviba/modelos/src/old/Proyecto/Proyecto.php @@ -131,7 +131,7 @@ class Proyecto extends Model $unidades['disponibles'] = (array) $this->unidades->disponibles; } } - $q_s = "SELECT u.* + /*$q_s = "SELECT u.* FROM (SELECT * FROM unidad WHERE proyecto = ? ORDER BY tipo) AS u LEFT JOIN @@ -158,7 +158,18 @@ class Proyecto extends Model OR propiedad.bodegas LIKE CONCAT('%;', unidad.id) OR propiedad.bodegas LIKE CONCAT('%;', unidad.id, ';%') WHERE venta.estado = 1) AS b ON b.id = u.id - WHERE v.id IS NULL AND e.id IS NULL AND b.id IS NULL"; + WHERE v.id IS NULL AND e.id IS NULL AND b.id IS NULL";*/ + $q_s = "SELECT u.* + FROM + (SELECT * FROM unidad WHERE proyecto = ? ORDER BY tipo) AS u + LEFT JOIN (SELECT unidad.* + FROM unidad + JOIN propiedad_unidad pu ON pu.unidad = unidad.id + JOIN venta ON venta.propiedad = pu.propiedad + JOIN (SELECT e1.* FROM estado_venta e1 + JOIN (SELECT venta, MAX(id) AS id FROM estado_venta GROUP BY venta) e0 ON e0.id = e1.id) ev ON ev.venta = venta.id + WHERE ev.estado = 1) AS v ON v.id = u.id + WHERE v.id IS NULL"; $q_p = " ORDER BY u.tipo, LPAD(u.descripcion, 4, '0')"; switch (strtolower($id_tipo)) { case null: diff --git a/modules/operadores b/modules/operadores new file mode 160000 index 0000000..71e10c5 --- /dev/null +++ b/modules/operadores @@ -0,0 +1 @@ +Subproject commit 71e10c52bddbaa7a78220b69c4dd31681bf74fd5 diff --git a/resources/views/layout/menu.blade.php b/resources/views/layout/menu.blade.php index 670e00d..d1e442b 100644 --- a/resources/views/layout/menu.blade.php +++ b/resources/views/layout/menu.blade.php @@ -33,6 +33,13 @@ @endif + + @if (\App\Contract\Auth::checkAccess('viewHerramientas'))