1144 lines
57 KiB
PHP
1144 lines
57 KiB
PHP
@extends('layout.base')
|
|
|
|
@section('page_content')
|
|
<div class="ui container">
|
|
<h2 class="ui header">
|
|
Facturación -
|
|
<a href="{{$urls->base}}/proyecto/{{$venta->proyecto()->id}}">
|
|
{{$venta->proyecto()->descripcion}}<span class="ui tiny text"><sub><i
|
|
class="search icon"></i></sub></span>
|
|
</a>
|
|
-
|
|
<a href="{{$urls->base}}/venta/{{$venta->id}}">
|
|
{{$venta->propiedad()->summary()}}
|
|
</a>
|
|
</h2>
|
|
<span id="venta"></span>
|
|
<div id="facturas"></div>
|
|
</div>
|
|
@endsection
|
|
|
|
@include('layout.body.scripts.luxon')
|
|
|
|
@push('page_scripts')
|
|
<script>
|
|
class BaseObject {
|
|
props
|
|
constructor(props) {
|
|
this.props = props
|
|
}
|
|
}
|
|
class Propietario extends BaseObject {
|
|
update() {
|
|
return {
|
|
proporcion: (valor) => {
|
|
this.props.proporcion = valor
|
|
facturas.draw().facturas()
|
|
},
|
|
rut: rut => {
|
|
this.props.rut = rut
|
|
facturas.draw().facturas()
|
|
},
|
|
nombre: nombre => {
|
|
this.props.nombre = nombre
|
|
facturas.draw().facturas()
|
|
},
|
|
direccion: direccion => {
|
|
this.props.direccion = direccion
|
|
facturas.draw().facturas()
|
|
},
|
|
comuna: comuna => {
|
|
this.props.comuna = comuna
|
|
facturas.draw().facturas()
|
|
},
|
|
fecha: fecha => {
|
|
this.props.fecha = fecha
|
|
this.update().uf(fecha).then(() => {
|
|
facturas.draw().facturas()
|
|
})
|
|
},
|
|
uf: fecha => {
|
|
const url = '{{$urls->api}}/money/uf'
|
|
const method = 'post'
|
|
const body = new FormData()
|
|
body.set('fecha', [fecha.getFullYear(), fecha.getMonth()+1, fecha.getDate()].join('-'))
|
|
return fetchAPI(url, {method, body}).then(response => {
|
|
if (!response) {
|
|
return
|
|
}
|
|
return response.json().then(json => {
|
|
if (typeof json.uf === 'undefined') {
|
|
return
|
|
}
|
|
this.props.uf = parseFloat(json.uf)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
watch() {
|
|
return {
|
|
propietario: () => {
|
|
this.watch().proporcion()
|
|
this.watch().rut()
|
|
this.watch().nombre()
|
|
this.watch().direccion()
|
|
this.watch().comuna()
|
|
this.watch().fecha()
|
|
},
|
|
proporcion: () => {
|
|
document.getElementById('proporcion'+this.props.index).addEventListener('input', inputEvent => {
|
|
const newValue = inputEvent.currentTarget.value / 100
|
|
if (newValue === this.props.proporcion) {
|
|
return
|
|
}
|
|
const saldo = facturas.venta.props.propietarios.reduce((sum, propietario) => sum + propietario.props.proporcion, 0) - this.props.proporcion
|
|
if (saldo + newValue > 1) {
|
|
return
|
|
}
|
|
this.update().proporcion(newValue)
|
|
})
|
|
},
|
|
rut: () => {},
|
|
nombre: () => {},
|
|
direccion: () => {},
|
|
comuna: () => {},
|
|
fecha: () => {
|
|
const cdo = structuredClone(calendar_date_options)
|
|
cdo['initialDate'] = this.props.fecha
|
|
cdo['onChange'] = (date, text, mode) => {
|
|
this.update().fecha(date)
|
|
}
|
|
$('#fecha_factura'+this.props.index).calendar(cdo)
|
|
}
|
|
}
|
|
}
|
|
draw() {
|
|
return {
|
|
propietario: () => {
|
|
const output = ['<div class="fields" data-index="'+this.props.index+'">']
|
|
output.push(this.draw().proporcion())
|
|
output.push(this.draw().rut())
|
|
output.push(this.draw().nombre())
|
|
output.push(this.draw().fecha())
|
|
output.push('</div>')
|
|
output.push('<div class="fields" data-index="'+this.props.index+'">')
|
|
output.push('<div class="three wide field"></div>')
|
|
output.push(this.draw().direccion())
|
|
output.push(this.draw().comuna())
|
|
output.push('</div>')
|
|
return output.join("\n")
|
|
},
|
|
proporcion: () => {
|
|
return [
|
|
'<div class="two wide field">',
|
|
'<label for="proporcion'+this.props.index+'">Proporción Factura</label>',
|
|
'<div class="ui right labeled input">',
|
|
'<input type="number" name="proporcion'+this.props.index+'" id="proporcion'+this.props.index+'" value="'+(this.props.proporcion*100)+'" max="100" min="0" />',
|
|
'<div class="ui basic icon label">',
|
|
'<i class="percent icon"></i>',
|
|
'</div>',
|
|
'</div>',
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
rut: () => {
|
|
return [
|
|
'<div class="three wide field">',
|
|
'<label for="rut'+this.props.index+'">RUT</label>',
|
|
'<div class="ui input">',
|
|
'<input type="text" name="rut'+this.props.index+'" id="rut'+this.props.index+'" value="'+this.props.rut.toUpperCase()+'" />',
|
|
'</div>',
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
nombre: () => {
|
|
return [
|
|
'<div class="six wide field">',
|
|
'<label for="propietario'+this.props.index+'">Propietario</label>',
|
|
'<div class="ui input">',
|
|
'<input type="text" name="propietario'+this.props.index+'" id="propietario'+this.props.index+'" value="'+this.props.nombre+'" />',
|
|
'</div>',
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
direccion: () => {
|
|
return [
|
|
'<div class="six wide field">',
|
|
'<label for="direccion_propietario'+this.props.index+'">Dirección</label>',
|
|
'<div class="ui input">',
|
|
'<input type="text" name="direccion_propietario'+this.props.index+'" id="direccion_propietario'+this.props.index+'" value="'+this.props.direccion+'" />',
|
|
'</div>',
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
comuna: () => {
|
|
return [
|
|
'<div class="two wide field">',
|
|
'<label for="comuna_propietario'+this.props.index+'">Comuna</label>',
|
|
'<input type="text" name="comuna_propietario'+this.props.index+'" id="comuna_propietario'+this.props.index+'" value="'+this.props.comuna+'" />',
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
fecha: () => {
|
|
return [
|
|
'<div class="three wide field">' +
|
|
'<label for="fecha_factura'+this.props.index+'">Fecha Factura</label>'+
|
|
'<div class="ui calendar" id="fecha_factura'+this.props.index+'">' +
|
|
'<div class="ui right icon input">' +
|
|
'<input type="text" name="fecha_factura'+this.props.index+'" />' +
|
|
'<i class="calendar icon"></i>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'</div>'
|
|
].join("\n")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class Unidad extends BaseObject {
|
|
update() {
|
|
return {
|
|
precio: newValue => {
|
|
const url = '{{$urls->api}}/ventas/propiedades/unidad/' + this.props.propiedad_unidad_id + '/edit'
|
|
const method = 'post'
|
|
const body = new FormData()
|
|
body.set('valor', newValue)
|
|
return fetchAPI(url, {method, body}).then(response => {
|
|
if (!response) {
|
|
return
|
|
}
|
|
return response.json().then(json => {
|
|
if (!json.edited) {
|
|
return
|
|
}
|
|
this.props.valor = parseFloat(json.input.valor)
|
|
//document.getElementById('precio' + this.props.propiedad_unidad_id).value = json.input.valor
|
|
})
|
|
})
|
|
},
|
|
prorrateo: newValue => {
|
|
const url = '{{$urls->api}}/ventas/unidad/' + this.props.id + '/prorrateo'
|
|
const method = 'post'
|
|
const body = new FormData()
|
|
body.set('prorrateo', newValue)
|
|
return fetchAPI(url, {method, body}).then(response => {
|
|
if (!response) {
|
|
return
|
|
}
|
|
return response.json().then(json => {
|
|
if (!json.edited) {
|
|
return
|
|
}
|
|
this.props.prorrateo = json.input.prorrateo
|
|
document.getElementById('prorrateo'+this.props.id).parentElement.parentElement.innerHTML = ''
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
watch() {
|
|
return {
|
|
unidad: () => {
|
|
this.watch().precio()
|
|
this.watch().prorrateo()
|
|
},
|
|
precio: () => {
|
|
document.getElementById('precio'+this.props.propiedad_unidad_id).addEventListener('change', changeEvent => {
|
|
const newValue = changeEvent.currentTarget.value
|
|
if (newValue === this.props.valor) {
|
|
return
|
|
}
|
|
this.update().precio(newValue).then(() => {
|
|
facturas.venta.update().totalUnidades()
|
|
facturas.draw().facturas()
|
|
})
|
|
})
|
|
},
|
|
prorrateo: () => {
|
|
const input = document.getElementById('prorrateo'+this.props.id)
|
|
if (input === null) {
|
|
return
|
|
}
|
|
input.addEventListener('change', changeEvent => {
|
|
const newValue = changeEvent.currentTarget.value
|
|
if (newValue === this.props.prorrateo) {
|
|
return
|
|
}
|
|
this.update().prorrateo(newValue)
|
|
facturas.draw().facturas()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
draw() {
|
|
return {
|
|
precio: () => {
|
|
return [
|
|
'<div class="three wide field">',
|
|
'<label for="precio'+this.props.propiedad_unidad_id+'">Precio<br /> '+this.props.tipo+' '+this.props.descripcion+'</label>',
|
|
'<div class="ui left labeled input" id="input'+this.props.propiedad_unidad_id+'">',
|
|
'<div class="ui basic label">UF</div>',
|
|
'<input class="price" type="text" name="precio'+this.props.propiedad_unidad_id+'" id="precio'+this.props.propiedad_unidad_id+'" data-id="'+this.props.propiedad_unidad_id+'" value="'+this.props.valor+'" />',
|
|
'</div>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
prorrateo: () => {
|
|
const output = []
|
|
output.push('<div class="three wide field">')
|
|
if (this.props.prorrateo === 0) {
|
|
output.push(...[
|
|
'<label for="prorrateo'+this.props.id+'">Prorrateo<br /> '+this.props.tipo+' '+this.props.descripcion+'</label>',
|
|
'<div class="ui right labeled input">',
|
|
'<input class="prorrateo" type="text" id="prorrateo'+this.props.id+'" value="'+this.props.prorrateo+'" />',
|
|
'<div class="ui basic label">%</div>',
|
|
'</div>'
|
|
])
|
|
}
|
|
output.push('</div>')
|
|
return output.join("\n")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class Venta extends BaseObject {
|
|
update() {
|
|
return {
|
|
propietarios: count => {
|
|
const diff = count - this.props.propietarios.length
|
|
if (diff > 0) {
|
|
const m = this.props.propietarios.length / count
|
|
let p = 1
|
|
this.props.propietarios.forEach((propietario, index) => {
|
|
this.props.propietarios[index].props.proporcion = parseFloat((this.props.propietarios[index].props.proporcion * m * 100).toFixed(0)) / 100
|
|
p -= this.props.propietarios[index].props.proporcion
|
|
})
|
|
p /= diff
|
|
const watch = []
|
|
for (let i = 0; i < diff; i ++) {
|
|
const propietario = this.add().propietario({
|
|
rut: '',
|
|
nombre: '',
|
|
proporcion: (p*100).toFixed(0)/100,
|
|
direccion: '',
|
|
comuna: '',
|
|
fecha: this.props.estado.fecha,
|
|
uf: this.props.uf
|
|
})
|
|
watch.push(propietario)
|
|
}
|
|
document.getElementById('propietarios').innerHTML = this.draw().propietarios()
|
|
this.watch().propietarios()
|
|
return
|
|
}
|
|
for (let i = this.props.propietarios.length - 1; i >= count; i --) {
|
|
this.remove().propietario(i)
|
|
}
|
|
},
|
|
fecha: date => {
|
|
this.props.fecha = date
|
|
const url = '{{$urls->api}}/money/uf'
|
|
const method = 'post'
|
|
const body = new FormData()
|
|
body.set('fecha', [date.getFullYear(), date.getMonth()+1, date.getDate()].join('-'))
|
|
return fetchAPI(url, {method, body}).then(response => {
|
|
if (!response) {
|
|
return
|
|
}
|
|
return response.json().then(json => {
|
|
if (typeof json.uf === 'undefined') {
|
|
return
|
|
}
|
|
this.props.uf = json.uf
|
|
})
|
|
})
|
|
},
|
|
totalUnidades: () => {
|
|
const unidades = this.props.unidades.reduce((sum, unidad) => sum + unidad.props.valor, 0)
|
|
const diff = parseFloat((this.props.valor - unidades).toFixed(4))
|
|
const $total = $('#total_unidades')
|
|
if (diff === 0) {
|
|
$total.html('')
|
|
return
|
|
}
|
|
$total.html('<div class="ui error compact message">' +
|
|
'<div class="header">' +
|
|
'<i class="exclamation triangle icon"></i>' +
|
|
'Diferencia Promesa - Precio Unidades</div>' +
|
|
'UF ' + facturas.formatters.ufs.format(diff) +
|
|
'</div>')
|
|
$total.find('.ui.message').css('display', 'inline-block')
|
|
},
|
|
terreno: newValue => {
|
|
const date = this.props.last.dicember
|
|
|
|
const url = '{{$urls->api}}/proyecto/{{$venta->proyecto()->id}}/terreno/edit'
|
|
const method = 'post'
|
|
const body = new FormData()
|
|
body.set('valor', newValue)
|
|
body.set('fecha', [date.getFullYear(), date.getMonth()+1, date.getDate()].join('-'))
|
|
return fetchAPI(url, {method, body}).then(response => {
|
|
if (!response) {
|
|
return
|
|
}
|
|
return response.json().then(json => {
|
|
if (!json.edited) {
|
|
return
|
|
}
|
|
this.props.terreno.fecha = this.props.last.dicember
|
|
this.props.terreno.valor = parseInt(json.input.valor)
|
|
|
|
document.getElementById('terreno').parentElement.parentElement.remove()
|
|
|
|
this.update().ipc()
|
|
})
|
|
})
|
|
},
|
|
ipc: () => {
|
|
const mesAnterior = DateTime.fromISO(this.props.estado.fecha.toISOString()).minus({months: 1})
|
|
const url = '{{$urls->api}}/money/ipc'
|
|
const method = 'post'
|
|
const body = new FormData()
|
|
body.set('start', [this.props.last.november.getFullYear(), this.props.last.november.getMonth()+1, this.props.last.november.getDate()].join('-'))
|
|
body.set('end', [mesAnterior.year, mesAnterior.month, mesAnterior.day].join('-'))
|
|
return fetchAPI(url, {method, body}).then(response => {
|
|
if (!response) {
|
|
return
|
|
}
|
|
return response.json().then(json => {
|
|
this.props.terreno.reajustado *= (1 + parseFloat(json.ipc))
|
|
})
|
|
})
|
|
},
|
|
form: () => {
|
|
// fecha
|
|
// unidades
|
|
// valor total unidades
|
|
// terreno
|
|
// propietarios
|
|
// ipc no disponible
|
|
}
|
|
}
|
|
}
|
|
watch() {
|
|
return {
|
|
venta: () => {
|
|
this.watch().fecha()
|
|
this.watch().unidades()
|
|
this.watch().terreno()
|
|
this.watch().cantidad()
|
|
this.watch().propietarios()
|
|
this.update().totalUnidades()
|
|
},
|
|
fecha: () => {
|
|
const cdo = structuredClone(calendar_date_options)
|
|
cdo['initialDate'] = this.props.estado.fecha
|
|
cdo['onChange'] = (date, text, mode) => {
|
|
this.update().fecha(date)
|
|
}
|
|
$('#fecha_uf').calendar(cdo)
|
|
},
|
|
unidades: () => {
|
|
this.props.unidades.forEach(unidad => {
|
|
unidad.watch().unidad()
|
|
})
|
|
},
|
|
terreno: () => {
|
|
const terreno = document.getElementById('terreno')
|
|
if (typeof terreno === 'undefined' || terreno === null) {
|
|
return
|
|
}
|
|
terreno.addEventListener('change', changeEvent => {
|
|
const newValue = changeEvent.currentTarget.value
|
|
if (newValue === this.props.terreno.valor) {
|
|
return
|
|
}
|
|
this.update().terreno(newValue)
|
|
})
|
|
},
|
|
cantidad: () => {
|
|
document.getElementById('cantidad_propietarios').addEventListener('change', changeEvent => {
|
|
const count = changeEvent.currentTarget.value
|
|
const diff = count - this.props.propietarios.length
|
|
if (diff === 0) {
|
|
return
|
|
}
|
|
this.update().propietarios(count)
|
|
facturas.draw().facturas()
|
|
})
|
|
},
|
|
propietarios: () => {
|
|
this.props.propietarios.forEach(propietario => {
|
|
propietario.watch().propietario()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
draw() {
|
|
return {
|
|
venta: ufFormatter => {
|
|
return [
|
|
this.draw().value(ufFormatter),
|
|
this.draw().form(),
|
|
this.draw().ipc()
|
|
].join("\n")
|
|
},
|
|
value: ufFormatter => {
|
|
return [
|
|
'<div class="ui grid">',
|
|
'<div class="three wide column">',
|
|
'<div class="ui very segment">',
|
|
'Valor Venta: UF ' + ufFormatter.format(this.props.valor),
|
|
'</div>',
|
|
'</div>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
form: () => {
|
|
const output = []
|
|
output.push('<form id="venta_form" class="ui form">')
|
|
output.push(this.draw().fecha())
|
|
output.push(this.draw().precios())
|
|
output.push(this.draw().prorrateos())
|
|
output.push('<div class="ui very basic segment" id="total_unidades"></div>')
|
|
output.push(this.draw().terreno())
|
|
output.push(...[
|
|
'<div class="two wide field">',
|
|
'<label for="propietarios">Propietarios</label>',
|
|
'<input type="number" name="cantidad_propietarios" id="cantidad_propietarios" min="1" value="1" />',
|
|
'</div>',
|
|
])
|
|
output.push('<div id="propietarios">')
|
|
output.push(this.draw().propietarios())
|
|
output.push('</div>')
|
|
output.push('</form>')
|
|
return output.join("\n")
|
|
},
|
|
fecha: () => {
|
|
return [
|
|
'<div class="three wide field">',
|
|
'<label for="fecha_uf">Fecha UF</label>',
|
|
'<div class="ui calendar" id="fecha_uf">',
|
|
'<div class="ui right icon input">',
|
|
'<input type="text" name="fecha_uf" />',
|
|
'<i class="calendar icon"></i>',
|
|
'</div>',
|
|
'</div>',
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
precios: () => {
|
|
const output = []
|
|
output.push('<div class="fields">')
|
|
this.props.unidades.forEach(unidad => {
|
|
output.push(unidad.draw().precio())
|
|
})
|
|
output.push('</div>')
|
|
return output.join("\n")
|
|
},
|
|
prorrateos: () => {
|
|
const output = []
|
|
output.push('<div class="fields">')
|
|
this.props.unidades.forEach(unidad => {
|
|
output.push(unidad.draw().prorrateo())
|
|
})
|
|
output.push('</div>')
|
|
return output.join("\n")
|
|
},
|
|
terreno: () => {
|
|
const output = []
|
|
if (typeof this.props.terreno.fecha === 'undefined' || this.props.terreno.fecha === null || this.props.terreno.fecha.getTime() < 0 || this.props.terreno.fecha < this.props.last.dicember) {
|
|
output.push(...[
|
|
'<div class="four wide field">',
|
|
'<label for="terreno">Valor Terreno al '+this.props.last.dicember.toString()+'</label>',
|
|
'<div class="ui left labeled input">',
|
|
'<div class="ui basic label">$</div>',
|
|
'<input type="number" id="terreno" />',
|
|
'</div>',
|
|
'</div>'
|
|
])
|
|
}
|
|
return output.join("\n")
|
|
},
|
|
propietarios: () => {
|
|
const output = []
|
|
this.props.propietarios.forEach(propietario => {
|
|
output.push(propietario.draw().propietario())
|
|
})
|
|
return output.join("\n")
|
|
},
|
|
ipc: () => {
|
|
if (!(this.props.fecha > this.props.terreno.fecha && this.props.terreno.valor === 0)) {
|
|
return ''
|
|
}
|
|
return [
|
|
'<div class="ui compact icon error message">',
|
|
'<i class="exclamation triangle icon"></i>',
|
|
'<div class="content">',
|
|
'IPC no disponible para este mes.',
|
|
'</div>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
facturas: formatters => {
|
|
const output = []
|
|
this.props.facturas.forEach((factura, index) => {
|
|
output.push(factura.draw().factura({
|
|
proyecto: this.props.proyecto,
|
|
propietario: this.props.propietarios[index],
|
|
unidades: this.props.unidades,
|
|
terreno: this.props.terreno,
|
|
venta: this.valores(this.props.propietarios[index].props.uf),
|
|
formatters
|
|
}))
|
|
})
|
|
return output.join("\n")
|
|
}
|
|
}
|
|
}
|
|
valores(uf) {
|
|
const total = this.props.valor * uf
|
|
const prorrateo = this.props.unidades.reduce((sum, unidad) => sum + unidad.props.prorrateo, 0)
|
|
const exento = this.props.terreno.reajustado * prorrateo
|
|
const bruto = total - exento
|
|
const neto = bruto / 1.19
|
|
const iva = neto * .19
|
|
const base = neto + exento
|
|
return {
|
|
base,
|
|
exento,
|
|
neto,
|
|
iva,
|
|
bruto,
|
|
descuento: prorrateo,
|
|
total,
|
|
totalUF: total / uf,
|
|
}
|
|
}
|
|
add() {
|
|
return {
|
|
propietario: ({rut, nombre, proporcion, direccion, comuna, fecha, uf}) => {
|
|
const index = this.props.propietarios.length + 1
|
|
const propietario = new Propietario({index, rut, nombre, proporcion, direccion, comuna, fecha, uf})
|
|
this.props.propietarios.push(propietario)
|
|
this.add().factura(propietario)
|
|
return propietario
|
|
},
|
|
factura: propietario => {
|
|
const factura = new Factura({index: propietario.props.index})
|
|
this.props.facturas.push(factura)
|
|
}
|
|
}
|
|
}
|
|
remove() {
|
|
return {
|
|
propietario: index => {
|
|
if (index <= 0) {
|
|
return
|
|
}
|
|
const P1 = this.props.propietarios.reduce((sum, propietario) => sum + propietario.props.proporcion, 0)
|
|
const propietario = this.props.propietarios.splice(index, 1)[0]
|
|
const P2 = this.props.propietarios.reduce((sum, propietario) => sum + propietario.props.proporcion, 0)
|
|
document.querySelectorAll("[data-index='"+propietario.props.index+"']").forEach(field => {
|
|
field.remove()
|
|
})
|
|
this.remove().factura(index)
|
|
},
|
|
factura: index => {
|
|
this.props.facturas.splice(index, 1)
|
|
document.getElementById('facturas').querySelectorAll("[data-index='"+(index+1)+"']").forEach(factura => {
|
|
factura.remove()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class Factura extends BaseObject {
|
|
draw() {
|
|
return {
|
|
divider: index => {
|
|
return '<div class="ui divider" data-index="'+index+'"></div>'
|
|
},
|
|
factura: ({proyecto, propietario, unidades, terreno, venta, formatters = {date, pesos, ufs, percent}}) => {
|
|
return [
|
|
this.draw().divider(propietario.props.index),
|
|
'<div class="factura" data-index="'+propietario.props.index+'">',
|
|
'<div class="ui compact grid">',
|
|
this.draw().cabecera(proyecto),
|
|
this.draw().propietario({propietario, formatters}),
|
|
this.draw().table({venta, unidades, propietario, terreno, formatters}),
|
|
this.draw().totales({propietario, venta, formatters}),
|
|
'</div>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
cabecera: proyecto => {
|
|
return [
|
|
'<div class="two columns row">',
|
|
this.draw().inmobiliaria(proyecto),
|
|
this.draw().rut(proyecto.inmobiliaria),
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
inmobiliaria: proyecto => {
|
|
return [
|
|
'<div class="twelve wide column">',
|
|
'<strong>'+proyecto.inmobiliaria.nombre.toUpperCase()+'</strong><br/>',
|
|
'GIRO: <br/>',
|
|
'Dirección: '+proyecto.direccion,
|
|
'</div>',
|
|
].join("\n")
|
|
},
|
|
rut: inmobiliaria => {
|
|
return [
|
|
'<div class="four wide column">',
|
|
'<div class="ui center aligned red segment">',
|
|
'<strong>',
|
|
'RUT:'+inmobiliaria.rut.toUpperCase()+'<br/>',
|
|
'FACTURA ELECTRÓNICA<br/>',
|
|
'N° #',
|
|
'</strong>',
|
|
'</div>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
propietario: ({propietario, formatters}) => {
|
|
return [
|
|
'<div class="row">',
|
|
'<table class="ui table">',
|
|
'<tr>'+
|
|
'<td class="grey"><strong>Señor(es)</strong></td>',
|
|
'<td>'+propietario.props.nombre+'</td>',
|
|
'<td class="grey"><strong>RUT</strong></td>',
|
|
'<td>'+propietario.props.rut.toUpperCase()+'</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(propietario.props.fecha)+'</td>',
|
|
'</tr>',
|
|
'<tr>',
|
|
'<td class="grey"><strong>Dirección</strong></td>',
|
|
'<td>'+propietario.props.direccion+'</td>',
|
|
'<td class="grey"><strong>Comuna</strong></td>',
|
|
'<td>'+propietario.props.comuna.toUpperCase()+'</td>',
|
|
'</tr>',
|
|
'</table>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
table: ({venta, unidades, propietario, terreno, formatters}) => {
|
|
return [
|
|
'<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>',
|
|
'<tbody>',
|
|
this.draw().unidades({venta, unidades, propietario, terreno, formatters}),
|
|
'</tbody>',
|
|
'<tfoot>',
|
|
'<tr>',
|
|
'<td colspan="6">',
|
|
'<br />',
|
|
'<br />',
|
|
'<br />',
|
|
'<br />',
|
|
'</td>',
|
|
'</tr>',
|
|
'</tfoot>',
|
|
'</table>',
|
|
'</div>'
|
|
].join("\n")
|
|
},
|
|
unidades: ({venta, unidades, propietario, terreno, formatters}) => {
|
|
const unidadesData = []
|
|
let no = 1
|
|
const classes = [
|
|
'',
|
|
'',
|
|
'center aligned',
|
|
'right aligned',
|
|
'center aligned',
|
|
'right aligned'
|
|
]
|
|
unidades.forEach(unidad => {
|
|
unidadesData.push(this.draw().unidad({
|
|
unidad,
|
|
propietario,
|
|
terreno,
|
|
no: no++,
|
|
classes,
|
|
formatters
|
|
}))
|
|
})
|
|
unidadesData.push(this.draw().resumen({
|
|
propietario,
|
|
venta,
|
|
no,
|
|
classes,
|
|
formatters
|
|
}))
|
|
return unidadesData.join("\n")
|
|
},
|
|
unidad: ({unidad, propietario, terreno, no, classes, formatters}) => {
|
|
const descuento = terreno.reajustado * unidad.props.prorrateo
|
|
const bruto = unidad.props.valor * propietario.props.uf - descuento
|
|
const neto = bruto / 1.19
|
|
const data = [
|
|
no,
|
|
unidad.props.tipo + ' ' + unidad.props.descripcion + ' (UF ' + formatters.ufs.format(unidad.props.valor * propietario.props.proporcion) + ')',
|
|
'1 UNID',
|
|
formatters.pesos.format(neto * propietario.props.proporcion),
|
|
'AF',
|
|
formatters.pesos.format(neto * propietario.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>')
|
|
return row.join('')
|
|
},
|
|
resumen: ({propietario, venta, no, classes, formatters}) => {
|
|
const emptyTerreno = '<div class="ui tiny red horizontal circular label">0</div>'
|
|
const data = [
|
|
no,
|
|
'Valor con Terreno: $' + formatters.pesos.format(venta.base * propietario.props.proporcion) + ' - Menos valor terreno: $' + ((venta.exento > 0) ? formatters.pesos.format(-venta.exento * propietario.props.proporcion) : emptyTerreno) + '<br />' +
|
|
'Base imponible (Neto): $' + formatters.pesos.format(venta.neto * propietario.props.proporcion) + '<br />' +
|
|
'IVA: $' + formatters.pesos.format(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 />' +
|
|
'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 />' +
|
|
'UF (' + formatters.date.format(propietario.props.fecha) + '): $' + formatters.ufs.format(propietario.props.uf),
|
|
'1 UNID',
|
|
formatters.pesos.format(venta.exento * propietario.props.proporcion),
|
|
'EX',
|
|
formatters.pesos.format(venta.exento * propietario.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(''))
|
|
})
|
|
return row.join('')
|
|
},
|
|
totales: ({propietario, venta, formatters}) => {
|
|
return [
|
|
'<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.neto * propietario.props.proporcion)+'</td>',
|
|
'</tr>',
|
|
'<tr>',
|
|
'<td class="grey">Monto Exento</td>',
|
|
'<td class="right aligned" id="exento">'+formatters.pesos.format(venta.exento * propietario.props.proporcion)+'</td>',
|
|
'</tr>',
|
|
'<tr>',
|
|
'<td class="grey">19% IVA</td>',
|
|
'<td class="right aligned" id="iva">'+formatters.pesos.format(venta.iva * propietario.props.proporcion)+'</td>',
|
|
'</tr>',
|
|
'<tr>',
|
|
'<td class="grey">Monto Total</td>',
|
|
'<td class="right aligned"><strong id="total">'+formatters.pesos.format(venta.bruto * propietario.props.proporcion)+'</strong></td>',
|
|
'</tr>',
|
|
'</tbody>',
|
|
'</table>',
|
|
'</div>',
|
|
'</div>'
|
|
].join("\n")
|
|
}
|
|
}
|
|
const output = [
|
|
'<div class="ui divider" data-index="'+propietario.props.index+'"></div>'+
|
|
'<div class="factura" data-index="'+propietario.props.index+'">'+
|
|
'<div class="ui compact grid">'+
|
|
'<div class="two columns row">'+
|
|
'<div class="twelve wide column">'+
|
|
'<strong>'+proyecto.inmobiliaria.nombre.toUpperCase()+'</strong><br/>'+
|
|
'GIRO: <br/>'+
|
|
'Dirección:'+proyecto.direccion+
|
|
'</div>'+
|
|
'<div class="four wide column">'+
|
|
'<div class="ui center aligned red segment">'+
|
|
'<strong>'+
|
|
'RUT:'+proyecto.inmobiliaria.rut.toUpperCase()+'<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.props.nombre+'</td>'+
|
|
'<td class="grey"><strong>RUT</strong></td>'+
|
|
'<td>'+propietario.props.rut.toUpperCase()+'</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(propietario.props.fecha)+'</td>'+
|
|
'</tr>'+
|
|
'<tr>'+
|
|
'<td class="grey"><strong>Dirección</strong></td>'+
|
|
'<td>'+propietario.props.direccion+'</td>'+
|
|
'<td class="grey"><strong>Comuna</strong></td>'+
|
|
'<td>'+propietario.props.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'
|
|
]
|
|
const uf_mult = this.props.uf / uf
|
|
unidades.forEach(unidad => {
|
|
const descuento = parseFloat(proyecto.terreno) * parseFloat(unidad.prorrateo) * uf_mult
|
|
const bruto = parseFloat(unidad.base) * uf_mult - descuento
|
|
const neto = bruto / 1.19
|
|
const data = [
|
|
c ++,
|
|
unidad.descripcion + ' (UF ' + formatters.ufs.format(unidad.precio * this.props.proporcion * uf_mult) + ')',
|
|
'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 * uf_mult + venta.terreno * uf_mult) * this.props.proporcion) + ' - Menos valor terreno ' + ((venta.terreno > 0) ? formatters.pesos.format(-venta.terreno * uf_mult * this.props.proporcion) : emptyTerreno) + '<br />' +
|
|
'Base imponible ' + formatters.pesos.format(venta.base * uf_mult * this.props.proporcion) + '<br />' +
|
|
'IVA ' + formatters.pesos.format(venta.iva * uf_mult * this.props.proporcion) + '<br />' +
|
|
'SUBTOTAL ' + formatters.pesos.format(venta.subtotal * uf_mult * this.props.proporcion) + '<br />' +
|
|
'Mas valor terreno ' + ((venta.terreno > 0) ? formatters.pesos.format(venta.terreno * uf_mult * this.props.proporcion) : emptyTerreno) + '<br />' +
|
|
'TOTAL ' + formatters.pesos.format(venta.total * uf_mult * 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 * uf_mult * this.props.proporcion),
|
|
'EX',
|
|
formatters.pesos.format(venta.terreno * uf_mult * 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.neto * uf_mult * this.props.proporcion)+'</td>'+
|
|
'</tr>'+
|
|
'<tr>'+
|
|
'<td class="grey">Monto Exento</td>'+
|
|
'<td class="right aligned" id="exento">'+formatters.pesos.format(venta.exento * uf_mult * this.props.proporcion)+'</td>'+
|
|
'</tr>'+
|
|
'<tr>'+
|
|
'<td class="grey">19% IVA</td>'+
|
|
'<td class="right aligned" id="iva">'+formatters.pesos.format(venta.iva * uf_mult * this.props.proporcion)+'</td>'+
|
|
'</tr>'+
|
|
'<tr>'+
|
|
'<td class="grey">Monto Total</td>'+
|
|
'<td class="right aligned"><strong id="total">'+formatters.pesos.format(venta.total * uf_mult * this.props.proporcion)+'</strong></td>'+
|
|
'</tr>'+
|
|
'</tbody>'+
|
|
'</table>'+
|
|
'</div>'+
|
|
'</div>'+
|
|
'</div>'+
|
|
'</div>'
|
|
)
|
|
return output.join('')
|
|
}
|
|
}
|
|
|
|
const facturas = {
|
|
ids: {},
|
|
venta: null,
|
|
formatters: {
|
|
date: new Intl.DateTimeFormat('es-CL', {year: 'numeric', month: '2-digit', day: '2-digit'}),
|
|
pesos: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 0, maximumFractionDigits: 0}),
|
|
ufs: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 2, maximumFractionDigits: 2}),
|
|
percent: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 2, maximumFractionDigits: 2})
|
|
},
|
|
draw() {
|
|
return {
|
|
venta: () => {
|
|
document.getElementById(this.ids.venta).innerHTML = this.venta.draw().venta(this.formatters.ufs)
|
|
this.venta.watch().venta()
|
|
},
|
|
facturas: () => {
|
|
document.getElementById(this.ids.facturas).innerHTML = this.venta.draw().facturas(this.formatters)
|
|
}
|
|
}
|
|
},
|
|
setup({ids}) {
|
|
this.ids = ids
|
|
this.venta = new Venta({
|
|
id: {{$venta->id}},
|
|
proyecto: {
|
|
id: {{$venta->proyecto()->id}},
|
|
inmobiliaria: {
|
|
rut: '{{$venta->proyecto()->inmobiliaria()->rut()}}',
|
|
nombre: '{{$venta->proyecto()->inmobiliaria()->nombreCompleto()}}'
|
|
},
|
|
direccion: '{{$venta->proyecto()->direccion()->simple()}}'
|
|
},
|
|
valor: {{$venta->valor}},
|
|
uf: {{$uf}},
|
|
estado: {
|
|
fecha: new Date('{{$venta->currentEstado()->fecha->add(new DateInterval('P1D'))->format('Y-m-d')}}')
|
|
},
|
|
terreno: {
|
|
fecha: new Date('{{$terreno->fecha->add(new DateInterval('P1D'))->format('Y-m-d')}}'),
|
|
valor: {{$terreno->valor}},
|
|
reajustado: {{$terreno->valor * (1+$ipc)}}
|
|
},
|
|
last: {
|
|
dicember: new Date('{{(new DateTimeImmutable())->sub(new DateInterval('P1Y'))->format('Y-11-31')}}'),
|
|
november: new Date('{{(new DateTimeImmutable())->sub(new DateInterval('P1Y'))->format('Y-11-30')}}')
|
|
},
|
|
unidades: [
|
|
@foreach ($venta->propiedad()->unidades as $unidad)
|
|
new Unidad({
|
|
id: {{$unidad->id}},
|
|
tipo: '{{ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion)}}',
|
|
descripcion: '{{$unidad->descripcion}}',
|
|
prorrateo: {{$unidad->prorrateo}},
|
|
propiedad_unidad_id: {{$unidad->pu_id}},
|
|
valor: {{($unidad->valor > 0) ? $unidad->valor : $unidad->precio($venta->currentEstado()->fecha)->valor}}
|
|
}),
|
|
@endforeach
|
|
],
|
|
propietarios: [],
|
|
facturas: []
|
|
})
|
|
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}}
|
|
})
|
|
this.draw().venta()
|
|
this.draw().facturas()
|
|
}
|
|
}
|
|
$(document).ready(() => {
|
|
facturas.setup({
|
|
ids: {
|
|
venta: 'venta',
|
|
facturas: 'facturas'
|
|
}
|
|
})
|
|
})
|
|
</script>
|
|
@endpush
|