Facturacion y Terreno
This commit is contained in:
13
app/common/Implement/Exception/HttpResponse.php
Normal file
13
app/common/Implement/Exception/HttpResponse.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Incoviba\Common\Implement\Exception;
|
||||
|
||||
use Throwable;
|
||||
use HttpResponseException;
|
||||
|
||||
class HttpResponse extends HttpResponseException
|
||||
{
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
@ -5,8 +5,10 @@ use Incoviba\Controller\API\Contabilidad\Nubox;
|
||||
$app->group('/nubox/{inmobiliaria_rut}', function($app) {
|
||||
$app->get('/token[/]', [Nubox::class, 'token']);
|
||||
$app->get('/sistemas[/]', [Nubox::class, 'sistemas']);
|
||||
$app->get('/cuentas[/]', [Nubox::class, 'cuentas']);
|
||||
$app->group('/libro', function($app) {
|
||||
$app->post('/mayor[/]', [Nubox::class, 'libroMayor']);
|
||||
$app->post('/diario[/]', [Nubox::class, 'libroDiario']);
|
||||
});
|
||||
$app->post('/cuenta', [Nubox::class, 'cuenta']);
|
||||
});
|
||||
|
@ -13,23 +13,18 @@
|
||||
{{$venta->propiedad()->summary()}}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="ui very basic segment">
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">
|
||||
<div class="ui very segment">
|
||||
Valor Venta: {{$format->ufs($venta->valor)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form id="venta_form" class="ui form">
|
||||
<div class="two wide field">
|
||||
<label for="proporcion">Proporción Factura</label>
|
||||
<div class="ui right labeled input">
|
||||
<input type="number" name="proporcion" id="proporcion" value="100" max="100" min="0"/>
|
||||
<div class="ui basic icon label">
|
||||
<i class="percent icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
@foreach ($venta->propiedad()->unidades as $unidad)
|
||||
<div class="three wide field">
|
||||
<label for="precio{{$unidad->pu_id}}">Precio {{ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion)}} {{$unidad->descripcion}}</label>
|
||||
<label for="precio{{$unidad->pu_id}}">Precio<br /> {{ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion)}} {{$unidad->descripcion}}</label>
|
||||
<div class="ui right labeled input" id="input{{$unidad->pu_id}}">
|
||||
<input class="price" type="text" name="precio{{$unidad->pu_id}}" id="precio{{$unidad->pu_id}}" data-id="{{$unidad->pu_id}}" value="{{($unidad->valor > 0) ? $unidad->valor : $unidad->precio($venta->currentEstado()->fecha)->valor}}" />
|
||||
<div class="ui basic label">UF</div>
|
||||
@ -41,7 +36,7 @@
|
||||
@foreach($venta->propiedad()->unidades as $unidad)
|
||||
<div class="three wide field">
|
||||
@if ($unidad->prorrateo === 0.0)
|
||||
<label for="prorrateo{{$unidad->id}}">Prorrateo {{ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion)}} {{$unidad->descripcion}}</label>
|
||||
<label for="prorrateo{{$unidad->id}}">Prorrateo<br /> {{ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion)}} {{$unidad->descripcion}}</label>
|
||||
<div class="ui right labeled input" id="prorrateo{{$unidad->id}}">
|
||||
<input class="prorrateo" type="text" data-id="{{$unidad->id}}" value="{{$unidad->prorrateo}}" />
|
||||
<div class="ui basic label">%</div>
|
||||
@ -51,8 +46,8 @@
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="ui very basic segment" id="total_unidades"></div>
|
||||
@php $lastDic = new DateTimeImmutable((new DateTimeImmutable())->sub(new DateInterval('P1Y'))->format('31-12-Y')); @endphp
|
||||
@if (!isset($venta->proyecto()->terreno->fecha) or $venta->proyecto()->terreno->fecha < $lastDic)
|
||||
@php $lastDic = new DateTimeImmutable((new DateTimeImmutable())->sub(new DateInterval('P1Y'))->format('Y-12-31')); @endphp
|
||||
@if (!isset($terreno->fecha) or $terreno->fecha->getTimestamp() < 0 or $terreno->fecha < $lastDic)
|
||||
<div class="four wide field">
|
||||
<label for="terreno">Valor Terreno al {{$lastDic->format('d-m-Y')}}</label>
|
||||
<div class="ui left labeled input">
|
||||
@ -61,6 +56,46 @@
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="two wide field">
|
||||
<label for="propietarios">Propietarios</label>
|
||||
<input type="number" name="cantidad_propietarios" id="cantidad_propietarios" min="1" value="1" />
|
||||
</div>
|
||||
<div id="propietarios">
|
||||
<div class="fields" data-index="1">
|
||||
<div class="two wide field">
|
||||
<label for="proporcion1">Proporción Factura</label>
|
||||
<div class="ui right labeled input">
|
||||
<input type="number" name="proporcion1" id="proporcion1" value="100" max="100" min="0"/>
|
||||
<div class="ui basic icon label">
|
||||
<i class="percent icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide field">
|
||||
<label for="rut1">RUT</label>
|
||||
<div class="ui input">
|
||||
<input type="text" name="rut1" id="rut1" value="{{$venta->propietario()->rut()}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="five wide field">
|
||||
<label for="propietario1">Propietario</label>
|
||||
<div class="ui input">
|
||||
<input type="text" name="propietario1" id="propietario1" value="{{$venta->propietario()->nombreCompleto()}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="six wide field">
|
||||
<label for="direccion_propietario1">Dirección</label>
|
||||
<div class="ui input">
|
||||
<input type="text" name="direccion_propietario1" id="direccion_propietario1" value="{{$venta->propietario()->datos->direccion->simple()}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="two wide field">
|
||||
<label for="comuna_propietario1">Comuna</label>
|
||||
<input type="text" name="comuna_propietario1" id="comuna_propietario1" value="{{$venta->propietario()->datos->direccion->comuna->descripcion}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="propietarios" class="fields"></div>
|
||||
</form>
|
||||
@if ($venta->currentEstado()->fecha->sub(new DateInterval('P1M')) > $venta->proyecto()->terreno->fecha
|
||||
and $IPC->get($venta->proyecto()->terreno->fecha, $venta->currentEstado()->fecha->sub(new DateInterval('P1M'))) === 0.0)
|
||||
@ -71,8 +106,8 @@
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="ui divider"></div>
|
||||
<div id="factura">
|
||||
<div id="facturas"></div>
|
||||
{{--<div id="factura">
|
||||
<div class="ui compact grid">
|
||||
<div class="two columns row">
|
||||
<div class="twelve wide column">
|
||||
@ -174,18 +209,213 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>--}}
|
||||
@endsection
|
||||
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
const factura = {
|
||||
id: '#unidades',
|
||||
totales: {},
|
||||
proporcion: 1,
|
||||
precio: {{$UF->transform($venta->currentEstado()->fecha, $venta->valor)}},
|
||||
class Factura
|
||||
{
|
||||
props
|
||||
|
||||
constructor(props) {
|
||||
this.props = props
|
||||
}
|
||||
|
||||
draw({parentDiv, inmobiliaria, proyecto, propietario, unidades, venta, estadoVenta, uf, formatters = {date, pesos, ufs, percent}}) {
|
||||
const output = [
|
||||
'<div class="ui divider"></div>'+
|
||||
'<div class="factura">'+
|
||||
'<div class="ui compact grid">'+
|
||||
'<div class="two columns row">'+
|
||||
'<div class="twelve wide column">'+
|
||||
'<strong>'+inmobiliaria.nombre+'</strong><br/>'+
|
||||
'GIRO: <br/>'+
|
||||
'Dirección:'+proyecto.direccion+
|
||||
'</div>'+
|
||||
'<div class="four wide column">'+
|
||||
'<div class="ui center aligned red segment">'+
|
||||
'<strong>'+
|
||||
'RUT:'+inmobiliaria.rut+'<br/>'+
|
||||
'FACTURA ELECTRÓNICA<br/>'+
|
||||
'N° #'+
|
||||
'</strong>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="row">'+
|
||||
'<table class="ui table">'+
|
||||
'<tr>'+
|
||||
'<td class="grey"><strong>Señor(es)</strong></td>'+
|
||||
'<td>'+propietario.nombre+'</td>'+
|
||||
'<td class="grey"><strong>RUT</strong></td>'+
|
||||
'<td>'+propietario.rut+'</td>'+
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td class="grey"><strong>Giro</strong></td>'+
|
||||
'<td>Otras Actividades Profesionales</td>'+
|
||||
'<td class="grey"><strong>Fecha Emisión</strong></td>'+
|
||||
'<td>'+formatters.date.format(estadoVenta.fecha)+'</td>'+
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td class="grey"><strong>Dirección</strong></td>'+
|
||||
'<td>'+propietario.direccion+'</td>'+
|
||||
'<td class="grey"><strong>Comuna</strong></td>'+
|
||||
'<td>'+propietario.comuna.toUpperCase()+'</td>'+
|
||||
'</tr>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'<div class="row">'+
|
||||
'<table class="ui celled table">'+
|
||||
'<thead>'+
|
||||
'<tr class="grey">'+
|
||||
'<th class="center aligned" colspan="6">DETALLES</th>'+
|
||||
'</tr>'+
|
||||
'<tr class="grey">'+
|
||||
'<th>N°</th>'+
|
||||
'<th class="center aligned">Descripción</th>'+
|
||||
'<th class="center aligned">Cant/Unidad</th>'+
|
||||
'<th class="center aligned">Prec. Unit.</th>'+
|
||||
'<th class="center aligned">Ind</th>'+
|
||||
'<th class="center aligned">Total</th>'+
|
||||
'</tr>'+
|
||||
'</thead>'
|
||||
]
|
||||
const unidadesData = []
|
||||
|
||||
let c = 1
|
||||
const classes = [
|
||||
'',
|
||||
'',
|
||||
'center aligned',
|
||||
'right aligned',
|
||||
'center aligned',
|
||||
'right aligned'
|
||||
]
|
||||
unidades.forEach(unidad => {
|
||||
const descuento = parseFloat(proyecto.terreno) * parseFloat(unidad.prorrateo)
|
||||
const bruto = parseFloat(unidad.base) - descuento
|
||||
const neto = bruto / 1.19
|
||||
const data = [
|
||||
c ++,
|
||||
unidad.descripcion + ' (UF ' + formatters.ufs.format(unidad.precio * this.props.proporcion) + ')',
|
||||
'1 UNID',
|
||||
formatters.pesos.format(neto * this.props.proporcion),
|
||||
'AF',
|
||||
formatters.pesos.format(neto * this.props.proporcion)
|
||||
]
|
||||
|
||||
const row = ['<tr>']
|
||||
data.forEach((value, i) => {
|
||||
const cell = ['<td']
|
||||
if (classes[i] !== '') {
|
||||
cell.push(' class="'+classes[i]+'"')
|
||||
}
|
||||
cell.push('>'+value+'</td>')
|
||||
row.push(cell.join(''))
|
||||
})
|
||||
row.push('</tr>')
|
||||
unidadesData.push(row.join(''))
|
||||
})
|
||||
|
||||
const emptyTerreno = '<div class="ui tiny red horizontal circular label">0</div>'
|
||||
const data = [
|
||||
c,
|
||||
'Valor con Terreno ' + formatters.pesos.format((venta.base + venta.terreno) * this.props.proporcion) + ' - Menos valor terreno ' + ((venta.terreno > 0) ? formatters.pesos.format(-venta.terreno * this.props.proporcion) : emptyTerreno) + '<br />' +
|
||||
'Base imponible ' + formatters.pesos.format(venta.base * this.props.proporcion) + '<br />' +
|
||||
'IVA ' + formatters.pesos.format(venta.iva * this.props.proporcion) + '<br />' +
|
||||
'SUBTOTAL ' + formatters.pesos.format(venta.subtotal * this.props.proporcion) + '<br />' +
|
||||
'Mas valor terreno ' + ((venta.terreno > 0) ? formatters.pesos.format(venta.terreno * this.props.proporcion) : emptyTerreno) + '<br />' +
|
||||
'TOTAL ' + formatters.pesos.format(venta.total * this.props.proporcion) + ';' + formatters.ufs.format(venta.totalUF * this.props.proporcion) + ' UF<br /><br />' +
|
||||
'Descuento Terreno: ' + ((venta.terreno > 0) ? formatters.percent.format(venta.prorrateo * 100) : emptyTerreno) + '%<br /><br />' +
|
||||
'UF: ' + formatters.ufs.format(uf),
|
||||
'1 UNID',
|
||||
formatters.pesos.format(venta.terreno * this.props.proporcion),
|
||||
'EX',
|
||||
formatters.pesos.format(venta.terreno * this.props.proporcion)
|
||||
]
|
||||
|
||||
const row = ['<tr class="top aligned">']
|
||||
data.forEach((value, i) => {
|
||||
const cell = ['<td']
|
||||
if (classes[i] !== '') {
|
||||
cell.push(' class="'+classes[i]+'"')
|
||||
}
|
||||
cell.push('>'+value+'</td>')
|
||||
row.push(cell.join(''))
|
||||
})
|
||||
unidadesData.push(row.join(''))
|
||||
|
||||
output.push('<tbody id="unidades">'+unidadesData.join('')+'</tbody>')
|
||||
output.push(
|
||||
'<tfoot>'+
|
||||
'<tr>'+
|
||||
'<td colspan="6">'+
|
||||
'<br />'+
|
||||
'<br />'+
|
||||
'<br />'+
|
||||
'<br />'+
|
||||
'</td>'+
|
||||
'</tr>'+
|
||||
'</tfoot>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'<div class="row">'+
|
||||
'<div class="ten wide column"></div>'+
|
||||
'<div class="six wide column">'+
|
||||
'<table class="ui celled very compact table">'+
|
||||
'<thead>'+
|
||||
'<tr>'+
|
||||
'<th class="center aligned grey" colspan="2">TOTALES</th>'+
|
||||
'</tr>'+
|
||||
'</thead>'+
|
||||
'<tbody>'+
|
||||
'<tr>'+
|
||||
'<td class="grey">Monto Neto</td>'+
|
||||
'<td class="right aligned" id="neto">'+formatters.pesos.format(venta.base * this.props.proporcion)+'</td>'+
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td class="grey">Monto Exento</td>'+
|
||||
'<td class="right aligned" id="exento">'+formatters.pesos.format(venta.terreno * this.props.proporcion)+'</td>'+
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td class="grey">19% IVA</td>'+
|
||||
'<td class="right aligned" id="iva">'+formatters.pesos.format(venta.iva * this.props.proporcion)+'</td>'+
|
||||
'</tr>'+
|
||||
'<tr>'+
|
||||
'<td class="grey">Monto Total</td>'+
|
||||
'<td class="right aligned"><strong id="total">'+formatters.pesos.format(venta.total * this.props.proporcion)+'</strong></td>'+
|
||||
'</tr>'+
|
||||
'</tbody>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'
|
||||
)
|
||||
parentDiv.append(output.join(''))
|
||||
}
|
||||
}
|
||||
const facturasForm = {
|
||||
props: {
|
||||
ids: {
|
||||
facturas: '',
|
||||
propietarios: ''
|
||||
}
|
||||
},
|
||||
inmobiliaria: {
|
||||
rut: '{{$venta->proyecto()->inmobiliaria()->rut()}}',
|
||||
nombre: '{{mb_strtoupper($venta->proyecto()->inmobiliaria()->nombreCompleto())}}'
|
||||
},
|
||||
proyecto: {
|
||||
direccion: '{{$venta->proyecto()->direccion()->simple()}}',
|
||||
terreno: {{(isset($terreno->fecha) and $terreno->fecha >= $lastDic) ?
|
||||
$IPC->readjust($terreno->valor, $terreno->fecha, $venta->currentEstado()->fecha) : 0}},
|
||||
},
|
||||
estadoVenta: {
|
||||
fecha: new Date('{{$venta->currentEstado()->fecha->format('Y-m-d')}}')
|
||||
},
|
||||
precio: {{$UF->transform($venta->currentEstado()->fecha, $venta->valor)}},
|
||||
uf: {{$UF->get($venta->currentEstado()->fecha)}},
|
||||
unidades: JSON.parse('{!! json_encode(array_map(function(Incoviba\Model\Venta\PropiedadUnidad $unidad) use ($venta, $UF, $format) {
|
||||
$precio = ($unidad->valor > 0) ? $unidad->valor : ($unidad->precio($venta->currentEstado()->fecha) ? $unidad->precio($venta->currentEstado()->fecha)->valor : 0);
|
||||
@ -198,98 +428,84 @@
|
||||
'prorrateo' => $unidad->prorrateo,
|
||||
];
|
||||
}, $venta->propiedad()->unidades)) !!}'),
|
||||
build: function() {
|
||||
const tbody = $(this.id)
|
||||
tbody.html('')
|
||||
const pesoFormatter = new Intl.NumberFormat('es-CL', {maximumFractionDigits: 0, minimumFractionDigits: 0})
|
||||
const ufFormatter = new Intl.NumberFormat('es-CL', {maximumFractionDigits: 2, minimumFractionDigits: 2})
|
||||
const percentFormatter = new Intl.NumberFormat('es-CL', {maximumFractionDigits: 5, minimumFractionDigits: 5})
|
||||
let terreno = 0
|
||||
let prorrateo = 0
|
||||
let totalUnidades = 0
|
||||
let precioUnidades = 0
|
||||
let c = 1
|
||||
const classes = [
|
||||
'',
|
||||
'',
|
||||
'center aligned',
|
||||
'right aligned',
|
||||
'center aligned',
|
||||
'right aligned'
|
||||
]
|
||||
this.unidades.forEach(unidad => {
|
||||
totalUnidades += unidad.base
|
||||
precioUnidades += unidad.precio
|
||||
const descuento = this.terreno * unidad.prorrateo
|
||||
terreno += descuento
|
||||
prorrateo += unidad.prorrateo
|
||||
const bruto = unidad.base - descuento
|
||||
const neto = bruto / 1.19
|
||||
const data = [
|
||||
c ++,
|
||||
unidad.descripcion + ' (UF ' + ufFormatter.format(unidad.precio * this.proporcion) + ')',
|
||||
'1 UNID',
|
||||
pesoFormatter.format(neto * this.proporcion),
|
||||
'AF',
|
||||
pesoFormatter.format(neto * this.proporcion)
|
||||
]
|
||||
|
||||
const row = $('<tr></tr')
|
||||
data.forEach((value, i) => {
|
||||
const cell = $('<td></td>')
|
||||
if (classes[i] !== '') {
|
||||
cell.addClass(classes[i])
|
||||
venta: {
|
||||
base: 0,
|
||||
precio: 0,
|
||||
descuento: 0,
|
||||
prorrateo: 0,
|
||||
bruto: 0,
|
||||
iva: 0,
|
||||
neto: 0,
|
||||
subtotal: 0,
|
||||
total: 0,
|
||||
totalUF: 0
|
||||
},
|
||||
propietarios: [
|
||||
{
|
||||
rut: '{{$venta->propietario()->rut()}}',
|
||||
nombre: '{{$venta->propietario()->nombreCompleto()}}',
|
||||
direccion: '{{$venta->propietario()->datos->direccion->simple()}}',
|
||||
comuna: '{{$venta->propietario()->datos->direccion->comuna->descripcion}}'
|
||||
}
|
||||
cell.html(value)
|
||||
row.append(cell)
|
||||
})
|
||||
tbody.append(row)
|
||||
})
|
||||
$('#total_unidades')
|
||||
.attr('class', 'ui compact segment ' + ((totalUnidades.toFixed(2) !== this.precio.toFixed(2)) ? 'inverted red' : 'inverted green'))
|
||||
.html('Total Unidades: ' + ufFormatter.format(precioUnidades) + ' UF' +
|
||||
((totalUnidades.toFixed(2) !== this.precio.toFixed(2)) ? '; Diferencia: ' + ufFormatter.format({{$venta->valor}} - precioUnidades) + ' UF' : ''))
|
||||
if (totalUnidades.toFixed(2) !== this.precio.toFixed(2)) {
|
||||
this.highlight()
|
||||
],
|
||||
facturas: [],
|
||||
draw() {
|
||||
return {
|
||||
propietarios: () => {
|
||||
const proporcion = (100 / this.propietarios.length).toFixed()
|
||||
const parent = $(this.props.ids.propietarios)
|
||||
parent.html('')
|
||||
this.propietarios.forEach((propietario, idx) => {
|
||||
parent.append(
|
||||
'<div class="fields" data-index="'+(idx+1)+'">' +
|
||||
'<div class="two wide field">'+
|
||||
'<label for="proporcion'+(idx+1)+'">Proporción Factura</label>'+
|
||||
'<div class="ui right labeled input">'+
|
||||
'<input type="number" class="proporcion" name="proporcion'+(idx+1)+'" id="proporcion'+(idx+1)+'" value="'+proporcion+'" max="100" min="0"/>'+
|
||||
'<div class="ui basic icon label">'+
|
||||
'<i class="percent icon"></i>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="three wide field">'+
|
||||
'<label for="rut'+(idx+1)+'">RUT</label>'+
|
||||
'<div class="ui input">'+
|
||||
'<input type="text" name="rut'+(idx+1)+'" id="rut'+(idx+1)+'" value="'+propietario.rut+'" />'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="five wide field">'+
|
||||
'<label for="propietario'+(idx+1)+'">Propietario</label>'+
|
||||
'<div class="ui input">'+
|
||||
'<input type="text" name="propietario'+(idx+1)+'" id="propietario'+(idx+1)+'" value="'+propietario.nombre+'" />'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="six wide field">'+
|
||||
'<label for="direccion_propietario'+(idx+1)+'">Dirección</label>'+
|
||||
'<div class="ui input">'+
|
||||
'<input type="text" name="direccion_propietario'+(idx+1)+'" id="direccion_propietario'+(idx+1)+'" value="'+propietario.direccion+'" />'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="two wide field">'+
|
||||
'<label for="comuna_propietario'+(idx+1)+'">Comuna</label>'+
|
||||
'<input type="text" name="comuna_propietario'+(idx+1)+'" id="comuna_propietario'+(idx+1)+'" value="'+propietario.comuna+'" />'+
|
||||
'</div>' +
|
||||
'</div>')
|
||||
const inputs = {
|
||||
proporcion: 'proporciones',
|
||||
rut: 'rut',
|
||||
propietario: 'propietario',
|
||||
direccion_propietario: 'direccion',
|
||||
comuna_propietario: 'comuna'
|
||||
}
|
||||
const bruto = this.precio - terreno
|
||||
const base = bruto / 1.19
|
||||
const iva = base * .19
|
||||
const subtotal = base + iva
|
||||
const total = subtotal + terreno
|
||||
const totalUF = total / this.uf
|
||||
|
||||
const emptyTerreno = '<div class="ui tiny red horizontal circular label">0</div>'
|
||||
const data = [
|
||||
c,
|
||||
'Valor con Terreno ' + pesoFormatter.format((base + terreno) * this.proporcion) + ' - Menos valor terreno ' + ((terreno > 0) ? pesoFormatter.format(-terreno * this.proporcion) : emptyTerreno) + '<br />' +
|
||||
'Base imponible ' + pesoFormatter.format(base * this.proporcion) + '<br />' +
|
||||
'IVA ' + pesoFormatter.format(iva * this.proporcion) + '<br />' +
|
||||
'SUBTOTAL ' + pesoFormatter.format(subtotal * this.proporcion) + '<br />' +
|
||||
'Mas valor terreno ' + ((terreno > 0) ? pesoFormatter.format(terreno * this.proporcion) : emptyTerreno) + '<br />' +
|
||||
'TOTAL ' + pesoFormatter.format(total * this.proporcion) + ';' + ufFormatter.format(totalUF * this.proporcion) + ' UF<br /><br />' +
|
||||
'Descuento Terreno: ' + ((terreno > 0) ? percentFormatter.format(prorrateo * 100) : emptyTerreno) + '%<br /><br />' +
|
||||
'UF: ' + ufFormatter.format(this.uf),
|
||||
'1 UNID',
|
||||
pesoFormatter.format(terreno * this.proporcion),
|
||||
'EX',
|
||||
pesoFormatter.format(terreno * this.proporcion)
|
||||
]
|
||||
const row = $('<tr></tr>').addClass('top aligned')
|
||||
data.forEach((value, i) => {
|
||||
const cell = $('<td></td>')
|
||||
if (classes[i] !== '') {
|
||||
cell.addClass(classes[i])
|
||||
}
|
||||
cell.html(value)
|
||||
row.append(cell)
|
||||
Object.entries(inputs).forEach(([name, method]) => {
|
||||
$("[name='"+name+(idx+1)+"']").change(event => {
|
||||
this.update()[method](idx)
|
||||
this.update().facturas()
|
||||
})
|
||||
tbody.append(row)
|
||||
|
||||
$(this.totales.afecto).html(pesoFormatter.format(base * this.proporcion))
|
||||
$(this.totales.exento).html(pesoFormatter.format(terreno * this.proporcion))
|
||||
$(this.totales.iva).html(pesoFormatter.format(iva * this.proporcion))
|
||||
$(this.totales.total).html(pesoFormatter.format(total * this.proporcion))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
update: function() {
|
||||
return {
|
||||
@ -299,7 +515,7 @@
|
||||
if (idx === -1) {
|
||||
return
|
||||
}
|
||||
const old_value = this.unidades[idx].precio
|
||||
const old_value = parseFloat(this.unidades[idx].precio)
|
||||
if (old_value === parseFloat(value)) {
|
||||
return
|
||||
}
|
||||
@ -316,8 +532,7 @@
|
||||
}
|
||||
const idx = this.unidades.findIndex(unidad => unidad.pid === json.propiedad_unidad_id)
|
||||
this.unidades[idx].precio = parseFloat(json.input.valor)
|
||||
this.unidades[idx].base = parseFloat(json.input.valor * this.unidades[idx].base / old_value)
|
||||
this.build()
|
||||
this.unidades[idx].base = parseFloat(json.input.valor) * parseFloat(this.unidades[idx].base) / old_value
|
||||
})
|
||||
},
|
||||
terreno: value => {
|
||||
@ -344,7 +559,7 @@
|
||||
}
|
||||
}).then(json => {
|
||||
this.terreno *= (1 + parseFloat(json.ipc))
|
||||
this.build()
|
||||
this.update().unidades()
|
||||
})
|
||||
})
|
||||
},
|
||||
@ -365,35 +580,128 @@
|
||||
}
|
||||
const idx = this.unidades.findIndex(unidad => unidad.id === json.unidad_id)
|
||||
this.unidades[idx].prorrateo = parseFloat(json.input.prorrateo)
|
||||
this.build()
|
||||
})
|
||||
},
|
||||
propietarios: value => {
|
||||
if (value < this.propietarios.length) {
|
||||
for (let i = value; i < this.propietarios.length; i ++) {
|
||||
this.propietarios.splice(i, 1)
|
||||
if (i in this.facturas.keys()) {
|
||||
this.facturas[i].splice(i, 1)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for (let i = this.propietarios.length; i < value; i ++) {
|
||||
const propietario = {
|
||||
rut: '',
|
||||
nombre: '',
|
||||
direccion: '',
|
||||
comuna: ''
|
||||
}
|
||||
this.propietarios.push(propietario)
|
||||
if (!(i in this.facturas.keys())) {
|
||||
this.facturas.push(new Factura({propietario, proporcion: 0}))
|
||||
}
|
||||
}
|
||||
},
|
||||
proporciones: idx => {
|
||||
const proporcion = $("[name='proporcion"+(idx+1)+"']")
|
||||
const proporciones = $('.proporcion')
|
||||
const total = proporciones.toArray().reduce((sum, input) => sum + parseInt($(input).val()), 0)
|
||||
if (total <= 100) {
|
||||
this.facturas[idx].props.proporcion = parseInt(proporcion.val()) / 100
|
||||
return
|
||||
}
|
||||
const otros = proporciones.toArray().filter(input => input.name !== 'proporcion' + (idx+1)).reduce((sum, input) => sum + parseInt($(input).val()), 0)
|
||||
proporcion.val(100 - otros)
|
||||
this.facturas[idx].props.proporcion = parseInt(proporcion.val()) / 100
|
||||
},
|
||||
rut: idx => {
|
||||
this.propietarios[idx].rut = $("[name='rut"+(idx+1)+"']").val()
|
||||
},
|
||||
propietario: idx => {
|
||||
this.propietarios[idx].nombre = $("[name='propietario"+(idx+1)+"']").val()
|
||||
},
|
||||
direccion: idx => {
|
||||
this.propietarios[idx].direccion = $("[name='direccion"+(idx+1)+"']").val()
|
||||
},
|
||||
comuna: idx => {
|
||||
this.propietarios[idx].comuna = $("[name='comuna"+(idx+1)+"']").val()
|
||||
},
|
||||
unidades: () => {
|
||||
const ufFormatter = new Intl.NumberFormat('es-CL', {maximumFractionDigits: 2, minimumFractionDigits: 2})
|
||||
let totalUnidades = 0
|
||||
let precioUnidades = 0
|
||||
let terreno = 0
|
||||
let prorrateo = 0
|
||||
this.unidades.forEach(unidad => {
|
||||
totalUnidades += parseFloat(unidad.base)
|
||||
precioUnidades += parseFloat(unidad.precio)
|
||||
terreno += parseFloat(this.proyecto.terreno) * parseFloat(unidad.prorrateo)
|
||||
prorrateo += parseFloat(unidad.prorrateo)
|
||||
})
|
||||
|
||||
$('#total_unidades')
|
||||
.attr('class', 'ui compact segment ' + ((totalUnidades.toFixed(2) !== this.precio.toFixed(2)) ? 'inverted red' : 'inverted green'))
|
||||
.html('Total Unidades: ' + ufFormatter.format(precioUnidades) + ' UF' +
|
||||
((totalUnidades.toFixed(2) !== this.precio.toFixed(2)) ? '; Diferencia: ' + ufFormatter.format({{$venta->valor}} - precioUnidades) + ' UF' : ''))
|
||||
if (totalUnidades.toFixed(2) !== this.precio.toFixed(2)) {
|
||||
this.highlight()
|
||||
}
|
||||
this.venta.base = totalUnidades
|
||||
this.venta.precio = precioUnidades
|
||||
this.venta.terreno = terreno
|
||||
this.venta.prorrateo = prorrateo
|
||||
this.venta.bruto = this.venta.base - this.venta.terreno
|
||||
this.venta.neto = this.venta.bruto / 1.19
|
||||
this.venta.iva = this.venta.neto * 0.19
|
||||
this.venta.subtotal = this.venta.base + this.venta.iva
|
||||
this.venta.total = this.venta.subtotal + this.venta.terreno
|
||||
this.venta.totalUF = this.venta.total / this.uf
|
||||
},
|
||||
facturas: () => {
|
||||
const facturas = $(this.props.ids.facturas)
|
||||
facturas.html('')
|
||||
this.facturas.forEach((factura, idx) => {
|
||||
factura.draw({
|
||||
parentDiv: facturas,
|
||||
inmobiliaria: this.inmobiliaria,
|
||||
proyecto: this.proyecto,
|
||||
propietario: this.propietarios[idx],
|
||||
unidades: this.unidades,
|
||||
venta: this.venta,
|
||||
estadoVenta: this.estadoVenta,
|
||||
uf: this.uf,
|
||||
formatters: {
|
||||
date: new Intl.DateTimeFormat('es-CL', {day:'2-digit',month:'2-digit',year:'numeric'}),
|
||||
pesos: new Intl.NumberFormat('es-CL', {maximumFractionDigits: 0, minimumFractionDigits: 0}),
|
||||
ufs: new Intl.NumberFormat('es-CL', {maximumFractionDigits: 2, minimumFractionDigits: 2}),
|
||||
percent: new Intl.NumberFormat('es-CL', {maximumFractionDigits: 5, minimumFractionDigits: 5})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: function() {
|
||||
return {
|
||||
proporcion: id => {
|
||||
$(id).change(event => {
|
||||
const val = $(event.currentTarget).val()
|
||||
if (val / 100 === this.proporcion) {
|
||||
return
|
||||
}
|
||||
this.proporcion = val / 100
|
||||
this.build()
|
||||
})
|
||||
},
|
||||
prices: class_name => {
|
||||
$(class_name).change(event => {
|
||||
const val = $(event.currentTarget).val()
|
||||
const id = $(event.currentTarget).data('id')
|
||||
this.update().price(id, val)
|
||||
this.update().price(id, val).then(() => {
|
||||
this.update().unidades()
|
||||
})
|
||||
})
|
||||
},
|
||||
prorrateo: class_name => {
|
||||
$(class_name).change(event => {
|
||||
const val = $(event.currentTarget).val()
|
||||
const id = $(event.currentTarget).data('id')
|
||||
this.update().prorrateo(id, val)
|
||||
this.update().prorrateo(id, val).then(() => {
|
||||
this.update().unidades()
|
||||
})
|
||||
})
|
||||
},
|
||||
terreno: id => {
|
||||
@ -401,8 +709,20 @@
|
||||
const val = $(event.currentTarget).val()
|
||||
this.update().terreno(val).then(() => {
|
||||
$(id).parent().parent().hide()
|
||||
this.update().unidades()
|
||||
})
|
||||
})
|
||||
},
|
||||
propietarios: id => {
|
||||
$(id).change(event => {
|
||||
let val = $(event.currentTarget).val()
|
||||
if (val < 1) {
|
||||
$(event.currentTarget).val(1)
|
||||
val = 1
|
||||
}
|
||||
this.update().propietarios(val)
|
||||
this.draw().propietarios()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -421,31 +741,31 @@
|
||||
input.find('.label').removeClass('red')
|
||||
input.removeAttr('data-content')
|
||||
},
|
||||
setup: function({form_id, tbody_id, input_id, prices_class, prorrateo_class, terreno_id, totales_ids}) {
|
||||
setup: function({form_id, prices_class, prorrateo_class, terreno_id, propietarios_id, count_propietarios_id, facturas_id}) {
|
||||
this.props.ids.facturas = facturas_id
|
||||
this.props.ids.propietarios = propietarios_id
|
||||
$(form_id).submit(event => {
|
||||
event.preventDefault()
|
||||
return false
|
||||
})
|
||||
this.id = tbody_id
|
||||
this.totales = totales_ids
|
||||
this.proporcion = $(input_id).val() / 100
|
||||
this.watch().proporcion(input_id)
|
||||
this.watch().prices(prices_class)
|
||||
this.watch().prorrateo(prorrateo_class)
|
||||
@if (!isset($venta->proyecto()->terreno->fecha) or $venta->proyecto()->terreno->fecha <= $lastDic)
|
||||
this.watch().propietarios(count_propietarios_id)
|
||||
@if (!isset($terreno->fecha) or $terreno->fecha->getTimestamp() < 0 or $terreno->fecha <= $lastDic)
|
||||
this.watch().terreno(terreno_id)
|
||||
@endif
|
||||
this.build()
|
||||
this.facturas.push(new Factura({propietario: this.propietarios[0], proporcion: 1}))
|
||||
this.update().unidades()
|
||||
this.update().propietarios($(count_propietarios_id).val())
|
||||
this.draw().propietarios()
|
||||
this.unidades.forEach((unidad, idx) => {
|
||||
this.update().proporciones(idx)
|
||||
})
|
||||
this.update().facturas()
|
||||
}
|
||||
}
|
||||
$(document).ready(() => {
|
||||
factura.setup({form_id: '#venta_form', tbody_id: '#unidades', input_id: '#proporcion',
|
||||
prices_class: '.price', prorrateo_class: '.prorrateo', terreno_id: '#terreno', totales_ids: {
|
||||
afecto: '#neto',
|
||||
exento: '#exento',
|
||||
iva: '#iva',
|
||||
total: '#total'
|
||||
}})
|
||||
facturasForm.setup({form_id: '#venta_form', prices_class: '.price', prorrateo_class: '.prorrateo', terreno_id: '#terreno', propietarios_id: '#propietarios', count_propietarios_id: '#cantidad_propietarios', facturas_id: '#facturas'})
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
|
@ -2,6 +2,8 @@
|
||||
namespace Incoviba\Controller\API\Contabilidad;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Incoviba\Common\Implement\Exception\EmptyRedis;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResult;
|
||||
use Incoviba\Common\Implement\Exception\HttpResponse;
|
||||
use Incoviba\Controller\API\withJson;
|
||||
use Incoviba\Service;
|
||||
@ -88,4 +90,34 @@ class Nubox
|
||||
}
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function cuentas(ServerRequestInterface $request, ResponseInterface $response, Service\Contabilidad\Nubox $nuboxService, int $inmobiliaria_rut): ResponseInterface
|
||||
{
|
||||
$body = $request->getParsedBody();
|
||||
$output = [
|
||||
'inmobiliaria_rut' => $inmobiliaria_rut,
|
||||
'input' => $body,
|
||||
'cuentas' => $nuboxService->getCuentas($inmobiliaria_rut)
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function cuenta(ServerRequestInterface $request, ResponseInterface $response, Service\Contabilidad\Nubox $nuboxService, int $inmobiliaria_rut): ResponseInterface
|
||||
{
|
||||
$body = $request->getParsedBody();
|
||||
$output = [
|
||||
'inmobiliaria_rut' => $inmobiliaria_rut,
|
||||
'input' => $body,
|
||||
'movimientos' => []
|
||||
];
|
||||
try {
|
||||
$mes = new DateTimeImmutable($body['mes']);
|
||||
$cuenta = $body['cuenta'];
|
||||
$output['movimientos'] = $nuboxService->getMesCuenta($inmobiliaria_rut, $cuenta, $mes);
|
||||
} catch (HttpResponse $exception) {
|
||||
$output['error'] = [
|
||||
'code' => $exception->getCode(),
|
||||
'message' => $exception->getMessage()
|
||||
];
|
||||
}
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,11 @@ class Nubox extends Ideal\Service
|
||||
{
|
||||
if (!isset($this->sistemas[$inmobiliaria_rut])) {
|
||||
$redisKey = "nubox:{$inmobiliaria_rut}";
|
||||
try {
|
||||
$this->sistemas[$inmobiliaria_rut] = json_decode($this->redisService->get($redisKey));
|
||||
} catch (Exception\EmptyRedis) {
|
||||
$this->getToken($inmobiliaria_rut);
|
||||
}
|
||||
}
|
||||
return $this->sistemas[$inmobiliaria_rut];
|
||||
}
|
||||
@ -80,6 +84,34 @@ class Nubox extends Ideal\Service
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuentas(int $inmobiliaria_rut): array
|
||||
{
|
||||
$redisKey = "nubox:cuentas:{$inmobiliaria_rut}";
|
||||
try {
|
||||
return json_decode($this->redisService->get($redisKey));
|
||||
} catch (Exception\EmptyRedis) {
|
||||
$today = new DateTimeImmutable();
|
||||
$libro = $this->getLibroMayor($inmobiliaria_rut, new DateTimeImmutable($today->format('Y-m-1')), new DateTimeImmutable($today->format('Y-m-t')));
|
||||
$cuentas = array_map(function($cuenta) {
|
||||
$full = $cuenta['Cuenta'];
|
||||
$space = strpos($full, ' ');
|
||||
return ['codigo' => substr($full, 0, $space), 'nombre' => substr($full, $space + 1)];
|
||||
}, $libro);
|
||||
$this->redisService->set($redisKey, json_encode($cuentas));
|
||||
return $cuentas;
|
||||
}
|
||||
}
|
||||
public function getCuenta(int $inmobiliaria_rut, string $cuentaNombre): string
|
||||
{
|
||||
$cuentas = $this->getCuentas($inmobiliaria_rut);
|
||||
$validas = array_filter($cuentas, function($cuenta) use ($cuentaNombre) {
|
||||
return strtolower($cuenta->nombre) === strtolower($cuentaNombre);
|
||||
});
|
||||
if (count($validas) === 0) {
|
||||
throw new Exception\EmptyResult('Cuenta no encontrada');
|
||||
}
|
||||
return array_values($validas)[0]->codigo;
|
||||
}
|
||||
public function getLibroMayor(int $inmobiliaria_rut, DateTimeInterface $from, DateTimeInterface $to): array
|
||||
{
|
||||
$inmobiliaria = $this->nuboxRepository->fetchByInmobiliaria($inmobiliaria_rut);
|
||||
@ -131,7 +163,7 @@ class Nubox extends Ideal\Service
|
||||
}
|
||||
return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
|
||||
}
|
||||
public function getSaldoCuenta(int $inmobiliaria_rut, string $cuenta, DateTimeInterface $mes, bool $acumuladoAnual = false): array
|
||||
public function getMesCuenta(int $inmobiliaria_rut, string $cuenta, DateTimeInterface $mes): array
|
||||
{
|
||||
$inmobiliaria = $this->nuboxRepository->fetchByInmobiliaria($inmobiliaria_rut);
|
||||
$from = new DateTimeImmutable($mes->format('Y-m-1'));
|
||||
|
@ -2,6 +2,7 @@
|
||||
namespace Incoviba\Service\Proyecto;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateInterval;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Common\Implement;
|
||||
@ -12,7 +13,7 @@ use Incoviba\Model;
|
||||
class Terreno extends Ideal\Service
|
||||
{
|
||||
public function __construct(LoggerInterface $logger, protected Repository\Proyecto $proyectoRepository,
|
||||
protected Service\Contabilidad\Nubox $nuboxService)
|
||||
protected Service\Contabilidad\Nubox $nuboxService, protected Service\IPC $ipcService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
@ -23,14 +24,45 @@ class Terreno extends Ideal\Service
|
||||
try {
|
||||
$proyecto = $this->proyectoRepository->fetchById($proyecto_id);
|
||||
$today = new DateTimeImmutable();
|
||||
$lastDecember = (new DateTimeImmutable($today->modify('-1 year')->format('Y-12-31')));
|
||||
// 1110-02
|
||||
$movimientos = $this->nuboxService->getSaldoCuenta($proyecto->inmobiliaria()->rut, '1110-02', $lastDecember, true);
|
||||
error_log(var_export($movimientos, true).PHP_EOL,3,'/logs/debug');
|
||||
$lastDecember = (new DateTimeImmutable($today->format('Y-01-01')));
|
||||
if ($proyecto->terreno->fecha >= $lastDecember) {
|
||||
return $proyecto->terreno;
|
||||
}
|
||||
try {
|
||||
return $this->getValorContable($proyecto, $lastDecember);
|
||||
} catch (Implement\Exception\EmptyResponse) {}
|
||||
if ($proyecto->terreno->fecha->getTimestamp() > 0) {
|
||||
return $this->getValorReajustado($proyecto, $lastDecember);
|
||||
}
|
||||
$terreno = $proyecto->terreno;
|
||||
} catch (Implement\Exception\EmptyResult) {}
|
||||
return $terreno;
|
||||
}
|
||||
|
||||
protected function getValorContable(Model\Proyecto $proyecto, DateTimeImmutable $lastDecember): Model\Proyecto\Terreno
|
||||
{
|
||||
$cuentaNubox = $this->nuboxService->getCuenta($proyecto->inmobiliaria()->rut, 'Terrenos');
|
||||
$movimientos = $this->nuboxService->getMesCuenta($proyecto->inmobiliaria()->rut, $cuentaNubox, $lastDecember);
|
||||
if (count($movimientos) === 0 or $movimientos[0]['Saldo'] === 0.0) {
|
||||
throw new Implement\Exception\EmptyResponse("No hay movimientos para este proyecto para la fecha {$lastDecember->format('d-m-Y')}");
|
||||
}
|
||||
$data = [
|
||||
'fecha' => (new DateTimeImmutable($movimientos[0]['FechaMovimiento']))->format('Y-m-d'),
|
||||
'valor' => $movimientos[0]['Saldo'],
|
||||
];
|
||||
$proyecto = $this->proyectoRepository->editTerreno($proyecto, $data);
|
||||
return $proyecto->terreno;
|
||||
}
|
||||
|
||||
protected function getValorReajustado(Model\Proyecto $proyecto): ?Model\Proyecto\Terreno
|
||||
{
|
||||
$novPrevTerreno = new DateTimeImmutable($proyecto->terreno->fecha->format('m') < 12 ? $proyecto->terreno->fecha->sub(new DateInterval('P1Y'))->format('Y-11-1') : $proyecto->terreno->fecha->format('Y-11-1'));
|
||||
$lastDecember = new DateTimeImmutable((new DateTimeImmutable())->sub(new DateInterval('P1Y'))->format('Y-12-31'));
|
||||
$novLast = new DateTimeImmutable($lastDecember->format('Y-11-1'));
|
||||
$ipc = $this->ipcService->get($novPrevTerreno, $novLast);
|
||||
$terreno = $proyecto->terreno;
|
||||
$terreno->fecha = $lastDecember;
|
||||
$terreno->valor *= (1 + $ipc);
|
||||
return $terreno;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user