2024-06-18

This commit is contained in:
Juan Pablo Vial
2024-06-18 22:41:03 -04:00
parent 6169089475
commit 390e79ad6d
60 changed files with 3162 additions and 155 deletions

9
api.compose.yml Normal file
View File

@ -0,0 +1,9 @@
services:
httpclient:
profiles:
- testing
container_name: incoviba_client
image: flawiddsouza/restfox
restart: unless-stopped
ports:
- "${HTTPCLIENT_PORT:-4004}:4004"

View File

@ -37,6 +37,9 @@ abstract class Repository implements Define\Repository
$this->connection->execute($query, [$model->id]); $this->connection->execute($query, [$model->id]);
} }
/**
* @throws EmptyResult
*/
public function fetchById(int $id): Define\Model public function fetchById(int $id): Define\Model
{ {
$query = $this->connection->getQueryBuilder() $query = $this->connection->getQueryBuilder()
@ -45,6 +48,10 @@ abstract class Repository implements Define\Repository
->where("{$this->getKey()} = ?"); ->where("{$this->getKey()} = ?");
return $this->fetchOne($query, [$id]); return $this->fetchOne($query, [$id]);
} }
/**
* @throws EmptyResult
*/
public function fetchAll(null|string|array $ordering = null): array public function fetchAll(null|string|array $ordering = null): array
{ {
$query = $this->connection->getQueryBuilder() $query = $this->connection->getQueryBuilder()

View File

@ -2,12 +2,18 @@
namespace Incoviba\Common\Implement\Exception; namespace Incoviba\Common\Implement\Exception;
use Throwable; use Throwable;
use HttpResponseException; use Exception;
class HttpResponse extends HttpResponseException class HttpResponse extends Exception
{ {
public function __construct($message = "", $code = 0, Throwable $previous = null) public function __construct($reason = "", $message = "", $code = 0, Throwable $previous = null)
{ {
$this->reason = "HTTP Reason: {$reason}";
parent::__construct($message, $code, $previous); parent::__construct($message, $code, $previous);
} }
protected string $reason;
public function getReason(): string
{
return $this->reason;
}
} }

View File

@ -2,5 +2,15 @@
use Incoviba\Controller\Inmobiliarias; use Incoviba\Controller\Inmobiliarias;
$app->group('/inmobiliarias', function($app) { $app->group('/inmobiliarias', function($app) {
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'inmobiliarias']);
if (file_exists($folder)) {
$files = new FilesystemIterator($folder);
foreach ($files as $file) {
if ($file->isDir()) {
continue;
}
include_once $file->getRealPath();
}
}
$app->get('[/]', Inmobiliarias::class); $app->get('[/]', Inmobiliarias::class);
})->add($app->getContainer()->get(Incoviba\Middleware\Authentication::class)); })->add($app->getContainer()->get(Incoviba\Middleware\Authentication::class));

View File

@ -1,5 +1,4 @@
<?php <?php
use Incoviba\Controller\API\Contabilidad\Nubox; use Incoviba\Controller\API\Contabilidad\Nubox;
$app->group('/nubox/{inmobiliaria_rut}', function($app) { $app->group('/nubox/{inmobiliaria_rut}', function($app) {
@ -10,5 +9,6 @@ $app->group('/nubox/{inmobiliaria_rut}', function($app) {
$app->post('/mayor[/]', [Nubox::class, 'libroMayor']); $app->post('/mayor[/]', [Nubox::class, 'libroMayor']);
$app->post('/diario[/]', [Nubox::class, 'libroDiario']); $app->post('/diario[/]', [Nubox::class, 'libroDiario']);
}); });
$app->get('/facturas/{dia}[/]', [Nubox::class, 'facturas']);
$app->post('/cuenta', [Nubox::class, 'cuenta']); $app->post('/cuenta', [Nubox::class, 'cuenta']);
}); });

View File

@ -15,7 +15,7 @@ $app->group('/inmobiliarias', function($app) {
$app->get('[/]', Inmobiliarias::class); $app->get('[/]', Inmobiliarias::class);
}); });
$app->group('/inmobiliaria/{inmobiliaria_rut}', function($app) { $app->group('/inmobiliaria/{inmobiliaria_rut}', function($app) {
$app->post('/agentes', [Inmobiliarias::class, 'agentes']); $app->post('/proveedores', [Inmobiliarias::class, 'proveedores']);
$app->get('/cuentas[/]', [Inmobiliarias::class, 'cuentas']); $app->get('/cuentas[/]', [Inmobiliarias::class, 'cuentas']);
$app->get('/proyectos[/]', [Inmobiliarias::class, 'proyectos']); $app->get('/proyectos[/]', [Inmobiliarias::class, 'proyectos']);
}); });

View File

@ -1,11 +1,11 @@
<?php <?php
use Incoviba\Controller\API\Inmobiliarias\Agentes; use Incoviba\Controller\API\Inmobiliarias\Agentes;
$app->group('/agentes', function($app) { $app->group('/proveedores', function($app) {
$app->post('/add[/]', [Agentes::class, 'add']); $app->post('/add[/]', [Agentes::class, 'add']);
$app->post('/register[/]', [Agentes::class, 'register']); $app->post('/register[/]', [Agentes::class, 'register']);
$app->get('[/]', Agentes::class); $app->get('[/]', Agentes::class);
}); });
$app->group('/agente/{agente_id}', function($app) { $app->group('/proveedor/{agente_id}', function($app) {
$app->post('/edit[/]', [Agentes::class, 'edit']); $app->post('/edit[/]', [Agentes::class, 'edit']);
}); });

View File

@ -0,0 +1,7 @@
<?php
use Incoviba\Controller\API\Inmobiliarias\Sociedades;
$app->group('/sociedades', function($app) {
$app->post('/add[/]', [Sociedades::class, 'add']);
$app->get('[/]', Sociedades::class);
});

View File

@ -0,0 +1,17 @@
<?php
use Incoviba\Controller\API\Sociedades;
$app->group('/sociedades', function($app) {
$app->group('/add', function($app) {
$app->post('/one[/]', Sociedades::class . ':add');
$app->post('[/]', Sociedades::class . ':addMany');
});
$app->post('/get[/]', [Sociedades::class, 'getMany']);
$app->post('/edit[/]', [Sociedades::class, 'edit']);
$app->post('/delete[/]', [Sociedades::class, 'delete']);
$app->post('/asisgnar[/]', [Sociedades::class, 'asignar']);
$app->get('[/]', Sociedades::class);
});
$app->group('/sociedad/{sociedad_rut}', function($app) {
$app->get('[/]', Sociedades::class . ':get');
});

View File

@ -0,0 +1,6 @@
<?php
use Incoviba\Controller\API\Ventas\Facturas;
$app->group('/facturas', function($app) {
$app->post('/add[/]', [Facturas::class, 'add']);
});

View File

@ -0,0 +1,4 @@
<?php
use Incoviba\Controller\Contabilidad;
$app->get('/cuadratura[/]', [Contabilidad::class, 'cuadratura']);

View File

@ -0,0 +1,6 @@
<?php
use Incoviba\Controller\Inmobiliarias\Proveedores;
$app->group('/proveedores', function($app) {
$app->get('[/]', Proveedores::class);
});

View File

@ -0,0 +1,225 @@
@extends('layout.base')
@section('page_content')
<div class="ui container">
<h1 class="ui header">
Cuadratura de Cartola Mensual
</h1>
<form class="ui form" id="cuadratura_form">
<div class="ui grid">
<div class="fourteen wide column">
<div class="fields">
<div class="five wide field">
<label>Inmobiliaria</label>
<div class="ui selection search dropdown" id="inmobiliaria">
<input type="hidden" name="inmobiliaria"/>
<i class="dropdown icon"></i>
<div class="default text">Inmobiliaria</div>
<div class="menu">
@foreach ($inmobiliarias as $inmobiliaria)
<div class="item" data-value="{{$inmobiliaria->rut}}">{{$inmobiliaria->razon}}</div>
@endforeach
</div>
</div>
</div>
<div class="two wide field">
<label>Banco</label>
<div class="ui selection search dropdown" id="banco">
<input type="hidden" name="banco"/>
<i class="dropdown icon"></i>
<div class="default text">Banco</div>
<div class="menu"></div>
</div>
</div>
<div class="field">
<label>Mes</label>
<div class="ui calendar" id="mes">
<div class="ui icon input">
<i class="calendar icon"></i>
<input type="text" name="mes"/>
</div>
</div>
</div>
<div class="field">
<label for="file">Cartola</label>
<input type="file" name="file" id="file" class="ui invisible file input" />
<label for="file" class="ui icon button">
<i class="file icon"></i>
Cargar
</label>
</div>
</div>
</div>
<div class="two wide middle aligned column">
<button class="ui icon button">
Procesar
<i class="sync icon"></i>
</button>
</div>
</div>
</form>
<div class="ui two columns grid">
<div class="column"></div>
<div class="right aligned column">
<div class="ui inline active loader" id="loader"></div>
</div>
</div>
</div>
<div class="ui fluid container" id="movimientos"></div>
@endsection
@include('layout.head.styles.datatables')
@include('layout.body.scripts.datatables')
@push('page_scripts')
<script>
class LibroMayor {
props
constructor(props) {
this.props = props
}
}
class Cartola {
}
const movimientos = {
ids: {},
data: {
inmobiliaria: {
rut: '',
razon: ''
},
banco: {
id: '',
nombre: ''
}
},
get() {
return {
bancos: inmobiliaria_rut => {
const url = '{{$urls->api}}/inmobiliaria/' + inmobiliaria_rut + '/cuentas'
$(this.ids.loader).show()
return fetchAPI(url).then(response => {
$(this.ids.loader).hide()
if (!response) {
return
}
return response.json().then(json => {
if (json.cuentas.length === 0) {
return
}
$(this.ids.inputs.banco).dropdown('change values', json.cuentas.map(cuenta => {
return {value: cuenta.banco.id, text: cuenta.banco.nombre, name: cuenta.banco.nombre}
})).dropdown('refresh')
})
})
},
firstDate: inmobiliaria_rut => {
const url = '{{$urls->api}}/inmobiliaria/' + inmobiliaria_rut + '/proyectos'
$(this.ids.loader).show()
return fetchAPI(url).then(response => {
$(this.ids.loader).hide()
if (!response) {
return
}
return response.json().then(json => {
if (json.proyectos.length === 0) {
return
}
const min = json.proyectos.reduce((min, proyecto) => {
const date = new Date(proyecto.current_estado.fecha.date)
if (min > date.getTime()) {
return date.getTime()
}
return min
}, (new Date()).getTime())
$(this.ids.inputs.mes).calendar('set minDate', new Date(min))
})
})
}
}
},
parse() {
return {
cartola: submitEvent => {
submitEvent.preventDefault()
const body = new FormData(document.getElementById('asignar_form'))
body.set('mes', $(this.ids.inputs.mes).calendar('get date').toISOString())
const url = '{{$urls->api}}/contabilidad/cartolas/procesar'
$(this.ids.loader).show()
fetchAPI(url, {method: 'post', body}).then(response => {
$(this.ids.loader).hide()
if (!response) {
return
}
return response.json().then(json => {
if (json.movimientos.length === 0) {
return
}
this.data.movimientos = []
json.movimientos.forEach((row, idx) => {
const fecha = new Date(row.fecha)
fecha.setDate(fecha.getDate() + 1)
this.data.movimientos[idx] = {
fecha: fecha,
glosa: row.glosa,
documento: row.documento,
cargo: row.cargo,
abono: row.abono,
}
})
this.draw().cartola()
})
})
return false
}
}
},
setup(ids) {
this.ids = ids
$(this.ids.inputs.inmobiliaria).dropdown({
fireOnInit: true,
onChange: (value, text, $choice) => {
this.data.inmobiliaria.rut = value
this.data.inmobiliaria.razon = text
this.get().bancos(value)
this.get().firstDate(value)
},
})
$(this.ids.inputs.banco).dropdown({
fireOnInit: true,
onChange: (value, text, $choice) => {
this.data.banco.id = value
this.data.banco.nombre = text
}
})
$(this.ids.loader).hide()
calendar_date_options['type'] = 'month'
const lastMonth = new Date()
lastMonth.setDate(0)
calendar_date_options['maxDate'] = lastMonth
calendar_date_options['onChange'] = (date, text, mode) => {
this.data.mes = text
}
$(this.ids.inputs.mes).calendar(calendar_date_options)
$(this.ids.form).submit(this.parse().cartola)
//$(this.ids.button).click(this.export().cartola)
}
}
$(document).ready(() => {
movimientos.setup({
movimientos: '#movimientos',
form: '#cuadratura_form',
loader: '#loader',
inputs: {
inmobiliaria: '#inmobiliaria',
banco: '#banco',
mes: '#mes',
cartola: '#file'
}
})
})
</script>
@endpush

View File

@ -71,7 +71,7 @@
<tbody id="movimientos"></tbody> <tbody id="movimientos"></tbody>
</table> </table>
</div> </div>
<div class="ui modal" id="movimientos_modal"> <div class="ui fluid modal" id="movimientos_modal">
<div class="content"> <div class="content">
<h3 class="header"> <h3 class="header">
Editar Movimiento Editar Movimiento
@ -92,30 +92,66 @@
<form class="ui form" id="movimientos_edit"> <form class="ui form" id="movimientos_edit">
<input type="hidden" name="index"/> <input type="hidden" name="index"/>
<input type="hidden" name="id"/> <input type="hidden" name="id"/>
<div class="field"> <div class="fields" id="movimiento_data">
<label for="centro_costo">Centro de Costo</label> <div class="field">
<div class="ui selection search dropdown" id="centro_costo"> <label for="centro_costo">Centro de Costo</label>
<input type="hidden" name="centro_id"/> <div class="ui selection search dropdown" id="centro_costo">
<i class="dropdown icon"></i> <input type="hidden" name="centro_id"/>
<div class="default text">Centro de Costo</div> <i class="dropdown icon"></i>
<div class="menu"> <div class="default text">Centro de Costo</div>
@foreach($centros as $centroCosto) <div class="menu">
<div class="item" data-value="{{$centroCosto->id}}">{{$centroCosto->id}} - {{$centroCosto->descripcion}}</div> @foreach($centros as $centroCosto)
@endforeach <div class="item" data-value="{{$centroCosto->id}}">{{$centroCosto->id}} - {{$centroCosto->descripcion}}</div>
@endforeach
</div>
</div>
</div>
<div class="field">
<label>RUT</label>
<div class="ui right labeled input">
<input type="text" name="rut" placeholder="RUT" />
<div class="ui basic label">-<span id="digito"></span></div>
</div>
</div>
<div class="field">
<label>Nombre</label>
<input type="text" name="nombre" placeholder="Nombre" />
</div>
<div class="field">
<label>Categoría</label>
<input type="text" name="categoria" placeholder="Categoría" />
</div>
<div class="field">
<label for="detalle">Detalle</label>
<div class="ui input">
<textarea id="detalle" name="detalle"></textarea>
</div> </div>
</div> </div>
</div> </div>
<div class="field"> <table class="ui table" id="auxiliares">
<label for="detalle">Detalle</label> <thead>
<div class="ui input"> <tr>
<textarea id="detalle" name="detalle"></textarea> <th>Cargo</th>
</div> <th>Abono</th>
</div> <th>Centro Costo</th>
<th>RUT</th>
<th>Nombre</th>
<th>Categoría</th>
<th>Detalle</th>
<th class="right aligned">
<button class="ui green icon button" type="button" id="add_auxiliar">
<i class="plus icon"></i>
</button>
</th>
</tr>
</thead>
<tbody></tbody>
</table>
</form> </form>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui cancel button">Cancelar</div> <div class="ui red cancel button">Cancelar</div>
<div class="ui approve success button">Guardar</div> <div class="ui green approve success button">Guardar</div>
</div> </div>
</div> </div>
@endsection @endsection
@ -123,10 +159,573 @@
@include('layout.body.scripts.datatables') @include('layout.body.scripts.datatables')
@include('layout.body.scripts.datatables.searchbuilder') @include('layout.body.scripts.datatables.searchbuilder')
@include('layout.body.scripts.datatables.buttons') @include('layout.body.scripts.datatables.buttons')
@include('layout.body.scripts.rut')
@push('page_scripts') @push('page_scripts')
<script> <script>
const movimientos = { class CentrosCostos {
static data = []
static get() {
if (this.data.length === 0) {
this.data = JSON.parse('{!! json_encode($centros) !!}')
}
return this.data
}
}
</script>
@endpush
@push('page_scripts')
<script>
class EventHandler {
props
constructor({movimientosHandler, modalHandler}) {
this.props = {
movimientosHandler,
modalHandler
}
}
get() {
return {
movimientos: submitEvent => {
submitEvent.preventDefault()
submitEvent.data.handler.props.movimientosHandler.get().movimientos({
sociedades_ruts: $(submitEvent.data.sociedades_id).dropdown('get values'),
mes: $(submitEvent.data.mes_id).calendar('get date')
}).then(() => {
app.handlers.table.draw().table(app.handlers.results.props.parsed)
})
return false
}
}
}
edit() {
return {
movimiento: clickEvent => {
const id = $(clickEvent.currentTarget).data('id')
const index = $(clickEvent.currentTarget).data('index')
const type = $(clickEvent.currentTarget).data('type')
const movimiento = this.props.movimientosHandler.find().id(id)
clickEvent.data.handler.props.modalHandler.show({movimiento, type})
}
}
}
}
class LoadingHandler {
props
constructor({ids}) {
this.props = {
ids
}
}
show() {
$(this.props.id).addClass('loading')
}
hide() {
$(this.props.id).removeClass('loading')
}
}
class ControlForm {
props
constructor({ids,}) {
this.props = {
ids,
}
}
setup() {
$(this.props.ids.sociedades).dropdown()
const cdo = structuredClone(calendar_date_options)
cdo['type'] = 'month'
cdo['maxDate'] = new Date()
$(this.props.ids.mes).calendar(cdo)
$(this.props.ids.form).on('submit', {
sociedades_id: this.props.ids.sociedades,
mes_id: this.props.ids.mes,
handler: app.handlers.event
}, app.handlers.event.get().movimientos)
}
}
class MovimientosTable {
props
constructor({ids, eventHandler}) {
this.props = {
ids,
eventHandler,
movimientos: [],
formatters: {
number: new Intl.NumberFormat('es-CL'),
date: new Intl.DateTimeFormat('es-CL', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
}),
},
}
}
draw() {
return {
table: (movimientos) => {
this.props.table.clear().rows.add(this.draw().movimientos(movimientos)).draw()
$(this.props.ids.buttons.edit).on('click', {handler: this.props.eventHandler}, this.props.eventHandler.edit().movimiento)
},
movimientos: (movimientos) => {
const output = []
movimientos.forEach(movimiento => {
this.draw().movimiento({movimiento, output})
})
return output
},
movimiento: ({movimiento, output}) => {
const valor = movimiento.abono - movimiento.cargo
const fecha = movimiento.fecha
output.push([
movimiento.cuenta.inmobiliaria.sigla,//0
movimiento.cuenta.banco.nombre,//1
movimiento.cuenta.cuenta,//2
this.props.formatters.date.format(fecha),//3
[fecha.getFullYear(), fecha.getMonth() + 1, fecha.getDate()].join('-'),//4
fecha.getMonth() + 1,//5
fecha.getFullYear(),//6
this.props.formatters.number.format(valor),//7
valor,//8
(movimiento.detalles) ? movimiento.detalles.centro_costo.id : '',//9
movimiento.glosa,//10
(movimiento.detalles) ? movimiento.detalles.detalle : '',//11
'<button class="ui icon button edit_button" data-id="' + movimiento.id + '" data-index="' + movimiento.index + '" data-type="movimiento"><i class="edit icon"></i></button>',//12
])
}
}
}
setup() {
let dtD = structuredClone(datatables_defaults)
dtD = Object.assign(dtD, {
columnDefs: [
{targets: [0, 5, 9, 12], width: '5%', type: 'string'},
{targets: [1, 2, 3, 6, 10], width: '10%', type: 'string'},
{targets: [11], width: '20%', type: 'string'},
{targets: 4, visible: false},
{targets: 7, className: 'dt-right', width: '10%'},
{targets: 8, visible: false, type: 'num'}
],
order: [[4, 'asc']],
language: Object.assign(dtD.language, {
searchBuilder: {
add: 'Filtrar',
condition: 'Comparador',
clearAll: 'Resetear',
delete: 'Eliminar',
deleteTitle: 'Eliminar Titulo',
data: 'Columna',
left: 'Izquierda',
leftTitle: 'Titulo Izquierdo',
logicAnd: 'Y',
logicOr: 'O',
right: 'Derecha',
rightTitle: 'Titulo Derecho',
title: {
0: 'Filtros',
_: 'Filtros (%d)'
},
value: 'Opciones',
valueJoiner: 'y'
}
}),
layout: {
top1: {
searchBuilder: {
columns: [0, 1, 2, 4, 6, 7, 8]
}
},
top1End: {
buttons: [
{
extend: 'excelHtml5',
className: 'green',
text: 'Exportar a Excel <i class="file excel icon"></i>',
title: 'Movimientos Contables',
download: 'open',
exportOptions: {
columns: [0, 1, 2, 3, 5, 6, 8, 9, 10, 11],
format: {
body: (data, row, column, node) => {
if (column === 3) {
return data.split('-').reverse().join('-')
}
return data
}
}
}
}
]
}
},
data: this.props.movimientos,
})
this.props.table = new DataTable(this.props.ids.table, dtD)
}
}
class EditModal {
props
constructor({ids, editForm}) {
this.props = {
ids,
editForm,
movimientos: []
}
}
reset() {
$(this.props.ids.modal).find('.sociedad').text('')
$(this.props.ids.modal).find('.banco').text('')
$(this.props.ids.modal).find('.cuenta').text('')
$(this.props.ids.modal).find('.fecha').text('')
$(this.props.ids.modal).find('.valor').text('')
this.props.editForm.reset()
}
show({movimiento, type}) {
$(this.props.ids.modal).modal('show')
$(this.props.ids.modal).find('.sociedad').text('Sociedad: ' + movimiento.cuenta.inmobiliaria.sigla)
$(this.props.ids.modal).find('.banco').text('Banco: ' + movimiento.cuenta.banco.nombre)
$(this.props.ids.modal).find('.cuenta').text('Cuenta: ' + movimiento.cuenta.cuenta)
$(this.props.ids.modal).find('.fecha').text('Fecha: ' + movimiento.fecha)
$(this.props.ids.modal).find('.valor').text('Valor: ' + (movimiento.abono - movimiento.cargo))
this.props.editForm.show({modal: $(this.props.ids.modal), movimiento, type})
}
hide() {
$(this.props.ids.modal).modal('hide')
this.reset()
}
}
class EditForm {
props
constructor({ids}) {
this.props = {
ids,
movimientos: []
}
}
reset() {
$(this.props.ids.form).form('reset')
}
show({modal, movimiento, type}) {
if (movimiento.detalles) {
modal.find('#movimiento_data').show()
modal.find('#auxiliares').hide()
modal.find("[name='centro_id']").dropdown('set selected', movimiento.detalles.centro_costo.id)
modal.find("[name='rut']").val(movimiento.detalles.rut)
modal.find("#digito").text(Rut.digitoVerificador(movimiento.detalles.rut))
modal.find("[name='nombre']").val(movimiento.detalles.nombre)
modal.find("[name='categoria']").val(movimiento.detalles.categoria)
modal.find("[name='detalle']").val(movimiento.detalles.detalle)
} else {
modal.find('#movimiento_data').hide()
modal.find('#auxiliares').show()
const tbody = modal.find('#auxiliares tbody')
tbody.empty()
movimiento.auxiliares.forEach((auxiliar, i) => {
tbody.append(`<tr>
<td><input type="text" name="cargo${i}" value="${auxiliar.cargo}" /></td>
<td><input type="text" name="abono${i}" value="${auxiliar.abono}" /></td>
<td>
<div class="ui selection search dropdown">
<input type="hidden" name="centro_costo${i}" />
<i class="dropdown icon"></i>
<div class="default text">Centro de Costo</div>
<div class="menu">
${CentrosCostos.get().map(centroCosto => `<div class="item" data-value="${centroCosto.id}">${centroCosto.id} - ${centroCosto.descripcion}</div>`).join('')}
</div>
</div>
</td>
<td>
<div class="ui right labeled input">
<input type="text" name="rut${i}" value="${auxiliar.detalles.rut}" />
<div class="ui basic label">-<span class="digito" data-index="${i}">${Rut.digitoVerificador(auxiliar.detalles.rut)}</span></div>
</div>
</td>
<td><input type="text" name="nombre${i}" value="${auxiliar.detalles.nombre}" /></td>
<td><input type="text" name="categoria${i}" value="${auxiliar.detalles.categoria}" /></td>
<td><input type="text" name="detalle${i}" value="${auxiliar.detalles.detalle}" /></td>
<td><button class="ui red icon button" type="button"><i class="trash icon"></i></button></td>
</tr>`)
})
}
}
}
class MovimientosHandler {
props
constructor({urls, loadingHandler, resultsHandler}) {
this.props = {
urls,
loadingHandler,
resultsHandler
}
}
get() {
return {
movimientos: ({sociedades_ruts, mes}) => {
this.props.loadingHandler.show()
const method = 'post'
const body = new FormData()
sociedades_ruts.forEach(sociedad_rut => {
body.append('sociedades_ruts[]', sociedad_rut)
})
body.set('mes', [mes.getFullYear(), mes.getMonth() + 1, mes.getDate()].join('-'))
return APIClient.fetch(this.props.urls.get, {method, body}).then(response => {
if (!response) {
throw new Error('No se pudo obtener los movimientos')
}
return response.json().then(json => {
this.movimientos = json.movimientos
this.props.resultsHandler.props.movimientos = json.movimientos
this.props.resultsHandler.parse().movimientos()
return json.movimientos
})
}).catch(error => {
console.error(error)
}).finally(() => {
this.props.loadingHandler.hide()
})
}
}
}
edit() {
return {
movimiento: ({id, index, type, data}) => {
/*this.props.resultsHandler.parse().movimientos()
this.props.resultsHandler.props.movimientos.forEach((movimiento, i) => {
if (movimiento.id === id && movimiento.index === index) {
this.props.editModal.show({id, index, type})
}
})*/
}
}
}
find() {
return {
id: id => {
return this.props.resultsHandler.props.movimientos.find(movimiento => movimiento.id === id)
}
}
}
}
class ResultsHandler {
props
constructor() {
this.props = {
movimientos: [],
timezone: ((new Date()).getTimezoneOffset()) / -60
}
}
parse() {
return {
movimientos: () => {
const movimientos = this.props.movimientos
const data = []
movimientos.forEach((movimiento, index) => {
this.parse().movimiento({data, movimiento, index})
})
this.props.parsed = data
},
movimiento: ({data, movimiento, index}) => {
const fecha = new Date(movimiento.fecha + 'Z' + this.props.timezone)
if (movimiento.auxiliares.length > 0) {
movimiento.auxiliares.forEach((auxiliar, indexAuxiliar) => {
data.push({
tipo: 'auxiliar',
id: auxiliar.id,
index: index * 100000 + indexAuxiliar,
cuenta: movimiento.cuenta,
fecha,
cargo: auxiliar.cargo,
abono: auxiliar.abono,
detalles: auxiliar.detalles,
glosa: movimiento.glosa,
})
})
return
}
data.push({
tipo: 'movimiento',
id: movimiento.id,
index,
cuenta: movimiento.cuenta,
fecha,
cargo: movimiento.cargo,
abono: movimiento.abono,
glosa: movimiento.glosa,
detalles: movimiento.detalles,
auxiliares: movimiento.auxiliares,
})
}
}
}
}
/*const editForm = {
id: '',
data: {
index: 0,
id: 0,
sociedad: '',
banco: '',
fecha: '',
valor: 0,
glosa: '',
},
fields: {
centro_costo: '',
rut: '',
nombre: '',
categoria: '',
detalle: '',
},
auxiliares: [],
show({type, index, id, sociedad, banco, cuenta, fecha, valor, glosa, centro_costo = '', rut = '', nombre = '', categoria = '', detalle = '', auxiliares = []}) {
this.data = {
index,
id,
sociedad,
banco,
cuenta,
fecha,
valor,
glosa,
}
this.auxiliares = auxiliares
$(this.id).find('.sociedad').text('Sociedad: ' + sociedad)
$(this.id).find('.banco').text('Banco: ' + banco)
$(this.id).find('.cuenta').text('Cuenta: ' + cuenta)
$(this.id).find('.fecha').text('Fecha: ' + fecha)
$(this.id).find('.valor').text('Valor: ' + valor)
$(this.id).find('.glosa').text('Glosa: ' + glosa)
if (auxiliares.length > 0) {
this.auxiliares = auxiliares
$(this.id).find('#movimiento_data').hide()
$(this.id).find('#auxiliares').show()
const tbody = $(this.id).find('#auxiliares tbody')
tbody.empty()
auxiliares.forEach((auxiliar, i) => {
this.draw().auxiliar({auxiliar, i})
})
} else {
$(this.id).find('#movimiento_data').show()
if (type === 'auxiliar') {
$(this.id).find('#auxiliares').hide()
}
if (centro_costo !== '') {
$(this.id).find("[name='centro_costo']").dropdown('set selected', centro_costo)
}
$(this.id).find("[name='rut']").val(rut)
$(this.id).find("#digito").text(Rut.digitoVerificador(rut))
$(this.id).find("[name='nombre']").val(nombre)
$(this.id).find("[name='categoria']").val(categoria)
$(this.id).find("[name='detalle']").val(detalle)
}
$(this.id).modal('show')
},
draw() {
return {
auxiliar: ({auxiliar, i}) => {
const tbody = $(this.id).find('#auxiliares tbody')
const menu = $('<div></div>').addClass('menu')
CentrosCostos.get().forEach(centroCosto => {
menu.append(`<div class="item" data-value="${centroCosto.id}">${centroCosto.id} - ${centroCosto.descripcion}</div>`)
})
const tr = $('<tr>')
if (typeof auxiliar !== 'undefined' && auxiliar.includes('cargo')) {
tr.append(`<td><input type="text" name="cargo${i}" value="${auxiliar.cargo}" /></td>`)
} else {
tr.append(`<td><input type="text" name="cargo${i}" /></td>`)
}
if (typeof auxiliar !== 'undefined' && auxiliar.includes('abono')) {
tr.append(`<td><input type="text" name="abono${i}" value="${auxiliar.abono}" /></td>`)
} else {
tr.append(`<td><input type="text" name="abono${i}" /></td>`)
}
tr.append(
$('<td>').append($('<div>').addClass('ui selection search dropdown').attr('data-index', i).append(
`<input type="hidden" name="centro_costo${i} />`
).append(
`<i class="dropdown icon"></i>`
).append(
`<div class="default text">Centro de Costo</div>`
).append(menu)
)
)
if (typeof auxiliar !== 'undefined' && auxiliar.includes('detalles') && auxiliar.detalles.includes('rut')) {
tr.append(`<td>
<div class="ui right labeled input">
<input type="text" name="rut${i}" value="${auxiliar.detalles.rut}" />
<div class="ui basic label">-<span class="digito" data-index="${i}">${Rut.digitoVerificador(auxiliar.detalles.rut)}</span></div>
</div>
</td>`)
} else {
tr.append(`<td>
<div class="ui right labeled input">
<input type="text" name="rut${i}" />
<div class="ui basic label">-<span class="digito" data-index="${i}"></span></div>
</div>
</td>`)
}
if (typeof auxiliar !== 'undefined' && auxiliar.includes('nombre')) {
tr.append(`<td><input type="text" name="nombre${i}" value="${auxiliar.nombre}" /></td>`)
} else {
tr.append(`<td><input type="text" name="nombre${i}" /></td>`)
}
if (typeof auxiliar !== 'undefined' && auxiliar.includes('categoria')) {
tr.append(`<td><input type="text" name="categoria${i}" value="${auxiliar.categoria}" /></td>`)
} else {
tr.append(`<td><input type="text" name="categoria${i}" /></td>`)
}
if (typeof auxiliar !== 'undefined' && auxiliar.includes('detalle')) {
tr.append(`<td><input type="text" name="detalle${i}" value="${auxiliar.detalle}" /></td>`)
} else {
tr.append(`<td><input type="text" name="detalle${i}" /></td>`)
}
tbody.append(tr)
$(this.id).find(`.dropdown[data-index='${i}']`).dropdown()
if (typeof auxiliar !== 'undefined' && auxiliar.detalles && auxiliar.detalles.centro_costo && auxiliar.detalles.centro_costo.id) {
$(this.id).find(`[name='centro_costo${i}']`).val(auxiliar.detalles.centro_costo.id)
}
$(this.id).find(`[name='rut${i}']`).on('input', inputEvent => {
inputEvent.currentTarget.value = Rut.format(inputEvent.currentTarget.value)
const index = $(inputEvent.currentTarget).data('index')
$(this.id).find(`.digito[data-index='${index}']`).text(Rut.digitoVerificador($(inputEvent.currentTarget).val()))
})
}
}
},
submit() {
const url = ''
const method = 'post'
const body = new FormData()
const data = this.data
data.centro_costo = $(this.id).find("[name='centro_costo']").dropdown('get value')
data.rut = $(this.id).find("[name='rut']").val()
data.nombre = $(this.id).find("[name='nombre']").val()
data.categoria = $(this.id).find("[name='categoria']").val()
data.detalle = $(this.id).find("[name='detalle']").val()
console.debug(data)
},
setup(id) {
this.id = id
$(this.id).modal({
onApprove: $element => {
this.submit()
}
})
$(this.id).find('#centro_costo').dropdown()
$(this.id).find("[name='rut']").on('input', inputEvent => {
$(this.id).find('#digito').text(Rut.digitoVerificador($(inputEvent.currentTarget).val()))
})
$(this.id).find('#digito').text(Rut.digitoVerificador($("[name='rut']").val()))
$(this.id).find('#add_auxiliar').click(clickEvent => {
const i = this.auxiliares.length + 1
this.draw().auxiliar({i})
})
}
}*/
/*const movimientos = {
ids: { ids: {
form: { form: {
id: '', id: '',
@ -144,7 +743,7 @@
const sociedades_ruts = $(this.ids.forms.search.sociedades).dropdown('get values') const sociedades_ruts = $(this.ids.forms.search.sociedades).dropdown('get values')
const mes = $(this.ids.forms.search.mes).calendar('get date') const mes = $(this.ids.forms.search.mes).calendar('get date')
const url = '{{$urls->api}}/contabilidad/movimientos/sociedad/mes' const url = '/contabilidad/movimientos/sociedad/mes'
const method = 'post' const method = 'post'
const body = new FormData() const body = new FormData()
sociedades_ruts.forEach(sociedad_rut => { sociedades_ruts.forEach(sociedad_rut => {
@ -175,31 +774,37 @@
if (data.length === 0) { if (data.length === 0) {
return return
} }
const formatter = new Intl.NumberFormat('es-CL')
const dateFormatter = new Intl.DateTimeFormat('es-CL', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
})
const tz = ((new Date()).getTimezoneOffset()) / -60 const tz = ((new Date()).getTimezoneOffset()) / -60
data.forEach((movimiento, index) => { data.forEach((movimiento, index) => {
const fecha = new Date(movimiento.fecha + 'Z' + tz) const fecha = new Date(movimiento.fecha + 'Z' + tz)
const valor = movimiento.abono - movimiento.cargo if (movimiento.auxiliares.length > 0) {
this.movimientos.push([ movimiento.auxiliares.forEach((auxiliar, indexAuxiliar) => {
movimiento.cuenta.inmobiliaria.sigla,//0 this.movimientos.push({
movimiento.cuenta.banco.nombre,//1 tipo: 'auxiliar',
movimiento.cuenta.cuenta,//2 id: auxiliar.id,
dateFormatter.format(fecha),//3 index: index * 100 + indexAuxiliar,
[fecha.getFullYear(), fecha.getMonth() + 1, fecha.getDate()].join('-'),//4 cuenta: movimiento.cuenta,
fecha.getMonth() + 1,//5 fecha,
fecha.getFullYear(),//6 cargo: auxiliar.cargo,
formatter.format(valor),//7 abono: auxiliar.abono,
valor,//8 detalles: auxiliar.detalles,
(movimiento.detalles) ? movimiento.detalles.centro_costo.id : '',//9 glosa: movimiento.glosa,
movimiento.glosa,//10 })
(movimiento.detalles) ? movimiento.detalles.detalle : '',//11 })
'<button class="ui icon button edit_button" data-id="' + movimiento.id + '" data-index="' + index + '"><i class="edit icon"></i></button>',//12 return
]) }
this.movimientos.push({
tipo: 'movimiento',
id: movimiento.id,
index,
cuenta: movimiento.cuenta,
fecha,
cargo: movimiento.cargo,
abono: movimiento.abono,
glosa: movimiento.glosa,
detalles: movimiento.detalles,
auxiliares: movimiento.auxiliares,
})
}) })
} }
} }
@ -238,27 +843,64 @@
draw() { draw() {
return { return {
table: () => { table: () => {
this.table.clear().rows.add(this.movimientos).draw() this.table.clear().rows.add(this.draw().movimientos()).draw()
$(this.ids.buttons.edit).click(clickEvent => { $(this.ids.buttons.edit).click(clickEvent => {
const id = $(clickEvent.currentTarget).data('id') const id = $(clickEvent.currentTarget).data('id')
const index = $(clickEvent.currentTarget).data('index') const index = $(clickEvent.currentTarget).data('index')
this.draw().edit({id, index}) this.draw().edit({id, index})
}) })
}, },
edit: ({id, index}) => { movimientos: () => {
const form = $('#' + this.ids.forms.edit.id) const output = []
form.find("[name='id']").val(id) this.movimientos.forEach(movimiento => {
form.find("[name='index']").val(index) this.draw().movimiento({movimiento, output})
$(this.ids.modals.edit).find('.sociedad').text('Sociedad: ' + this.movimientos[index][0]) })
$(this.ids.modals.edit).find('.banco').text('Banco: ' + this.movimientos[index][1]) return output
$(this.ids.modals.edit).find('.cuenta').text('Cuenta: ' + this.movimientos[index][2]) },
$(this.ids.modals.edit).find('.fecha').text('Fecha: ' + this.movimientos[index][3]) movimiento: ({movimiento, output}) => {
$(this.ids.modals.edit).find('.valor').text('Valor: ' + this.movimientos[index][7]) const formatter = new Intl.NumberFormat('es-CL')
$(this.ids.modals.edit).find('.glosa').text('Glosa: ' + this.movimientos[index][10]) const dateFormatter = new Intl.DateTimeFormat('es-CL', {
form.find("[name='centro_costo']").dropdown('set selected', this.movimientos[index][9]) day: '2-digit',
form.find("[name='detalle']").val(this.movimientos[index][11]) month: '2-digit',
year: 'numeric'
$(this.ids.modals.edit).modal('show') })
const valor = movimiento.abono - movimiento.cargo
const fecha = movimiento.fecha
output.push([
movimiento.cuenta.inmobiliaria.sigla,//0
movimiento.cuenta.banco.nombre,//1
movimiento.cuenta.cuenta,//2
dateFormatter.format(fecha),//3
[fecha.getFullYear(), fecha.getMonth() + 1, fecha.getDate()].join('-'),//4
fecha.getMonth() + 1,//5
fecha.getFullYear(),//6
formatter.format(valor),//7
valor,//8
(movimiento.detalles) ? movimiento.detalles.centro_costo.id : '',//9
movimiento.glosa,//10
(movimiento.detalles) ? movimiento.detalles.detalle : '',//11
'<button class="ui icon button edit_button" data-id="' + movimiento.id + '" data-index="' + movimiento.index + '" data-type="movimiento"><i class="edit icon"></i></button>',//12
])
},
edit: ({id, index, type}) => {
const idx = this.movimientos.findIndex(movimiento => movimiento.index === index)
editForm.show({
type,
id,
index,
sociedad: this.movimientos[idx].cuenta.inmobiliaria.sigla,
banco: this.movimientos[idx].cuenta.banco.nombre,
cuenta: this.movimientos[idx].cuenta.cuenta,
fecha: this.movimientos[idx].fecha,
valor: this.movimientos[idx].abono - this.movimientos[idx].cargo,
glosa: this.movimientos[idx].glosa,
centro_costo: (this.movimientos[idx].detalles !== null && typeof this.movimientos[idx].detalles.centro_costo) ? this.movimientos[idx].detalles.centro_costo.id : '',
rut: (this.movimientos[idx].detalles !== null) ? this.movimientos[idx].detalles.rut : '',
nombre: (this.movimientos[idx].detalles !== null) ? this.movimientos[idx].detalles.nombre : '',
categoria: (this.movimientos[idx].detalles !== null) ? this.movimientos[idx].detalles.categoria : '',
detalle: (this.movimientos[idx].detalles !== null) ? this.movimientos[idx].detalles.detalle : '',
auxiliares: this.movimientos[idx].auxiliares,
})
} }
} }
}, },
@ -274,7 +916,7 @@
const index = body.get('index') const index = body.get('index')
body.delete('id') body.delete('id')
body.delete('index') body.delete('index')
const url = '{{$urls->api}}/contabilidad/movimiento/' + id + '/detalles' const url = '/contabilidad/movimiento/' + id + '/detalles'
const method = 'post' const method = 'post'
return fetchAPI(url, {method, body}).then(response => { return fetchAPI(url, {method, body}).then(response => {
if (!response) { if (!response) {
@ -363,17 +1005,38 @@
}) })
this.table = new DataTable(this.ids.table, dtD) this.table = new DataTable(this.ids.table, dtD)
$(this.ids.modals.edit).modal({ /!*$(this.ids.modals.edit).modal({
onApprove: $element => { onApprove: $element => {
this.edit() this.edit()
} }
})*!/
editForm.setup(this.ids.modals.edit)
//$('#' + this.ids.forms.edit.id).find('#centro_costo').dropdown()
}
}*/
const app = {
handlers: {},
setup() {
this.handlers.results = new ResultsHandler()
this.handlers.loading = new LoadingHandler({id: '#movimientos_form'})
this.handlers.movimientos = new MovimientosHandler({urls: {get: '{{$urls->api}}/contabilidad/movimientos/sociedad/mes', edit: '{{$urls->api}}/contabilidad/movimientos/edit'}, loadingHandler: this.handlers.loading, resultsHandler: this.handlers.results})
this.handlers.forms = {}
this.handlers.forms.edit = new EditForm({ids: {modal: '#movimientos_modal', form: '#movimientos_edit',}})
this.handlers.modals = {}
this.handlers.modals.edit = new EditModal({ids: {modal: '#movimientos_modal'}, editForm: this.handlers.forms.edit})
this.handlers.events = new EventHandler({movimientosHandler: this.handlers.movimientos, modalHandler: this.handlers.modals.edit})
this.handlers.table = new MovimientosTable({ids: {table: '#tabla_movimientos', buttons: {edit: '.edit_button'}}, eventHandler: this.handlers.events})
this.handlers.forms.control = new ControlForm({
ids: {form: '#movimientos_form', sociedades: '#sociedades', mes: '#mes',},
}) })
$('#' + this.ids.forms.edit.id).find('#centro_costo').dropdown() this.handlers.table.setup()
this.handlers.forms.control.setup()
} }
} }
$(document).ready(() => { $(document).ready(() => {
movimientos.setup({ app.setup()
/*movimientos.setup({
forms: { forms: {
search: { search: {
id: '#movimientos_form', id: '#movimientos_form',
@ -391,7 +1054,7 @@
buttons: { buttons: {
edit: '.edit_button' edit: '.edit_button'
} }
}) })*/
}) })
</script> </script>
@endpush @endpush

View File

@ -0,0 +1,330 @@
@extends('layout.base')
@section('page_content')
<div class="ui container">
<table class="ui table">
<thead>
<tr>
<th>Nombre</th>
<th>Contacto</th>
<th class="right aligned">
<button class="ui green icon button" id="add_button">
<i class="plus icon"></i>
</button>
</th>
</tr>
</thead>
<tbody id="proveedores">
@foreach ($sociedades as $sociedad)
<tr>
<td>{{$sociedad->nombre}}</td>
<td>{{$sociedad->contacto->nombreCompleto()}}</td>
<td class="right aligned">
<button class="ui icon button" data-sociedad="{{$sociedad->rut}}">
<i class="edit icon"></i>
</button>
<button class="ui red icon button" data-sociedad="{{$sociedad->rut}}">
<i class="remove icon"></i>
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="ui modal" id="add_modal">
<div class="content">
<form class="ui form">
<div class="three wide field">
<label for="rut">RUT</label>
<div class="ui right labeled input">
<input class="right aligned" type="text" id="rut" name="rut" placeholder="RUT" maxlength="10" required />
<div class="ui basic label">-<span id="dv"></span></div>
</div>
</div>
<div class="five wide field">
<label for="nombre">Nombre</label>
<input type="text" id="nombre" name="nombre" placeholder="Nombre" required />
</div>
<div class="field">
<label for="razon">Razón Social</label>
<input type="text" id="razon" name="razon" placeholder="Razón Social" required />
</div>
<div class="four wide field">
<label for="tipo">Tipo</label>
<div class="ui selection dropdown" id="tipo">
<input type="hidden" name="tipo" required />
<i class="dropdown icon"></i>
<div class="default text">Tipo</div>
<div class="menu">
@foreach ($tiposSociedades as $tipo)
<div class="item" data-value="{{$tipo->id}}">{{$tipo->descripcion}}</div>
@endforeach
</div>
</div>
</div>
<div class="ui divider">Contacto</div>
<div class="three wide field">
<label for="rut_contacto">RUT</label>
<div class="ui right labeled input">
<input type="text" id="rut_contacto" name="rut_contacto" placeholder="RUT" maxlength="10" required />
<div class="ui basic label">-<span id="dv_contacto"></span></div>
</div>
</div>
<div class="fields">
<div class="five wide field">
<label for="nombre_contacto">Nombre</label>
<input type="text" id="nombre_contacto" name="nombre_contacto" placeholder="Nombre" required />
</div>
<div class="field">
<label for="apellido_paterno_contacto">Apellido Paterno</label>
<input type="text" id="apellido_paterno_contacto" name="apellido_paterno_contacto" placeholder="Apellido Paterno" required />
</div>
<div class="field">
<label for="apellido_materno_contacto">Apellido Materno</label>
<input type="text" id="apellido_materno_contacto" name="apellido_materno_contacto" placeholder="Apellido Materno" />
</div>
</div>
<div class="field">
<label for="email_contacto">Email</label>
<input type="email" id="email_contacto" name="email_contacto" placeholder="Email" />
</div>
<div class="field">
<label for="telefono_contacto">Teléfono</label>
<div class="ui left labeled input">
<div class="ui basic label">+56</div>
<input type="text" id="telefono_contacto" name="telefono_contacto" placeholder="Teléfono" />
</div>
</div>
</form>
</div>
<div class="actions">
<button class="ui green approve button">Guardar</button>
</div>
</div>
@endsection
@push('page_scripts')
<script>
const proveedores = {
ids: {
modal: '',
buttons: {
add: '',
edit: '',
remove: ''
},
add: {
form: '',
rut: '',
dv: '',
nombre: '',
razon: '',
tipo: '',
contacto: {
rut: '',
dv: '',
nombre: '',
apellido_paterno: '',
apellido_materno: '',
email: '',
telefono: ''
}
},
proveedores: ''
},
data: JSON.parse('{!! json_encode($sociedades) !!}'),
add() {
return {
sociedad: () => {
const data = {
rut: $(this.ids.add.rut).val().replace(/\D/g, ''),
digito: $(this.ids.add.dv).text(),
nombre: $(this.ids.add.nombre).val(),
razon: $(this.ids.add.razon).val(),
tipo_sociedad_id: $(this.ids.add.tipo).dropdown('get value'),
contacto: {
rut: $(this.ids.add.contacto.rut).val().replace(/\D/g, ''),
digito: $(this.ids.add.contacto.dv).text(),
nombres: $(this.ids.add.contacto.nombre).val(),
apellido_paterno: $(this.ids.add.contacto.apellido_paterno).val(),
apellido_materno: $(this.ids.add.contacto.apellido_materno).val(),
email: $(this.ids.add.contacto.email).val(),
telefono: $(this.ids.add.contacto.telefono).val().replace(/\D/g, ''),
}
}
const body = new FormData()
body.append('sociedades[]', JSON.stringify(data))
const url = '{{$urls->api}}/sociedades/add'
const method = 'post'
fetchAPI(url, {method, body})
.then(response => (response) ? response.json() : null)
.then(data => {
if (data.sociedades !== null) {
data.sociedades.forEach(sociedad => {
const exists = this.data.find(s => s.rut === sociedad.rut)
if (typeof exists !== 'undefined') {
return
}
this.data.push(sociedad)
})
this.draw().sociedades()
$(this.ids.modal).find('form.form').trigger('reset')
}
})
}
}
},
edit() {},
remove() {
return {
sociedad: rut => {
const body = new FormData()
body.append('sociedades_ruts[]', rut)
const url = '{{$urls->api}}/sociedades/delete'
const method = 'post'
fetchAPI(url, {method, body})
.then(response => (response) ? response.json() : null)
.then(data => {
if (data.sociedades !== null) {
data.sociedades.forEach(sociedad => {
if (sociedad.sociedad.rut !== rut) {
return
}
if (!sociedad.deleted) {
return
}
$(this.ids.proveedores).find(`button[data-sociedad="${rut}"]`).closest('tr').remove()
})
}
})
}
}
},
draw() {
return {
sociedades: () => {
$(this.ids.proveedores).empty()
this.data.forEach(sociedad => {
$(this.ids.proveedores).append(`
<tr>
<td>${sociedad.nombre}</td>
<td>${sociedad.contacto.nombreCompleto}</td>
<td class="right aligned">
<button class="ui icon button" data-sociedad="${sociedad.rut}">
<i class="edit icon"></i>
</button>
<button class="ui red icon button" data-sociedad="${sociedad.rut}">
<i class="remove icon"></i>
</button>
</td>
</tr>
`)
})
$(this.ids.buttons.remove).click((e) => {
this.remove().sociedad($(e.target).data('sociedad'))
})
}
}
},
formatters() {
return {
rut: value => {
const rut = value.replace(/[^0-9]/g, '')
if (rut.length <= 1) {
return rut
}
return rut.replace(/\B(?=(\d{3})+(?!\d))/g, '.')
},
telefono: value => {
const phone = value.replace(/[^0-9]/g, '')
if (phone.length <= 1) {
return phone
}
return phone.replace(/(\d{2})(\d{3})(\d{4})/, '$1 $2 $3')
}
}
},
digitoVerificador(value) {
let rut = value.replace(/[^0-9kK]/g, '')
if (rut.length < 1) {
return ''
}
let suma = 0
let mul = 2
for (let i = rut.length-1; i >= 0; i--) {
suma += parseInt(rut[i]) * mul
mul = (mul + 1) % 8 || 2
}
const dv = 11 - suma % 11
return dv === 10 ? 'K' : (dv === 11 ? '0' : dv.toString())
},
setup(ids) {
this.ids = ids
$(this.ids.buttons.add).click(() => {
$(this.ids.modal).modal('show')
})
$(this.ids.add.rut).on('input', (e) => {
e.target.value = this.formatters().rut(e.target.value)
$(this.ids.add.dv).text(this.digitoVerificador(e.target.value))
})
if ($(this.ids.add.rut).val().length > 0) {
$(this.ids.add.rut).val(this.formatters().rut($(this.ids.add.rut).val()))
$(this.ids.add.dv).text(this.digitoVerificador($(this.ids.add.rut).val()))
}
$(this.ids.tipo).dropdown()
$(this.ids.add.contacto.rut).on('input', (e) => {
e.target.value = this.formatters().rut(e.target.value)
$(this.ids.add.contacto.dv).text(this.digitoVerificador(e.target.value))
})
if ($(this.ids.add.contacto.rut).val().length > 0) {
$(this.ids.add.contacto.rut).val(this.formatters().rut($(this.ids.add.contacto.rut).val()))
$(this.ids.add.contacto.dv).text(this.digitoVerificador($(this.ids.add.contacto.rut).val()))
}
$(this.ids.add.contacto.telefono).on('input', (e) => {
e.target.value = this.formatters().telefono(e.target.value)
})
if ($(this.ids.add.contacto.telefono).val().length > 0) {
$(this.ids.add.contacto.telefono).val(this.formatters().telefono($(this.ids.add.contacto.telefono).val()))
}
$(this.ids.modal).modal({
onApprove: () => {
this.add().sociedad()
}
})
$(this.ids.buttons.remove).click((e) => {
this.remove().sociedad($(e.target).data('sociedad'))
})
}
}
$(document).ready(() => {
proveedores.setup({
modal: '#add_modal',
buttons: {
add: '#add_button',
edit: '.edit',
remove: '.remove'
},
add: {
form: '#add_modal form.form',
rut: '#rut',
dv: '#dv',
nombre: '#nombre',
razon: '#razon',
tipo: '#tipo',
contacto: {
rut: '#rut_contacto',
dv: '#dv_contacto',
nombre: '#nombre_contacto',
apellido_paterno: '#apellido_paterno_contacto',
apellido_materno: '#apellido_materno_contacto',
email: '#email_contacto',
telefono: '#telefono_contacto'
}
},
proveedores: '#proveedores'
})
})
</script>
@endpush

View File

@ -2,6 +2,11 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.3/semantic.min.js" integrity="sha512-gnoBksrDbaMnlE0rhhkcx3iwzvgBGz6mOEj4/Y5ZY09n55dYddx6+WYc72A55qEesV8VX2iMomteIwobeGK1BQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.3/semantic.min.js" integrity="sha512-gnoBksrDbaMnlE0rhhkcx3iwzvgBGz6mOEj4/Y5ZY09n55dYddx6+WYc72A55qEesV8VX2iMomteIwobeGK1BQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript"> <script type="text/javascript">
class APIClient {
static fetch(url, options=null) {
return fetchAPI(url, options)
}
}
function fetchAPI(url, options=null) { function fetchAPI(url, options=null) {
if (options === null) { if (options === null) {
options = {} options = {}

View File

@ -0,0 +1,26 @@
@push('page_scripts')
<script>
class Rut {
static digitoVerificador(rut) {
if (rut.length === 0) {
return ''
}
let M = 0, S = 1
for (; rut; rut = Math.floor(rut / 10)) {
S = (S + rut % 10 * (9 - M++ % 6)) % 11
}
return S ? S - 1 : 'K'
}
static format(rut) {
if (rut.length === 0) {
return ''
}
rut.replace(/\./g, '')
return rut.replace(/^(\d{1,2})(\d{3})(\d{3})$/, '$1.$2.$3')
}
static validar(rut, digito) {
return Rut.digitoVerificador(rut) === digito
}
}
</script>
@endpush

View File

@ -656,9 +656,9 @@
} }
add() { add() {
return { return {
propietario: ({rut, nombre, proporcion, direccion, comuna, fecha, uf}) => { propietario: ({rut, nombre, proporcion, direccion, comuna, fecha}) => {
const index = this.props.propietarios.length + 1 const index = this.props.propietarios.length + 1
const propietario = new Propietario({index, rut, nombre, proporcion, direccion, comuna, fecha, uf}) const propietario = new Propietario({index, proporcion, rut, nombre, direccion, comuna, fecha})
this.props.propietarios.push(propietario) this.props.propietarios.push(propietario)
this.add().factura(propietario) this.add().factura(propietario)
return propietario return propietario
@ -691,6 +691,47 @@
} }
} }
} }
save() {
return {
factura: ({index}) => {
const factura = this.props.facturas[index]
if (factura.props.saved) {
return
}
const url = '{{$urls->api}}/ventas/facturas/add'
const method = 'post'
const body = new FormData()
body.set('venta_id', this.props.id)
body.set('index', (index+1).toString())
const data = structuredClone(factura.props)
delete data.index
data.detalle.base = data.detalle.base.toFixed(0)
data.detalle.terreno = data.detalle.terreno.toFixed(0)
data.detalle.neto = data.detalle.neto.toFixed(0)
data.detalle.iva = data.detalle.iva.toFixed(0)
data.detalle.bruto = data.detalle.bruto.toFixed(0)
data.detalle.total = data.detalle.total.toFixed(0)
data.total.neto = data.total.neto.toFixed(0)
data.total.exento = data.total.exento.toFixed(0)
data.total.iva = data.total.iva.toFixed(0)
data.total.total = data.total.total.toFixed(0)
data.fecha = [data.fecha.getFullYear(), data.fecha.getMonth()+1, data.fecha.getDate()].join('-')
data.uf.fecha = [data.uf.fecha.getFullYear(), data.uf.fecha.getMonth()+1, data.uf.fecha.getDate()].join('-')
body.set('data', JSON.stringify(data))
return fetchAPI(url, {method, body}).then(response => {
if (!response) {
return
}
return response.json().then(json => {
if (!json.saved) {
return
}
this.props.facturas[json.factura.index-1].props.saved = true
})
})
}
}
}
} }
class Factura extends BaseObject { class Factura extends BaseObject {
draw() { draw() {
@ -699,6 +740,45 @@
return '<div class="ui divider" data-index="'+index+'"></div>' return '<div class="ui divider" data-index="'+index+'"></div>'
}, },
factura: ({proyecto, propietario, unidades, terreno, venta, uf, formatters = {date, pesos, ufs, percent}}) => { factura: ({proyecto, propietario, unidades, terreno, venta, uf, formatters = {date, pesos, ufs, percent}}) => {
this.props.proporcion = propietario.props.proporcion
this.props.emisor = {
nombre: proyecto.inmobiliaria.nombre,
rut: proyecto.inmobiliaria.rut,
direccion: proyecto.direccion
}
this.props.receptor = {
nombre: propietario.props.nombre,
rut: propietario.props.rut,
direccion: propietario.props.direccion,
comuna: propietario.props.comuna
}
this.props.fecha = propietario.props.fecha
this.props.unidades = []
this.props.detalle = {
base: venta.base * propietario.props.proporcion,
terreno: venta.exento * propietario.props.proporcion,
neto: venta.neto * propietario.props.proporcion,
iva: venta.iva * propietario.props.proporcion,
bruto: venta.bruto * propietario.props.proporcion,
descuento: venta.descuento * propietario.props.proporcion,
total: venta.total * propietario.props.proporcion
}
this.props.total = {
neto: venta.neto * propietario.props.proporcion,
exento: venta.exento * propietario.props.proporcion,
iva: venta.iva * propietario.props.proporcion,
total: venta.total * propietario.props.proporcion,
}
this.props.uf = uf
unidades.forEach(unidad => {
const descuento = terreno.reajustado * unidad.props.prorrateo
const bruto = unidad.props.valor * uf.valor - descuento
const neto = bruto / 1.19
this.props.unidades.push({
descripcion: unidad.props.tipo + ' ' + unidad.props.descripcion + ' (UF ' + formatters.ufs.format(unidad.props.valor * propietario.props.proporcion) + ')',
precio: (neto * propietario.props.proporcion).toFixed(0)
})
})
return [ return [
this.draw().divider(propietario.props.index), this.draw().divider(propietario.props.index),
'<div class="factura" data-index="'+propietario.props.index+'">', '<div class="factura" data-index="'+propietario.props.index+'">',
@ -707,6 +787,7 @@
this.draw().propietario({propietario, formatters}), this.draw().propietario({propietario, formatters}),
this.draw().table({venta, unidades, propietario, terreno, uf, formatters}), this.draw().table({venta, unidades, propietario, terreno, uf, formatters}),
this.draw().totales({propietario, venta, uf, formatters}), this.draw().totales({propietario, venta, uf, formatters}),
this.draw().guardar({index: propietario.props.index}),
'</div>', '</div>',
'</div>' '</div>'
].join("\n") ].join("\n")
@ -868,7 +949,7 @@
'SUBTOTAL (Bruto): $' + formatters.pesos.format((venta.neto + venta.iva) * propietario.props.proporcion) + '<br />' + 'SUBTOTAL (Bruto): $' + formatters.pesos.format((venta.neto + venta.iva) * propietario.props.proporcion) + '<br />' +
'Mas valor terreno: $' + ((venta.exento > 0) ? formatters.pesos.format(venta.exento * propietario.props.proporcion) : emptyTerreno) + '<br />' + 'Mas valor terreno: $' + ((venta.exento > 0) ? formatters.pesos.format(venta.exento * propietario.props.proporcion) : emptyTerreno) + '<br />' +
'TOTAL (Escritura): $' + formatters.pesos.format(venta.total * propietario.props.proporcion) + '; ' + formatters.ufs.format(venta.totalUF * propietario.props.proporcion) + ' UF<br /><br />' + 'TOTAL (Escritura): $' + formatters.pesos.format(venta.total * propietario.props.proporcion) + '; ' + formatters.ufs.format(venta.totalUF * propietario.props.proporcion) + ' UF<br /><br />' +
'Descuento Terreno: ' + ((venta.exento > 0) ? formatters.percent.format(venta.descuento * 100) : emptyTerreno) + '%<br /><br />' + 'Descuento Terreno: ' + ((venta.exento > 0) ? formatters.percent.format(venta.descuento * propietario.props.proporcion * 100) : emptyTerreno) + '%<br /><br />' +
'UF (' + formatters.date.format(uf.fecha) + '): $' + formatters.ufs.format(uf.valor), 'UF (' + formatters.date.format(uf.fecha) + '): $' + formatters.ufs.format(uf.valor),
'1 UNID', '1 UNID',
formatters.pesos.format(venta.exento * propietario.props.proporcion), formatters.pesos.format(venta.exento * propietario.props.proporcion),
@ -920,6 +1001,28 @@
'</div>', '</div>',
'</div>' '</div>'
].join("\n") ].join("\n")
},
guardar: ({index}) => {
if (this.props.saved) {
return [
'<div class="row">',
'<div class="fourteen wide column"></div>',
'<div class="two wide center aligned column">',
'<div class="ui green message">',
'<i class="check icon"></i>',
'Guardada',
'</div>',
'</div>',
'</div>'
].join("\n")
}
return [
'<div class="row">',
'<div class="right aligned sixteen wide column">',
`<button class="ui primary button guardar" data-index="${index}">Guardar</button>`,
'</div>',
'</div>'
].join("\n")
} }
} }
} }
@ -942,6 +1045,10 @@
}, },
facturas: () => { facturas: () => {
document.getElementById(this.ids.facturas).innerHTML = this.venta.draw().facturas(this.formatters) document.getElementById(this.ids.facturas).innerHTML = this.venta.draw().facturas(this.formatters)
$('button.guardar').click(clickEvent => {
const index = clickEvent.currentTarget.getAttribute('data-index')
facturas.venta.save().factura({index: index-1})
})
} }
} }
}, },
@ -955,7 +1062,7 @@
rut: '{{$venta->proyecto()->inmobiliaria()->rut()}}', rut: '{{$venta->proyecto()->inmobiliaria()->rut()}}',
nombre: '{{$venta->proyecto()->inmobiliaria()->nombreCompleto()}}' nombre: '{{$venta->proyecto()->inmobiliaria()->nombreCompleto()}}'
}, },
direccion: '{{$venta->proyecto()->direccion()->simple()}}' direccion: '{{$venta->proyecto()->direccion()->simple()}}, {{$venta->proyecto()->direccion()->comuna->descripcion}}'
}, },
valor: {{$venta->valor}}, valor: {{$venta->valor}},
uf: { uf: {
@ -990,15 +1097,35 @@
propietarios: [], propietarios: [],
facturas: [] facturas: []
}) })
this.venta.add().propietario({ @if (count($facturas) > 0)
rut: '{{$venta->propietario()->rut()}}', this.venta.props.uf = {
nombre: '{{$venta->propietario()->nombreCompleto()}}', fecha: new Date('{{$facturas[0]->fechaUF->format('Y-m-d')}}'),
proporcion: 1, valor: {{$facturas[0]->valorUF}}
direccion: '{{$venta->propietario()->datos->direccion->simple()}}', }
comuna: '{{$venta->propietario()->datos->direccion->comuna->descripcion}}', @foreach ($facturas as $factura)
fecha: new Date('{{$venta->currentEstado()->fecha->add(new DateInterval('P1D'))->format('Y-m-d')}}'), this.venta.props.facturas[{{$factura->index-1}}] = new Factura(JSON.parse('{!! json_encode($factura) !!}'))
uf: {{$uf}} this.venta.props.facturas[{{$factura->index-1}}].props.saved = true
}) this.venta.props.propietarios[{{$factura->index-1}}] = new Propietario({
index: {{$factura->index}},
proporcion: {{$factura->proporcion}},
rut: '{{$factura->receptorRut}}',
nombre: '{{$factura->receptorNombre}}',
direccion: '{{$factura->receptorDireccion}}',
comuna: '{{$factura->receptorComuna}}',
fecha: new Date('{{$factura->fecha->format('Y-m-d')}}'),
})
@endforeach
@else
this.venta.add().propietario({
rut: '{{$venta->propietario()->rut()}}',
nombre: '{{$venta->propietario()->nombreCompleto()}}',
proporcion: 1,
direccion: '{{$venta->propietario()->datos->direccion->simple()}}',
comuna: '{{$venta->propietario()->datos->direccion->comuna->descripcion}}',
fecha: new Date('{{$venta->currentEstado()->fecha->add(new DateInterval('P1D'))->format('Y-m-d')}}'),
uf: {{$uf}}
})
@endif
this.draw().venta() this.draw().venta()
this.draw().facturas() this.draw().facturas()
} }

View File

@ -120,4 +120,22 @@ class Nubox
} }
return $this->withJson($response, $output); return $this->withJson($response, $output);
} }
public function facturas(ServerRequestInterface $request, ResponseInterface $response, Service\Contabilidad\Nubox $nuboxService, int $inmobiliaria_rut, string $dia): ResponseInterface
{
$output = [
'inmobiliaria_rut' => $inmobiliaria_rut,
'dia' => $dia,
'facturas' => []
];
try {
$output['facturas'] = $nuboxService->getFacturas($inmobiliaria_rut, new DateTimeImmutable($dia));
} catch (HttpResponse $exception) {
$output['error'] = [
'code' => $exception->getCode(),
'reason' => $exception->getReason(),
'message' => $exception->getMessage(),
];
}
return $this->withJson($response, $output);
}
} }

View File

@ -55,27 +55,33 @@ class Inmobiliarias
} catch (EmptyResult) {} } catch (EmptyResult) {}
return $this->withJson($response, $output); return $this->withJson($response, $output);
} }
public function agentes(ServerRequestInterface $request, ResponseInterface $response, public function proveedores(ServerRequestInterface $request, ResponseInterface $response,
Repository\Inmobiliaria $inmobiliariaRepository, Repository\Inmobiliaria $inmobiliariaRepository,
Repository\Inmobiliaria\SociedadAgente $sociedadAgenteRepository, Repository\Inmobiliaria\SociedadAgente $sociedadAgenteRepository,
Repository\Inmobiliaria\TipoAgente $tipoAgenteRepository, Repository\Inmobiliaria\TipoAgente $tipoAgenteRepository,
int $inmobiliaria_rut): ResponseInterface int $inmobiliaria_rut): ResponseInterface
{ {
$input = $request->getParsedBody(); $input = $request->getParsedBody();
$output = [ $output = [
'sociedad_rut' => $inmobiliaria_rut, 'sociedad_rut' => $inmobiliaria_rut,
'input' => $input, 'input' => $input,
'sociedad' => null, 'sociedad' => null,
'agentes' => [] 'proveedores' => []
]; ];
try { try {
$inmobiliaria = $inmobiliariaRepository->fetchById($inmobiliaria_rut); $inmobiliaria = $inmobiliariaRepository->fetchById($inmobiliaria_rut);
$output['sociedad'] = $inmobiliaria; $output['sociedad'] = $inmobiliaria;
if (isset($input['tipo_agente_id'])) { if (isset($input['tipo_agente_id'])) {
$tipo = $tipoAgenteRepository->fetchById($input['tipo_agente_id']); $tipo = $tipoAgenteRepository->fetchById($input['tipo_agente_id']);
$output['agentes'] = $sociedadAgenteRepository->fetchBySociedadAndTipo($inmobiliaria->rut, $tipo->id); $proveedores = $sociedadAgenteRepository->fetchBySociedadAndTipo($inmobiliaria->rut, $tipo->id);
} else { } else {
$output['agentes'] = $sociedadAgenteRepository->fetchBySociedad($inmobiliaria->rut); $proveedores = $sociedadAgenteRepository->fetchBySociedad($inmobiliaria->rut);
}
$output['proveedores'] = json_decode(json_encode($proveedores), JSON_OBJECT_AS_ARRAY);
foreach ($proveedores as $i => $proveedor) {
$output['proveedores'][$i]['sociedad'] = $proveedor->sociedad;
$output['proveedores'][$i]['proveedor'] = $proveedor->agenteTipo->agente;
$output['proveedores'][$i]['tipo'] = $proveedor->agenteTipo->tipoAgente;
} }
} catch (EmptyResult) {} } catch (EmptyResult) {}
return $this->withJson($response, $output); return $this->withJson($response, $output);

View File

@ -0,0 +1,35 @@
<?php
namespace Incoviba\Controller\API\Inmobiliarias;
use Incoviba\Controller\API\withJson;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Common\Ideal;
use Incoviba\Repository;
use Incoviba\Service;
class Sociedades extends Ideal\Service
{
use withJson;
public function add(ServerRequestInterface $request, ResponseInterface $response,
Service\Sociedad $sociedadService, Service\Persona $personaService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'sociedad' => null
];
try {
$data = json_decode($input, true);
$contacto = $personaService->add($data['contacto']);
$data['contacto_rut'] = $contacto->rut;
unset($data['contacto']);
$output['sociedad'] = $sociedadService->add($data);
} catch (EmptyResult) {
$output['error'] = 'No se pudo agregar la sociedad';
}
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,132 @@
<?php
namespace Incoviba\Controller\API;
use Incoviba\Common\Ideal\Controller;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Incoviba\Service;
class Sociedades extends Controller
{
use withJson;
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService): ResponseInterface
{
$output = [
'sociedades' => $sociedadService->getAll('nombre'),
];
return $this->withJson($response, $output);
}
public function getMany(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'sociedades' => [],
];
foreach ($input['sociedades_ruts'] as $rut) {
$sociedad = $sociedadService->getByRut($rut);
if ($sociedad === null) {
continue;
}
$output['sociedades'][] = $sociedad;
}
return $this->withJson($response, $output);
}
public function get(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService, int $sociedad_rut): ResponseInterface
{
$output = [
'sociedad_rut' => $sociedad_rut,
'sociedad' => $sociedadService->getByRut($sociedad_rut),
];
return $this->withJson($response, $output);
}
public function add(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'sociedad' => $sociedadService->add($input),
];
return $this->withJson($response, $output);
}
public function addMany(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService, Service\Persona $personaService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'sociedades' => [],
];
try {
if (json_decode($input) !== null) {
$input = json_decode($input, true);
}
} catch (\TypeError) {}
foreach ($input['sociedades'] as $sociedadData) {
try {
if (json_decode($sociedadData) !== null) {
$sociedadData = json_decode($sociedadData, true);
}
} catch (\TypeError) {}
$contacto = $personaService->add($sociedadData['contacto']);
$sociedadData['contacto_rut'] = $contacto->rut;
unset($sociedadData['contacto']);
$sociedad = $sociedadService->add($sociedadData);
if ($sociedad === null) {
continue;
}
$output['sociedades'][] = $sociedad;
}
return $this->withJson($response, $output);
}
public function edit(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'sociedades' => [],
];
foreach ($input['sociedades'] as $sociedadData) {
$sociedad = $sociedadService->edit($sociedadData['rut'], $sociedadData);
if ($sociedad === null) {
continue;
}
$output['sociedades'][] = $sociedad;
}
return $this->withJson($response, $output);
}
public function delete(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'sociedades' => [],
];
foreach ($input['sociedades_ruts'] as $rut) {
$sociedad = $sociedadService->getByRut($rut);
$deleted = $sociedadService->delete($rut);
$output['sociedades'][] = [
'sociedad' => $sociedad,
'deleted' => $deleted,
];
}
return $this->withJson($response, $output);
}
public function asignar(ServerRequestInterface $request, ResponseInterface $response, Service\Sociedad $sociedadService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'proveedores' => [],
];
foreach ($input['proveedores'] as $proveedorData) {
$proveedor = $sociedadService->asignar($proveedorData['inmobiliaria_rut'], $proveedorData['sociedad_rut']);
if ($proveedor === null) {
continue;
}
$output['proveedores'][] = $proveedor;
}
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Incoviba\Controller\API\Ventas;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Incoviba\Controller\API\withJson;
use Incoviba\Common\Ideal\Controller;
use Incoviba\Common\Implement;
use Incoviba\Service;
class Facturas extends Controller
{
use withJson;
public function add(ServerRequestInterface $request, ResponseInterface $response, Service\Venta\Factura $facturaService): ResponseInterface
{
$data = $request->getParsedBody();
$output = [
'input' => $data,
'factura' => null,
'saved' => false
];
try {
$output['factura'] = $facturaService->add($data);
$output['saved'] = true;
} catch (Implement\Exception\EmptyResult) {
$output['error'] = 'No se pudo agregar la factura';
return $this->withJson($response, $output, 400);
}
return $this->withJson($response, $output);
}
}

View File

@ -80,6 +80,12 @@ class Contabilidad extends Controller
$informes = $contabilidadService->build($fecha); $informes = $contabilidadService->build($fecha);
$filename = "Informe de Tesorería {$fecha->format('d.m.Y')}"; $filename = "Informe de Tesorería {$fecha->format('d.m.Y')}";
return $view->render($response, 'contabilidad.informes.tesoreria', compact('fecha', 'anterior', 'siguiente', 'informes', 'filename')); return $view->render($response, 'contabilidad.informes.tesoreria', compact('fecha', 'anterior', 'siguiente', 'informes', 'filename'));
}
public function semanal(ServerRequestInterface $request, ResponseInterface $response, View $view,
Service\Contabilidad\Informe\Semanal $semanalService,
string $fecha = 'today'): ResponseInterface
{
} }
public function cuadratura(ServerRequestInterface $request, ResponseInterface $response, View $view, public function cuadratura(ServerRequestInterface $request, ResponseInterface $response, View $view,
Repository\Inmobiliaria $inmobiliariaRepository): ResponseInterface Repository\Inmobiliaria $inmobiliariaRepository): ResponseInterface

View File

@ -0,0 +1,27 @@
<?php
namespace Incoviba\Controller\Inmobiliarias;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Common\Alias\View;
use Incoviba\Repository;
use Incoviba\Service;
class Proveedores
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, View $view,
Service\Sociedad $sociedadService,
Repository\Inmobiliaria\TipoSociedad $tipoSociedad): ResponseInterface
{
$sociedades = [];
try {
$sociedades = $sociedadService->getAll('nombre');
} catch (EmptyResult) {}
$tiposSociedades = [];
try {
$tiposSociedades = $tipoSociedad->fetchAll('descripcion');
} catch (EmptyResult) {}
return $view->render($response, 'inmobiliarias.proveedores', compact('sociedades', 'tiposSociedades'));
}
}

View File

@ -26,9 +26,10 @@ class Facturacion extends Ideal\Controller
* @throws Exception * @throws Exception
*/ */
public function show(ServerRequestInterface $request, ResponseInterface $response, View $view, public function show(ServerRequestInterface $request, ResponseInterface $response, View $view,
Service\Venta $ventaService, Service\Proyecto\Terreno $terrenoService, Service\Venta $ventaService, Service\Proyecto\Terreno $terrenoService,
Service\IPC $ipcService, Service\UF $ufService, Service\IPC $ipcService, Service\UF $ufService,
int $venta_id): ResponseInterface Service\Venta\Factura $facturasService,
int $venta_id): ResponseInterface
{ {
$venta = $ventaService->getById($venta_id); $venta = $ventaService->getById($venta_id);
$uf = $ufService->get($venta->currentEstado()->fecha); $uf = $ufService->get($venta->currentEstado()->fecha);
@ -48,7 +49,8 @@ class Facturacion extends Ideal\Controller
$ipc = $ipcService->get($prevMonthTerreno, $prevMonth); $ipc = $ipcService->get($prevMonthTerreno, $prevMonth);
} }
} }
$facturas = $facturasService->getByVenta($venta->id);
return $view->render($response, 'ventas.facturacion.show', compact('venta', 'terreno', 'uf', 'ipc')); return $view->render($response, 'ventas.facturacion.show', compact('venta', 'terreno', 'uf', 'ipc', 'facturas'));
} }
} }

View File

@ -4,8 +4,8 @@ namespace Incoviba\Model\Contabilidad;
use DateTimeInterface; use DateTimeInterface;
use Incoviba\Common\Ideal; use Incoviba\Common\Ideal;
use Incoviba\Common\Implement\Exception\EmptyResult; use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Model\Contabilidad\Movimiento\Detalle;
use Incoviba\Model\Inmobiliaria; use Incoviba\Model\Inmobiliaria;
use Incoviba\Model\Movimiento\Detalle;
class Movimiento extends Ideal\Model class Movimiento extends Ideal\Model
{ {
@ -21,15 +21,20 @@ class Movimiento extends Ideal\Model
public function getDetalles(): ?Detalle public function getDetalles(): ?Detalle
{ {
if (!isset($this->detalles)) { if (!isset($this->detalles)) {
try { $this->detalles = $this->runFactory('detalles');
$this->detalles = $this->runFactory('detalles');
} catch (EmptyResult) {
$this->detalles = null;
}
} }
return $this->detalles; return $this->detalles;
} }
protected ?array $auxiliares;
public function getAuxiliares(): ?array
{
if (!isset($this->auxiliares)) {
$this->auxiliares = $this->runFactory('auxiliares');
}
return $this->auxiliares;
}
public function jsonSerialize(): mixed public function jsonSerialize(): mixed
{ {
return array_merge(parent::jsonSerialize(), [ return array_merge(parent::jsonSerialize(), [
@ -40,7 +45,8 @@ class Movimiento extends Ideal\Model
'cargo' => $this->cargo, 'cargo' => $this->cargo,
'abono' => $this->abono, 'abono' => $this->abono,
'saldo' => $this->saldo, 'saldo' => $this->saldo,
'detalles' => $this->getDetalles() ?? null 'detalles' => $this->getDetalles(),
'auxiliares' => $this->getAuxiliares()
]); ]);
} }
} }

View File

@ -0,0 +1,36 @@
<?php
namespace Incoviba\Model\Contabilidad\Movimiento;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
class Auxiliar extends Ideal\Model
{
public Model\Contabilidad\Movimiento $movimiento;
public int $cargo;
public int $abono;
protected ?Model\Contabilidad\Movimiento\Auxiliar\Detalle $detalles;
public function getDetalles(): ?Model\Contabilidad\Movimiento\Auxiliar\Detalle
{
if (!isset($this->detalles)) {
try {
$this->detalles = $this->runFactory('detalles');
} catch (Implement\Exception\EmptyResult) {
$this->detalles = null;
}
}
return $this->detalles;
}
public function jsonSerialize(): mixed
{
return array_merge(parent::jsonSerialize(), [
'movimiento_id' => $this->movimiento->id,
'cargo' => $this->cargo,
'abono' => $this->abono,
'detalles' => $this->getDetalles()
]);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Incoviba\Model\Contabilidad\Movimiento\Auxiliar;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Detalle extends Ideal\Model
{
public Model\Contabilidad\Movimiento\Auxiliar $auxiliar;
public ?Model\Contabilidad\CentroCosto $centroCosto;
public ?int $rut;
public ?string $digito;
public ?string $nombre;
public ?string $categoria;
public ?string $detalle;
public function rut(): string
{
return $this->rut . '-' . $this->digito;
}
public function rutFormatted(): string
{
return number_format($this->rut, 0, ',', '.') . '-' . $this->digito;
}
public function jsonSerialize(): mixed
{
return [
'auxiliar_id' => $this->auxiliar->id,
'centro_costo' => $this->centroCosto,
'rut' => $this->rut,
'digito' => $this->digito,
'nombre' => $this->nombre,
'categoria' => $this->categoria,
'detalle' => $this->detalle,
'rutFormatted' => $this->rutFormatted(),
];
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Incoviba\Model\Contabilidad\Movimiento;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Detalle extends Ideal\Model
{
public Model\Contabilidad\Movimiento $movimiento;
public ?Model\Contabilidad\CentroCosto $centroCosto;
public ?int $rut;
public ?string $digito;
public ?string $nombre;
public ?string $categoria;
public ?string $detalle;
public function rut(): string
{
return $this->rut . '-' . $this->digito;
}
public function rutFormatted(): string
{
return number_format($this->rut, 0, ',', '.') . '-' . $this->digito;
}
public function jsonSerialize(): mixed
{
return [
'movimiento_id' => $this->movimiento->id,
'centro_costo' => $this->centroCosto,
'rut' => $this->rut,
'digito' => $this->digito,
'nombre' => $this->nombre,
'categoria' => $this->categoria,
'detalle' => $this->detalle,
'rutFormatted' => $this->rutFormatted()
];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Incoviba\Model;
use DateTimeInterface;
use Incoviba\Common\Ideal;
class DatosPersona extends Ideal\Model
{
public Persona $persona;
public ?Direccion $direccion;
public ?int $telefono;
public ?string $email;
public ?DateTimeInterface $fechaNacimiento;
public ?string $sexo;
public ?string $estadoCivil;
public ?string $nacionalidad;
public ?string $profesion;
public function jsonSerialize(): mixed
{
return [
'direccion' => $this->direccion,
'telefono' => $this->telefono,
'email' => $this->email,
'fechaNacimiento' => $this->fechaNacimiento,
'sexo' => $this->sexo,
'estadoCivil' => $this->estadoCivil,
'nacionalidad' => $this->nacionalidad,
'profesion' => $this->profesion,
];
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Incoviba\Model\Inmobiliaria;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Proveedor extends Ideal\Model
{
public Model\Inmobiliaria $inmobiliaria;
public Model\Sociedad $sociedad;
protected array $tipos;
public function tipos(): array
{
if (!isset($this->tipos)) {
$this->tipos = $this->runFactory('tipos');
}
return $this->tipos;
}
public function jsonSerialize(): mixed
{
return array_merge(parent::jsonSerialize(), [
'inmobiliaria' => $this->inmobiliaria,
'sociedad' => $this->sociedad,
'tipos' => $this->tipos(),
]);
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Incoviba\Model\Movimiento;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Detalle extends Ideal\Model
{
public Model\Contabilidad\Movimiento $movimiento;
public ?Model\Contabilidad\CentroCosto $centroCosto;
public ?string $detalle;
public function jsonSerialize(): mixed
{
return [
'movimiento_id' => $this->movimiento->id,
'centro_costo' => $this->centroCosto,
'detalle' => $this->detalle
];
}
}

46
app/src/Model/Persona.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace Incoviba\Model;
use Incoviba\Common\Ideal;
class Persona extends Ideal\Model
{
public int $rut;
public string $digito;
public string $nombres;
public string $apellidoPaterno;
public string $apellidoMaterno;
public function nombreCompleto(): string
{
return $this->nombres . ' ' . $this->apellidoPaterno . ' ' . $this->apellidoMaterno;
}
public function rutCompleto(): string
{
return number_format($this->rut, 0, ',', '.') . '-' . $this->digito;
}
protected ?DatosPersona $datos;
public function datos(): ?DatosPersona
{
if (!isset($this->datos)) {
$this->datos = $this->runFactory('datos');
}
return $this->datos;
}
public function jsonSerialize(): mixed
{
return [
'rut' => $this->rut,
'digito' => $this->digito,
'nombres' => $this->nombres,
'apellidoPaterno' => $this->apellidoPaterno,
'apellidoMaterno' => $this->apellidoMaterno,
'nombreCompleto' => $this->nombreCompleto(),
'rutCompleto' => $this->rutCompleto(),
'datos' => $this->datos(),
];
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Incoviba\Model;
use Incoviba\Common\Ideal;
use Incoviba\Model\Inmobiliaria\TipoSociedad;
class Sociedad extends Ideal\Model
{
public int $rut;
public string $digito;
public string $nombre;
public string $razonSocial;
public TipoSociedad $tipoSociedad;
public Persona $contacto;
public function nombreCompleto(): string
{
return implode(' ', [$this->razonSocial, $this->tipoSociedad->descripcion]);
}
public function jsonSerialize(): mixed
{
return [
'rut' => $this->rut,
'digito' => $this->digito,
'nombre' => $this->nombre,
'razonSocial' => $this->razonSocial,
'tipoSociedad' => $this->tipoSociedad,
'contacto' => $this->contacto,
'nombreCompleto' => $this->nombreCompleto(),
];
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Incoviba\Model\Venta;
use DateTimeInterface;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Factura extends Ideal\Model
{
public Model\Venta $venta;
public int $index;
public float $proporcion;
public string $emisorRut;
public string $emisorNombre;
public string $emisorDireccion;
public string $receptorRut;
public string $receptorNombre;
public string $receptorDireccion;
public string $receptorComuna;
public DateTimeInterface $fecha;
public array $unidades;
public int $detalleBase;
public int $detalleTerreno;
public int $detalleNeto;
public int $detalleIva;
public int $detalleBruto;
public float $detalleDescuento;
public int $detalleTotal;
public int $totalNeto;
public int $totalExento;
public int $totalIva;
public int $totalTotal;
public DateTimeInterface $fechaUF;
public float $valorUF;
protected array $estados;
public function estados(): array
{
if (!isset($this->estados)) {
$this->estados = $this->runFactory('estados');
}
return $this->estados ?? [];
}
public function jsonSerialize(): mixed
{
return array_merge(parent::jsonSerialize(), [
'venta_id' => $this->venta->id,
'index' => $this->index,
'proporcion' => $this->proporcion,
'emisor' => [
'rut' => $this->emisorRut,
'nombre' => $this->emisorNombre,
'direccion' => $this->emisorDireccion
],
'receptor' => [
'rut' => $this->receptorRut,
'nombre' => $this->receptorNombre,
'direccion' => $this->receptorDireccion,
'comuna' => $this->receptorComuna
],
'fecha' => $this->fecha->format('Y-m-d'),
'unidades' => $this->unidades,
'detalle' => [
'base' => $this->detalleBase,
'terreno' => $this->detalleTerreno,
'neto' => $this->detalleNeto,
'iva' => $this->detalleIva,
'bruto' => $this->detalleBruto,
'descuento' => $this->detalleDescuento,
'total' => $this->detalleTotal
],
'total' => [
'neto' => $this->totalNeto,
'exento' => $this->totalExento,
'iva' => $this->totalIva,
'total' => $this->totalTotal
],
'uf' => [
'fecha' => $this->fechaUF->format('Y-m-d'),
'valor' => $this->valorUF
],
'estados' => $this->estados()
]);
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Incoviba\Model\Venta\Factura;
use DateTimeInterface;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Estado extends Ideal\Model
{
public Model\Venta\Factura $factura;
public DateTimeInterface $fecha;
public Model\Venta\Factura\Estado\Tipo $tipo;
public function jsonSerialize(): mixed
{
return array_merge(parent::jsonSerialize(), [
'factura_id' => $this->factura->id,
'fecha' => $this->fecha->format('Y-m-d'),
'tipo' => $this->tipo
]);
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace Incoviba\Model\Venta\Factura\Estado;
use Incoviba\Model\Tipo as Base;
class Tipo extends Base
{}

View File

@ -0,0 +1,62 @@
<?php
namespace Incoviba\Repository\Contabilidad\Movimiento;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Model;
use Incoviba\Repository;
class Auxiliar extends Ideal\Repository
{
public function __construct(Define\Connection $connection, protected Repository\Contabilidad\Movimiento $movimientoRepository)
{
parent::__construct($connection);
$this->setTable('movimientos_auxiliares');
}
public function create(?array $data = null): Model\Contabilidad\Movimiento\Auxiliar
{
$map = (new Implement\Repository\MapperParser(['cargo', 'abono']))
->register('movimiento_id', (new Implement\Repository\Mapper())
->setProperty('movimiento')
->setFunction(function($data) {
return $this->movimientoRepository->fetchById($data['movimiento_id']);
}));
return $this->parseData(new Model\Contabilidad\Movimiento\Auxiliar(), $data, $map);
}
public function save(Define\Model $model): Model\Contabilidad\Movimiento\Auxiliar
{
$model->id = $this->saveNew([
'movimiento_id',
'cargo',
'abono'
],[
$model->movimiento->id,
$model->cargo,
$model->abono
]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Contabilidad\Movimiento\Auxiliar
{
return $this->update($model, [
'movimiento_id',
'cargo',
'abono'
], $new_data);
}
/**
* @throws EmptyResult
*/
public function fetchByMovimiento(int $movimiento_id): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('movimiento_id = :movimiento_id');
return $this->fetchMany($query, ['movimiento_id' => $movimiento_id]);
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace Incoviba\Repository\Contabilidad\Movimiento\Auxiliar;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Model;
use Incoviba\Repository;
class Detalle extends Ideal\Repository
{
public function __construct(Define\Connection $connection,
protected Repository\Contabilidad\Movimiento\Auxiliar $auxiliarRepository,
protected Repository\Contabilidad\CentroCosto $centroCostoRepository)
{
parent::__construct($connection);
$this->setTable('auxiliar_detalles');
}
public function create(?array $data = null): Model\Contabilidad\Movimiento\Auxiliar\Detalle
{
$map = (new Implement\Repository\MapperParser(['rut', 'digito', 'nombre', 'categoria', 'detalle']))
->register('auxiliar_id', (new Implement\Repository\Mapper())
->setProperty('auxiliar')
->setFunction(function($data) {
return $this->auxiliarRepository->fetchById($data['auxiliar_id']);
}))
->register('centro_costo_id', (new Implement\Repository\Mapper())
->setProperty('centroCosto')
->setFunction(function($data) {
return $this->centroCostoRepository->fetchById($data['centro_costo_id']);
})
->setDefault(null));
return $this->parseData(new Model\Contabilidad\Movimiento\Auxiliar\Detalle(), $data, $map);
}
public function save(Define\Model $model): Model\Contabilidad\Movimiento\Auxiliar\Detalle
{
$this->saveNew([
'auxiliar_id',
'rut',
'digito',
'nombre',
'categoria',
'detalle'
], [
$model->auxiliar->id,
$model->rut,
$model->digito,
$model->nombre,
$model->categoria,
$model->detalle
]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Contabilidad\Movimiento\Auxiliar\Detalle
{
return $this->update($model, ['rut', 'digito', 'nombre', 'categoria', 'detalle'], $new_data);
}
/**
* @throws EmptyResult
*/
public function fetchByAuxiliar(int $auxiliar_id): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('auxiliar_id = :auxiliar_id');
return $this->fetchMany($query, ['auxiliar_id' => $auxiliar_id]);
}
}

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Incoviba\Repository\Movimiento; namespace Incoviba\Repository\Contabilidad\Movimiento;
use Incoviba\Common\Define; use Incoviba\Common\Define;
use Incoviba\Common\Ideal; use Incoviba\Common\Ideal;
@ -9,14 +9,15 @@ use Incoviba\Repository;
class Detalle extends Ideal\Repository class Detalle extends Ideal\Repository
{ {
public function __construct(Define\Connection $connection, protected Repository\Contabilidad\Movimiento $movimientoRepository, public function __construct(Define\Connection $connection,
protected Repository\Contabilidad\Movimiento $movimientoRepository,
protected Repository\Contabilidad\CentroCosto $centroCostoRepository) protected Repository\Contabilidad\CentroCosto $centroCostoRepository)
{ {
parent::__construct($connection); parent::__construct($connection);
$this->setTable('movimientos_detalles'); $this->setTable('movimientos_detalles');
} }
public function create(?array $data = null): Model\Movimiento\Detalle public function create(?array $data = null): Model\Contabilidad\Movimiento\Detalle
{ {
$map = (new Implement\Repository\MapperParser(['detalle'])) $map = (new Implement\Repository\MapperParser(['detalle']))
->register('movimiento_id', (new Implement\Repository\Mapper()) ->register('movimiento_id', (new Implement\Repository\Mapper())
@ -29,22 +30,22 @@ class Detalle extends Ideal\Repository
->setFunction(function(array $data) { ->setFunction(function(array $data) {
return $this->centroCostoRepository->fetchById($data['centro_costo_id']); return $this->centroCostoRepository->fetchById($data['centro_costo_id']);
})); }));
return $this->parseData(new Model\Movimiento\Detalle(), $data, $map); return $this->parseData(new Model\Contabilidad\Movimiento\Detalle(), $data, $map);
} }
public function save(Define\Model $model): Model\Movimiento\Detalle public function save(Define\Model $model): Model\Contabilidad\Movimiento\Detalle
{ {
$this->saveNew( $this->saveNew(
['movimiento_id', 'centro_costo_id', 'detalle'], ['movimiento_id', 'centro_costo_id', 'detalle'],
[$model->movimiento->id, $model->centroCosto->id, $model->detalle] [$model->movimiento->id, $model->centroCosto->id, $model->detalles]
); );
return $model; return $model;
} }
public function edit(Define\Model $model, array $new_data): Model\Movimiento\Detalle public function edit(Define\Model $model, array $new_data): Model\Contabilidad\Movimiento\Detalle
{ {
return $this->update($model, ['movimiento_id', 'centro_costo_id', 'detalle'], $new_data); return $this->update($model, ['movimiento_id', 'centro_costo_id', 'detalle'], $new_data);
} }
public function fetchByMovimiento(int $movimiento_id): Model\Movimiento\Detalle public function fetchByMovimiento(int $movimiento_id): Model\Contabilidad\Movimiento\Detalle
{ {
$query = $this->connection->getQueryBuilder() $query = $this->connection->getQueryBuilder()
->select() ->select()

View File

@ -0,0 +1,86 @@
<?php
namespace Incoviba\Repository;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
class DatosPersona extends Ideal\Repository
{
public function __construct(Define\Connection $connection, protected Persona $personaRepository,
protected Direccion $direccionRepository)
{
parent::__construct($connection);
$this->setTable('datos_personas');
}
public function create(?array $data = null): Model\DatosPersona
{
$map = (new Implement\Repository\MapperParser())
->register('persona_rut', (new Implement\Repository\Mapper())
->setProperty('persona')
->setFunction(function($data) {
return $this->personaRepository->fetchById($data['persona_rut']);
}))
->register('direccion_id', (new Implement\Repository\Mapper())
->setProperty('direccion')
->setFunction(function($data) {
return $this->direccionRepository->fetchById($data['direccion_id']);
})->setDefault(null))
->register('telefono', (new Implement\Repository\Mapper())->setFunction(function($data) {
return $data['telefono'];
})->setDefault(null))
->register('email', (new Implement\Repository\Mapper())->setFunction(function($data) {
return $data['email'];
})->setDefault(null))
->register('fecha_nacimiento', (new Implement\Repository\Mapper\DateTime('fecha_nacimiento'))
->setDefault(null)
->setProperty('fechaNacimiento'))
->register('sexo', (new Implement\Repository\Mapper())->setFunction(function($data) {
return $data['sexo'];
})->setDefault(null))
->register('estado_civil', (new Implement\Repository\Mapper())->setFunction(function($data) {
return $data['estado_civil'];
})->setDefault(null)->setProperty('estadoCivil'))
->register('nacionalidad', (new Implement\Repository\Mapper())->setFunction(function($data) {
return $data['nacionalidad'];
})->setDefault(null))
->register('profesion', (new Implement\Repository\Mapper())->setFunction(function($data) {
return $data['profesion'];
})->setDefault(null));
return $this->parseData(new Model\DatosPersona(), $data, $map);
}
public function save(Define\Model $model): Model\DatosPersona
{
$this->saveNew([
'persona_rut', 'direccion_id', 'telefono', 'email', 'fecha_nacimiento', 'sexo', 'estado_civil',
'nacionalidad', 'profesion'
], [
$model->persona->rut, $model->direccion?->id, $model->telefono, $model->email, $model->fechaNacimiento,
$model->sexo, $model->estadoCivil, $model->nacionalidad, $model->profesion
]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\DatosPersona
{
return $this->update($model, [
'direccion_id', 'telefono', 'email', 'fecha_nacimiento', 'sexo', 'estado_civil',
'nacionalidad', 'profesion'
], $new_data);
}
public function fetchByPersona(int $persona_rut): Model\DatosPersona
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('persona_rut = ?');
return $this->fetchOne($query, [$persona_rut]);
}
protected function getKey(): string
{
return 'persona_rut';
}
}

View File

@ -24,6 +24,7 @@ class AgenteTipo extends Ideal\Repository
return ($data === null) ? null : $this->agenteRepository->fetchById($data['agente']); return ($data === null) ? null : $this->agenteRepository->fetchById($data['agente']);
})) }))
->register('tipo', (new Implement\Repository\Mapper()) ->register('tipo', (new Implement\Repository\Mapper())
->setProperty('tipoAgente')
->setFunction(function(?array $data) { ->setFunction(function(?array $data) {
return ($data === null) ? null : $this->tipoAgenteRepository->fetchById($data['tipo']); return ($data === null) ? null : $this->tipoAgenteRepository->fetchById($data['tipo']);
})); }));

View File

@ -0,0 +1,61 @@
<?php
namespace Incoviba\Repository\Inmobiliaria;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Repository;
use Incoviba\Service;
class Proveedor extends Ideal\Repository
{
public function __construct(Define\Connection $connection, protected Repository\Inmobiliaria $inmobiliariaRepository,
protected Repository\Sociedad $sociedadRepository)
{
parent::__construct($connection);
$this->setTable('proveedores');
}
public function create(?array $data = null): Model\Inmobiliaria\Proveedor
{
$map = (new Implement\Repository\MapperParser())
->register('inmobiliaria_rut', (new Implement\Repository\Mapper())
->setProperty('inmobiliaria')
->setFunction(function($data) {
return $this->inmobiliariaRepository->fetchById($data['inmobiliaria_rut']);
}))
->register('sociedad_rut', (new Implement\Repository\Mapper())
->setProperty('sociedad')
->setFunction(function($data) {
return $this->sociedadRepository->fetchById($data['sociedad_rut']);
}));
return $this->parseData(new Model\Inmobiliaria\Proveedor(), $data, $map);
}
public function save(Define\Model $model): Model\Inmobiliaria\Proveedor
{
$model->id = $this->saveNew(['inmobiliaria_rut', 'sociedad_rut'], [$model->inmobiliaria->rut, $model->sociedad->rut]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Inmobiliaria\Proveedor
{
return $this->update($model, ['sociedad_id'], $new_data);
}
public function fetchByInmobiliaria(int $inmobiliaria_rut): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('inmobiliaria_rut = :inmobiliaria_rut');
return array_map([$this, 'load'], $this->fetchMany($query, compact('inmobiliaria_rut')));
}
public function fetchBySociedad(int $sociedad_rut): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('sociedad_rut = :sociedad_rut');
return array_map([$this, 'load'], $this->fetchMany($query, compact('sociedad_rut')));
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Incoviba\Repository;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
class Persona extends Ideal\Repository
{
public function __construct(Define\Connection $connection)
{
parent::__construct($connection);
$this->setTable('personas');
}
public function create(?array $data = null): Model\Persona
{
$map = (new Implement\Repository\MapperParser(['rut', 'digito', 'nombres']))
->register('apellido_paterno', (new Implement\Repository\Mapper())->setProperty('apellidoPaterno'))
->register('apellido_materno', (new Implement\Repository\Mapper())->setProperty('apellidoMaterno'))
;
return $this->parseData(new Model\Persona(), $data, $map);
}
public function save(Define\Model $model): Model\Persona
{
$this->saveNew(['rut', 'digito', 'nombres', 'apellido_paterno', 'apellido_materno'],
[$model->rut, $model->digito, $model->nombres, $model->apellidoPaterno, $model->apellidoMaterno]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Persona
{
return $this->update($model, $new_data, ['rut', 'digito', 'nombres', 'apellido_paterno', 'apellido_materno']);
}
public function fetchByRut(int $rut): Model\Persona
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('rut = ?');
return $this->fetchOne($query, [$rut]);
}
protected function getKey(): string
{
return 'rut';
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Incoviba\Repository;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Service;
class Sociedad extends Ideal\Repository
{
public function __construct(Define\Connection $connection,
protected Inmobiliaria\TipoSociedad $tipoSociedadRepository,
protected Service\Persona $personaService)
{
parent::__construct($connection);
$this->setTable('sociedades');
}
public function create(?array $data = null): Model\Sociedad
{
$map = (new Implement\Repository\MapperParser(['rut', 'digito', 'nombre']))
->register('razon', (new Implement\Repository\Mapper())
->setProperty('razonSocial'))
->register('tipo_sociedad_id', (new Implement\Repository\Mapper())
->setProperty('tipoSociedad')
->setFunction(function ($data) {
return $this->tipoSociedadRepository->fetchById($data['tipo_sociedad_id']);
})
)
->register('contacto_rut', (new Implement\Repository\Mapper())
->setProperty('contacto')
->setFunction(function ($data) {
return $this->personaService->getByRut($data['contacto_rut']);
}));
return $this->parseData(new Model\Sociedad(), $data, $map);
}
public function save(Define\Model $model): Model\Sociedad
{
$this->saveNew(['rut', 'digito', 'nombre', 'razon', 'tipo_sociedad_id', 'contacto_rut'],
[$model->rut, $model->digito, $model->nombre, $model->razonSocial, $model->tipoSociedad->id,
$model->contacto->rut]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Sociedad
{
return $this->update($model,
['digito', 'nombre', 'razon', 'tipo_sociedad_id', 'contacto_rut'], $new_data);
}
protected function getKey(): string
{
return 'rut';
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace Incoviba\Repository\Venta;
use DateTimeImmutable;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Model;
use Incoviba\Repository;
class Factura extends Ideal\Repository
{
public function __construct(Implement\Connection $connection, protected Repository\Venta $ventaRepository)
{
parent::__construct($connection);
$this->setTable('facturas');
}
public function create(?array $data = null): Model\Venta\Factura
{
$map = (new Implement\Repository\MapperParser(['index']))
->register('venta_id', (new Implement\Repository\Mapper())
->setProperty('venta')
->setFunction(function($data) {
return $this->ventaRepository->fetchById($data['venta_id']);
}));
$factura = $this->parseData(new Model\Venta\Factura(), $data, $map);
$json = json_decode($data['data']);
$factura->proporcion = $json->proporcion;
$factura->emisorRut = $json->emisor->rut;
$factura->emisorNombre = $json->emisor->nombre;
$factura->emisorDireccion = $json->emisor->direccion;
$factura->receptorRut = $json->receptor->rut;
$factura->receptorNombre = $json->receptor->nombre;
$factura->receptorDireccion = $json->receptor->direccion;
$factura->receptorComuna = $json->receptor->comuna;
$factura->fecha = new DateTimeImmutable($json->fecha);
$factura->unidades = $json->unidades;
$factura->detalleBase = $json->detalle->base;
$factura->detalleTerreno = $json->detalle->terreno;
$factura->detalleNeto = $json->detalle->neto;
$factura->detalleIva = $json->detalle->iva;
$factura->detalleBruto = $json->detalle->bruto;
$factura->detalleDescuento = $json->detalle->descuento;
$factura->detalleTotal = $json->detalle->total;
$factura->totalNeto = $json->total->neto;
$factura->totalExento = $json->total->exento;
$factura->totalIva = $json->total->iva;
$factura->totalTotal = $json->total->total;
$factura->fechaUF = new DateTimeImmutable($json->uf->fecha);
$factura->valorUF = $json->uf->valor;
return $factura;
}
public function save(Define\Model $model): Model\Venta\Factura
{
$model->id = $this->saveNew([
'venta_id',
'index',
'data'
], [
$model->venta->id,
$model->index,
json_encode([
'proporcion' => $model->proporcion,
'emisor' => [
'rut' => $model->emisorRut,
'nombre' => $model->emisorNombre,
'direccion' => $model->emisorDireccion
],
'receptor' => [
'rut' => $model->receptorRut,
'nombre' => $model->receptorNombre,
'direccion' => $model->receptorDireccion,
'comuna' => $model->receptorComuna
],
'fecha' => $model->fecha->format('Y-m-d'),
'unidades' => $model->unidades,
'detalle' => [
'base' => $model->detalleBase,
'terreno' => $model->detalleTerreno,
'neto' => $model->detalleNeto,
'iva' => $model->detalleIva,
'bruto' => $model->detalleBruto,
'descuento' => $model->detalleDescuento,
'total' => $model->detalleTotal
],
'total' => [
'neto' => $model->totalNeto,
'exento' => $model->totalExento,
'iva' => $model->totalIva,
'total' => $model->totalTotal
],
'uf' => [
'fecha' => $model->fechaUF->format('Y-m-d'),
'valor' => $model->valorUF
]
])
]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Venta\Factura
{
return $this->update($model, ['venta_id', 'index', 'data'], $new_data);
}
/**
* @throws EmptyResult
*/
public function fetchByVenta(int $venta_id): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('venta_id = :venta_id');
return $this->fetchMany($query, ['venta_id' => $venta_id]);
}
/**
* @throws EmptyResult
*/
public function fetchByVentaAndIndex(int $venta_id, int $index): Model\Venta\Factura
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('venta_id = :venta_id AND index = :index');
return $this->fetchOne($query, ['venta_id' => $venta_id, 'index' => $index]);
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Incoviba\Repository\Venta\Factura;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Repository;
class Estado extends Ideal\Repository
{
public function __construct(Define\Connection $connection, protected Repository\Venta\Factura $facturaRepository, protected Repository\Venta\Factura\Estado\Tipo $tipoRepository)
{
parent::__construct($connection);
$this->setTable('estados_facturas');
}
public function create(?array $data = null): Model\Venta\Factura\Estado
{
$map = (new Implement\Repository\MapperParser())
->register('factura_id', (new Implement\Repository\Mapper())
->setProperty('factura')
->setFunction(function($data) {
return $this->facturaRepository->fetchById($data['factura_id']);
}))
->register('fecha', new Implement\Repository\Mapper\DateTime('fecha'))
->register('tipo_id', (new Implement\Repository\Mapper())
->setProperty('tipo')
->setFunction(function($data) {
return $this->tipoRepository->fetchById($data['tipo_id']);
}));
return $this->parseData(new Model\Venta\Factura\Estado(), $data, $map);
}
public function save(Define\Model $model): Model\Venta\Factura\Estado
{
$model->id = $this->saveNew([
'factura_id',
'fecha',
'tipo_id'
], [
$model->factura->id,
$model->fecha->format('Y-m-d'),
$model->tipo->id
]);
return $model;
}
public function edit(Define\Model $model, array $new_data): Model\Venta\Factura\Estado
{
return $this->update($model, ['factura_id', 'fecha', 'tipo_id'], $new_data);
}
public function fetchByFactura(int $factura_id): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('factura_id = :factura_id');
return $this->fetchMany($query, ['factura_id' => $factura_id]);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Incoviba\Repository\Venta\Factura\Estado;
use Incoviba\Repository;
use Incoviba\Model;
class Tipo extends Repository\Tipo
{
public function getTable(): string
{
return 'tipos_estados_facturas';
}
protected function getBlank(): Model\Venta\Factura\Estado\Tipo
{
return new Model\Venta\Factura\Estado\Tipo;
}
}

View File

@ -65,7 +65,7 @@ class Propietario extends Ideal\Repository
{ {
$model->rut = $this->saveNew( $model->rut = $this->saveNew(
['rut', 'dv', 'nombres', 'apellido_paterno', 'apellido_materno', 'direccion', 'otro', 'representante'], ['rut', 'dv', 'nombres', 'apellido_paterno', 'apellido_materno', 'direccion', 'otro', 'representante'],
[$model->rut, $model->dv, $model->nombres, $model->apellidos['paterno'], $model->apellidos['materno'], $model->datos->direccion->id, $model->otro->rut ?? 0, $model->representante->rut ?? 0] [$model->rut, $model->dv, $model->nombres, $model->apellidos['paterno'], $model->apellidos['materno'], $model->datos->direccion->id, $model->otro->rut ?? 0, $model->contacto->rut ?? 0]
); );
return $model; return $model;
} }

View File

@ -0,0 +1,36 @@
<?php
namespace Incoviba\Service\Contabilidad\Informe;
use DateTimeInterface;
use DateInterval;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal;
use Incoviba\Repository;
use Incoviba\Service;
class Semanal extends Ideal\Service
{
public function __construct(LoggerInterface $logger, protected Repository\Inmobiliaria $repositoryRepository,
protected Service\Inmobiliaria\Cuenta $cuentaService,
protected Repository\Contabilidad\Deposito $depositoRepository,
protected Service\Contabilidad\Cartola $cartolaService,
protected Service\Contabilidad\Movimiento $movimientoService,
protected Service\Contabilidad\Informe\Tesoreria\Excel $excelService,
protected Service\Contabilidad\Informe\Tesoreria\PDF $pdfService)
{
parent::__construct($logger);
}
public function getAnterior(DateTimeInterface $fecha): DateTimeInterface
{
if (!isset($this->anterior)) {
$this->anterior = $fecha->sub(new DateInterval('P1D'));
if ($this->anterior->format('N') === '7') {
$this->anterior = $fecha->sub(new DateInterval('P3D'));
}
}
return $this->anterior;
}
public function build(DateTimeInterface $fecha): array
{}
}

View File

@ -2,17 +2,18 @@
namespace Incoviba\Service\Contabilidad; namespace Incoviba\Service\Contabilidad;
use DateTimeInterface; use DateTimeInterface;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal\Service; use Incoviba\Common\Ideal\Service;
use Incoviba\Common\Implement; use Incoviba\Common\Implement;
use Incoviba\Model; use Incoviba\Model;
use Incoviba\Repository; use Incoviba\Repository;
use Psr\Log\LoggerInterface;
class Movimiento extends Service class Movimiento extends Service
{ {
public function __construct(LoggerInterface $logger, public function __construct(LoggerInterface $logger,
protected Repository\Contabilidad\Movimiento $movimientoRepository, protected Repository\Contabilidad\Movimiento $movimientoRepository,
protected Repository\Movimiento\Detalle $detalleRepository) protected Repository\Contabilidad\Movimiento\Detalle $detalleRepository,
protected Movimiento\Auxiliar $auxiliarService)
{ {
parent::__construct($logger); parent::__construct($logger);
} }
@ -48,9 +49,20 @@ class Movimiento extends Service
public function process(Model\Contabilidad\Movimiento $movimiento): Model\Contabilidad\Movimiento public function process(Model\Contabilidad\Movimiento $movimiento): Model\Contabilidad\Movimiento
{ {
$movimiento->addFactory('detalles', (new Implement\Repository\Factory())->setCallable(function(int $movimiento_id) { $movimiento->addFactory('detalles', (new Implement\Repository\Factory())
return $this->detalleRepository->fetchByMovimiento($movimiento_id); ->setCallable(function(int $movimiento_id) {
})->setArgs(['movimiento_id' => $movimiento->id])); try {
return $this->detalleRepository->fetchByMovimiento($movimiento_id);
} catch (Implement\Exception\EmptyResult) {
return null;
}
})
->setArgs(['movimiento_id' => $movimiento->id]));
$movimiento->addFactory('auxiliares', (new Implement\Repository\Factory())
->setCallable(function(int $movimiento_id) {
return $this->auxiliarService->getByMovimiento($movimiento_id);
})
->setArgs(['movimiento_id' => $movimiento->id]));
return $movimiento; return $movimiento;
} }
} }

View File

@ -0,0 +1,42 @@
<?php
namespace Incoviba\Service\Contabilidad\Movimiento;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Repository;
use Incoviba\Service;
class Auxiliar extends Ideal\Service
{
public function __construct(LoggerInterface $logger,
protected Repository\Contabilidad\Movimiento\Auxiliar $auxiliarRepository,
protected Repository\Contabilidad\Movimiento\Auxiliar\Detalle $detalleRepository)
{
parent::__construct($logger);
}
public function getByMovimiento(int $movimiento_id): ?array
{
try {
return array_map([$this, 'process'], $this->auxiliarRepository->fetchByMovimiento($movimiento_id));
} catch (Implement\Exception\EmptyResult) {
return null;
}
}
protected function process(Model\Contabilidad\Movimiento\Auxiliar $auxiliar): Model\Contabilidad\Movimiento\Auxiliar
{
$auxiliar->addFactory('detalles', (new Implement\Repository\Factory())
->setCallable(function(int $auxiliar_id) {
try {
return $this->detalleRepository->fetchByAuxiliar($auxiliar_id);
} catch (Implement\Exception\EmptyResult) {
return null;
}
})
->setArgs(['auxiliar_id' => $auxiliar->id]));
return $auxiliar;
}
}

View File

@ -40,9 +40,6 @@ class Nubox extends Ideal\Service
->withHeader('Content-Type', 'application/json') ->withHeader('Content-Type', 'application/json')
->withHeader('Accept', 'application/json'); ->withHeader('Accept', 'application/json');
$response = $this->client->sendRequest($request); $response = $this->client->sendRequest($request);
if ($response->getStatusCode() !== 200) {
throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
}
$sistemas = json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY); $sistemas = json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
$this->setToken($inmobiliaria_rut, $response->getHeaderLine('Token')) $this->setToken($inmobiliaria_rut, $response->getHeaderLine('Token'))
@ -133,9 +130,6 @@ class Nubox extends Ideal\Service
]; ];
$uri = 'contabilidad/libro-mayor?' . http_build_query($query); $uri = 'contabilidad/libro-mayor?' . http_build_query($query);
$response = $this->send($uri, $inmobiliaria_rut); $response = $this->send($uri, $inmobiliaria_rut);
if ($response->getStatusCode() !== 200) {
throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
}
return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY); return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
} }
public function getLibroDiario(int $inmobiliaria_rut, DateTimeInterface $from, DateTimeInterface $to): array public function getLibroDiario(int $inmobiliaria_rut, DateTimeInterface $from, DateTimeInterface $to): array
@ -157,10 +151,6 @@ class Nubox extends Ideal\Service
]; ];
$uri = 'contabilidad/libro-diario?' . http_build_query($query); $uri = 'contabilidad/libro-diario?' . http_build_query($query);
$response = $this->send($uri, $inmobiliaria_rut); $response = $this->send($uri, $inmobiliaria_rut);
if ($response->getStatusCode() !== 200) {
$this->logger->debug($uri);
throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
}
return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY); return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
} }
public function getMesCuenta(int $inmobiliaria_rut, string $cuenta, DateTimeInterface $mes): array public function getMesCuenta(int $inmobiliaria_rut, string $cuenta, DateTimeInterface $mes): array
@ -186,11 +176,29 @@ class Nubox extends Ideal\Service
]; ];
$uri = 'contabilidad/libro-mayor?' . http_build_query($query); $uri = 'contabilidad/libro-mayor?' . http_build_query($query);
$response = $this->send($uri, $inmobiliaria_rut); $response = $this->send($uri, $inmobiliaria_rut);
if ($response->getStatusCode() !== 200) {
throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
}
return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY); return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
} }
public function getFacturas(int $inmobiliaria_rut, DateTimeInterface $dia): array
{
//$inmobiliaria = $this->nuboxRepository->fetchByInmobiliaria($inmobiliaria_rut);
$query = [
'factura',
'documento',
'78017310-6',
'estadoVenta',
551,
'FAC-EL',
1
];
$uri = implode('/', $query);
$response = $this->send($uri, $inmobiliaria_rut);
$content = json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
if (!is_array($content)) {
$this->logger->error($content);
return [];
}
return $content;
}
private function send(string $uri, int $inmobiliaria_rut, string $method = 'GET', ?StreamInterface $body = null): ResponseInterface private function send(string $uri, int $inmobiliaria_rut, string $method = 'GET', ?StreamInterface $body = null): ResponseInterface
{ {
@ -200,6 +208,12 @@ class Nubox extends Ideal\Service
if ($body !== null) { if ($body !== null) {
$request = $request->withBody($body); $request = $request->withBody($body);
} }
return $this->client->sendRequest($request); $response = $this->client->sendRequest($request);
if ($response->getStatusCode() !== 200) {
$json = json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
$message = $json['Message'] ?? '';
throw new Exception\HttpResponse($response->getReasonPhrase(), $message, $response->getStatusCode());
}
return $response;
} }
} }

View File

@ -0,0 +1,56 @@
<?php
namespace Incoviba\Service;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Repository;
class Persona extends Ideal\Service
{
public function __construct(LoggerInterface $logger, protected Repository\Persona $personaRepository,
protected Repository\DatosPersona $datosPersonaRepository)
{
parent::__construct($logger);
}
public function getByRut(int $rut): Model\Persona
{
return $this->process($this->personaRepository->fetchByRut($rut));
}
public function add(array $data): Model\Persona
{
try {
$persona = $this->personaRepository->fetchByRut($data['rut']);
} catch (Implement\Exception\EmptyResult) {
$persona = $this->personaRepository->create($data);
$persona = $this->personaRepository->save($persona);
}
if (isset($data['email']) or isset($data['telefono'])) {
$datosData = ['persona_rut' => $persona->rut];
if (isset($data['email'])) {
$datosData['email'] = $data['email'];
}
if (isset($data['telefono'])) {
$datosData['telefono'] = $data['telefono'];
}
try {
$datos = $this->datosPersonaRepository->fetchByPersona($persona->rut);
$this->datosPersonaRepository->edit($datos, $data);
} catch (Implement\Exception\EmptyResult) {
$datos = $this->datosPersonaRepository->create($datosData);
$this->datosPersonaRepository->save($datos);
}
}
return $this->process($persona);
}
protected function process(Model\Persona $persona): Model\Persona
{
$persona->addFactory('datos', (new Implement\Repository\Factory())
->setCallable([$this->datosPersonaRepository, 'fetchByPersona'])
->setArgs(['persona_rut' => $persona->rut]));
return $persona;
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace Incoviba\Service;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal;
use Incoviba\Repository;
use Incoviba\Model;
use Incoviba\Common\Implement\Exception\EmptyResult;
class Sociedad extends Ideal\Service
{
public function __construct(LoggerInterface $logger, protected Repository\Sociedad $sociedadRepository,
protected Repository\Inmobiliaria $inmobiliariaRepository,
protected Repository\Inmobiliaria\Proveedor $proveedorRepository)
{
parent::__construct($logger);
}
public function getByRut(int $sociedad_rut): ?Model\Sociedad
{
try {
return $this->process($this->sociedadRepository->fetchById($sociedad_rut));
} catch (EmptyResult) {
return null;
}
}
public function getAll(?string $orderBy = null): array
{
try {
return array_map([$this, 'process'], $this->sociedadRepository->fetchAll($orderBy));
} catch (EmptyResult) {
return [];
}
}
public function add(array $data): ?Model\Sociedad
{
try {
return $this->process($this->sociedadRepository->fetchById($data['rut']));
} catch (EmptyResult) {
try {
return $this->process($this->sociedadRepository->save($this->sociedadRepository->create($data)));
} catch (EmptyResult) {
return null;
}
}
}
public function edit(int $sociedad_rut, array $data): ?Model\Sociedad
{
try {
return $this->process(
$this->sociedadRepository->edit(
$this->sociedadRepository->fetchById($sociedad_rut), $data));
} catch (EmptyResult) {
return null;
}
}
public function delete(int $sociedad_rut): bool
{
try {
$this->sociedadRepository->remove($this->sociedadRepository->fetchById($sociedad_rut));
return true;
} catch (EmptyResult) {
return false;
}
}
public function asignar(int $inmobiliaria_rut, int $sociedad_rut): ?Model\Inmobiliaria\Proveedor
{
try {
$inmobiliaria = $this->inmobiliariaRepository->fetchById($inmobiliaria_rut);
$sociedad = $this->sociedadRepository->fetchById($sociedad_rut);
$data = [
'inmobiliaria_rut' => $inmobiliaria->rut,
'sociedad_rut' => $sociedad->rut,
];
return $this->proveedorRepository->save($this->proveedorRepository->create($data));
} catch (EmptyResult) {
return null;
}
}
protected function process(Model\Sociedad $sociedad): Model\Sociedad
{
return $sociedad;
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Incoviba\Service\Venta;
use DateTimeInterface;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Repository;
class Factura extends Ideal\Service
{
public function __construct(LoggerInterface $logger,
protected Repository\Venta\Factura $facturaRepository,
protected Repository\Venta\Factura\Estado $estadoRepository,
protected Repository\Venta\Factura\Estado\Tipo $tipoRepository)
{
parent::__construct($logger);
}
public function getAll(null|string|array $orderBy = null): array
{
try {
return array_map([$this, 'process'], $this->facturaRepository->fetchAll($orderBy));
} catch (Implement\Exception\EmptyResult) {
return [];
}
}
public function getByVenta(int $venta_id): array
{
try {
return array_map([$this, 'process'], $this->facturaRepository->fetchByVenta($venta_id));
} catch (Implement\Exception\EmptyResult) {
return [];
}
}
public function getById(int $factura_id): ?Model\Venta\Factura
{
try {
return $this->process($this->facturaRepository->fetchById($factura_id));
} catch (Implement\Exception\EmptyResult) {
return null;
}
}
public function getByVentaAndIndex(int $venta_id, int $index): ?Model\Venta\Factura
{
try {
return $this->process($this->facturaRepository->fetchByVentaAndIndex($venta_id, $index));
} catch (Implement\Exception\EmptyResult) {
return null;
}
}
public function add(array $data): Model\Venta\Factura
{
$factura = $this->getByVentaAndIndex($data['venta_id'], $data['index']);
if ($factura !== null) {
return $factura;
}
$factura = $this->facturaRepository->save($this->facturaRepository->create($data));
$tipo = $this->tipoRepository->fetchByDescripcion('generada');
$this->estadoRepository->save($this->estadoRepository->create([
'factura_id' => $factura->id,
'tipo_id' => $tipo->id,
'fecha' => $factura->fecha
]));
return $this->process($factura);
}
public function aprobar(int $factura_id, DateTimeInterface $fecha): ?Model\Venta\Factura
{
try {
$factura = $this->facturaRepository->fetchById($factura_id);
$tipo = $this->tipoRepository->fetchByDescripcion('aprobada');
$this->estadoRepository->save($this->estadoRepository->create([
'factura_id' => $factura->id,
'tipo_id' => $tipo->id,
'fecha' => $fecha->format('Y-m-d')
]));
return $this->process($factura);
} catch (Implement\Exception\EmptyResult) {
$this->logger->error('Error al aprobar factura', ['factura_id' => $factura_id]);
return null;
}
}
protected function process(Model\Venta\Factura $factura): Model\Venta\Factura
{
$factura->addFactory('estados', (new Implement\Repository\Factory())
->setCallable(function($factura_id) {
return $this->estadoRepository->fetchByFactura($factura_id);
})
->setArgs(['factura_id' => $factura->id]));
return $factura;
}
}

View File

@ -94,7 +94,7 @@ class Propietario extends Service
if ($sociedad->datos->direccion->id !== $mapped_data['direccion']) { if ($sociedad->datos->direccion->id !== $mapped_data['direccion']) {
$edits['direccion'] = $mapped_data['direccion']; $edits['direccion'] = $mapped_data['direccion'];
} }
if ($sociedad->representante->rut !== $mapped_data['representante']) { if ($sociedad->contacto->rut !== $mapped_data['representante']) {
$edits['representante'] = $mapped_data['representante']; $edits['representante'] = $mapped_data['representante'];
} }
$sociedad = $this->propietarioRepository->edit($sociedad, $edits); $sociedad = $this->propietarioRepository->edit($sociedad, $edits);

14
redisinsight.compose.yml Normal file
View File

@ -0,0 +1,14 @@
services:
insight:
profiles:
- insight
container_name: incoviba_insight
image: redis/redisinsight:latest
restart: unless-stopped
volumes:
- redisinsight:/data
ports:
- "5540:5540"
volumes:
redisinsight: {}