2025-01-17 00:07:45 -03:00
< script >
class Factura {
props = {
id : 0 ,
venta : null ,
index : 0 ,
proporcion : 0 ,
2025-02-24 15:47:58 -03:00
terreno : {
fecha : null ,
valor : 0
},
2025-01-17 00:07:45 -03:00
emisor : {
rut : '' ,
nombre : '' ,
direccion : '' ,
comuna : ''
},
receptor : {
rut : '' ,
nombre : '' ,
direccion : '' ,
comuna : ''
},
fecha : null ,
unidades : {
unidad : null ,
descripcion : '' ,
precio : 0 ,
prorrateo : 0
},
detalle : {
base : 0 ,
terreno : 0 ,
neto : 0 ,
iva : 0 ,
bruto : 0 ,
descuento : 0 ,
total : 0
},
total : {
neto : 0 ,
exento : 0 ,
iva : 0 ,
total : 0
},
uf : {
fecha : null ,
valor : 0
}
}
constructor ( props ) {
this . props = props
}
get saved () {
return this . props . id > 0
}
2025-02-24 15:47:58 -03:00
get prorrateo () {
return this . props . unidades . reduce (( sum , unidad ) => sum + unidad . prorrateo , 0 )
}
2025-01-17 00:07:45 -03:00
draw () {
return {
divider : () => {
return '<div class="ui divider" data-index="' + this . props . index + '"></div>'
},
factura : ({ formatters = { date , pesos , ufs , percent }}) => {
return [
this . draw () . divider ( this . props . index ),
`<div class="factura" data-index="${this.props.index}">` ,
'<div class="ui compact grid">' ,
this . draw () . cabecera (),
this . draw () . propietario ({ formatters }),
this . draw () . table ({ formatters }),
this . draw () . totales ({ formatters }),
this . draw () . guardar (),
'</div>' ,
'</div>'
] . join ( " \n " )
},
cabecera : () => {
return [
'<div class="two columns row">' ,
this . draw () . inmobiliaria (),
this . draw () . rut (),
'</div>'
] . join ( " \n " )
},
inmobiliaria : () => {
return [
'<div class="twelve wide column">' ,
'<strong>' + this . props . emisor . nombre . toUpperCase () + '</strong><br/>' ,
'GIRO: <br/>' ,
`Dirección: ${this.props.emisor.direccion}, ${this.props.emisor.comuna}` ,
'</div>' ,
] . join ( " \n " )
},
rut : () => {
return [
'<div class="four wide column">' ,
2025-02-03 22:17:57 -03:00
'<div class="ui center aligned orange segment">' ,
2025-01-17 00:07:45 -03:00
'<strong>' ,
`RUT:${this.props.emisor.rut.toUpperCase()}<br/>` ,
'FACTURA ELECTRÓNICA<br/>' ,
2025-02-03 22:17:57 -03:00
`<span class="ui red text">N° ${this.props.venta.id}${this.props.index}</span>` ,
2025-01-17 00:07:45 -03:00
'</strong>' ,
'</div>' ,
'</div>'
] . join ( " \n " )
},
propietario : ({ formatters }) => {
return [
'<div class="row">' ,
'<table class="ui table">' ,
'<tr>' +
'<td class="grey"><strong>Señor(es)</strong></td>' ,
'<td>' + this . props . receptor . nombre + '</td>' ,
'<td class="grey"><strong>RUT</strong></td>' ,
'<td>' + this . props . receptor . 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 ( this . props . fecha ) + '</td>' ,
'</tr>' ,
'<tr>' ,
'<td class="grey"><strong>Dirección</strong></td>' ,
'<td>' + this . props . receptor . direccion + '</td>' ,
'<td class="grey"><strong>Comuna</strong></td>' ,
'<td>' + this . props . receptor . comuna . toUpperCase () + '</td>' ,
'</tr>' ,
'</table>' ,
'</div>'
] . join ( " \n " )
},
table : ({ formatters }) => {
return [
'<div class="row">' ,
2025-02-03 22:17:57 -03:00
'<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 ({ formatters }),
'</tbody>' ,
'<tfoot>' ,
'<tr>' ,
'<td colspan="6">' ,
'<br />' ,
'<br />' ,
'<br />' ,
'<br />' ,
'</td>' ,
'</tr>' ,
'</tfoot>' ,
'</table>' ,
2025-01-17 00:07:45 -03:00
'</div>'
] . join ( " \n " )
},
unidades : ({ formatters }) => {
const unidadesData = []
let no = 1
const classes = [
'' ,
'' ,
'center aligned' ,
'right aligned' ,
'center aligned' ,
'right aligned'
]
this . props . unidades . forEach ( unidad => {
unidadesData . push ( this . draw () . unidad ({
unidad ,
no : no ++ ,
classes ,
formatters
}))
})
unidadesData . push ( this . draw () . resumen ({
no ,
classes ,
formatters
}))
return unidadesData . join ( " \n " )
},
unidad : ({ unidad , no , classes , formatters }) => {
2025-02-24 15:47:58 -03:00
const descuento = this . props . terreno . valor * unidad . prorrateo * this . props . proporcion
2025-01-17 00:07:45 -03:00
const bruto = unidad . precio - descuento
const neto = bruto / 1.19
const data = [
no ,
unidad . descripcion ,
'1 UNID' ,
2025-01-17 16:55:34 -03:00
formatters . pesos . format ( neto ),
2025-01-17 00:07:45 -03:00
'AF' ,
2025-01-17 16:55:34 -03:00
formatters . pesos . format ( neto )
2025-01-17 00:07:45 -03:00
]
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 : ({ no , classes , formatters }) => {
const emptyTerreno = '<div class="ui tiny red horizontal circular label">0</div>'
const data = [
no ,
'Valor con Terreno: $' + formatters . pesos . format ( this . props . detalle . base ) + ' - Menos valor terreno: $' + (( this . props . detalle . terreno > 0 ) ? formatters . pesos . format ( - this . props . detalle . terreno ) : emptyTerreno ) + '<br />' +
'Base imponible (Neto): $' + formatters . pesos . format ( this . props . detalle . neto ) + '<br />' +
'IVA: $' + formatters . pesos . format ( this . props . detalle . iva ) + '<br />' +
'SUBTOTAL (Bruto): $' + formatters . pesos . format ( this . props . detalle . bruto ) + '<br />' +
'Mas valor terreno: $' + (( this . props . detalle . terreno > 0 ) ? formatters . pesos . format ( this . props . detalle . terreno ) : emptyTerreno ) + '<br />' +
'TOTAL (Escritura): $' + formatters . pesos . format ( this . props . detalle . total ) + '; ' + formatters . ufs . format ( this . props . venta . valor * this . props . proporcion ) + ' UF<br /><br />' +
'Descuento Terreno: ' + (( this . props . detalle . terreno > 0 ) ? formatters . percent . format ( this . props . detalle . descuento * 100 ) : emptyTerreno ) + '%<br /><br />' +
'UF (' + formatters . date . format ( this . props . uf . fecha ) + '): $' + formatters . ufs . format ( this . props . uf . valor ),
'1 UNID' ,
formatters . pesos . format ( this . props . detalle . terreno ),
'EX' ,
formatters . pesos . format ( this . props . detalle . terreno ),
]
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 : ({ formatters }) => {
2025-02-24 15:47:58 -03:00
let tooltips = {
neto : null ,
iva : null ,
total : null
}
if ( this . props . total . neto !== this . props . detalle . neto ) {
tooltips . neto = ` data-tooltip="No coinciden netos! Promesa: ${formatters.pesos.format(this.props.detalle.neto)} - Unidades: ${formatters.pesos.format(this.props.total.neto)}"`
}
if ( this . props . total . iva !== this . props . detalle . iva ) {
tooltips . iva = ` data-tooltip="No coinciden ivas! Promesa: ${formatters.pesos.format(this.props.detalle.iva)} - Unidades: ${formatters.pesos.format(this.props.total.iva)}"`
}
if ( this . props . total . total !== this . props . detalle . total ) {
tooltips . total = ` data-tooltip="No coinciden totales! Promesa: ${formatters.pesos.format(this.props.detalle.total)} - Unidades: ${formatters.pesos.format(this.props.total.total)}"`
}
2025-01-17 00:07:45 -03:00
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>' ,
2025-02-24 15:47:58 -03:00
`<td class="right aligned${this.props.total.neto !== this.props.detalle.neto ? ' red' : ''}"${this.props.total.neto !== this.props.detalle.neto ? tooltips.neto : ''} id="neto">${formatters.pesos.format(this.props.total.neto)}</td>` ,
2025-01-17 00:07:45 -03:00
'</tr>' ,
'<tr>' ,
'<td class="grey">Monto Exento</td>' ,
'<td class="right aligned" id="exento">' + formatters . pesos . format ( this . props . total . exento ) + '</td>' ,
'</tr>' ,
'<tr>' ,
'<td class="grey">19% IVA</td>' ,
2025-02-24 15:47:58 -03:00
`<td class="right aligned${this.props.total.iva !== this.props.detalle.iva ? ' red' : ''}"${this.props.total.iva !== this.props.detalle.iva ? tooltips.iva : ''} id="iva">${formatters.pesos.format(this.props.total.iva)}</td>` ,
2025-01-17 00:07:45 -03:00
'</tr>' ,
'<tr>' ,
'<td class="grey">Monto Total</td>' ,
2025-02-24 15:47:58 -03:00
`<td class="right aligned${(this.props.total.total !== this.props.detalle.total) ? ' red' : ''}"${(this.props.total.total !== this.props.detalle.total) ? tooltips.total : ''}><strong id="total">${formatters.pesos.format(this.props.total.total)}</strong></td>` ,
2025-01-17 00:07:45 -03:00
'</tr>' ,
'</tbody>' ,
'</table>' ,
'</div>' ,
'</div>'
] . join ( " \n " )
},
guardar : () => {
if ( this . props . saved ) {
return [
'<div class="row">' ,
'<div class="fourteen wide column"></div>' ,
'<div class="two wide center aligned column">' ,
2025-02-03 22:17:57 -03:00
`<div class="ui green message guardar" data-index="${this.props.index}">` ,
2025-01-17 00:07:45 -03:00
'<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="${this.props.index}">Guardar</button>` ,
'</div>' ,
'</div>'
] . join ( " \n " )
}
}
}
watch () {
return {
save : () => {
2025-02-03 22:17:57 -03:00
document . querySelector ( `.guardar[data-index="${this.props.index}"]` ) . addEventListener ( 'click' , clickEvent => {
2025-01-17 00:07:45 -03:00
const index = clickEvent . currentTarget . getAttribute ( 'data-index' )
2025-02-03 22:17:57 -03:00
facturas . venta . save () . factura ({ index : index - 1 })
2025-01-17 00:07:45 -03:00
})
}
}
}
2025-02-03 22:17:57 -03:00
validate () {
if ( this . props . venta . id === null || typeof this . props . venta . id === 'undefined' ) {
return false
}
if ( this . props . index === null || typeof this . props . index === 'undefined' ) {
return false
}
if ( this . props . proporcion === null || typeof this . props . proporcion === 'undefined' ) {
return false
}
return ! ( this . props . receptor . rut === '' || this . props . receptor . nombre === '' || this . props . receptor . direccion === '' || this . props . receptor . comuna === '' );
}
2025-01-17 00:07:45 -03:00
save () {
2025-02-03 22:17:57 -03:00
if ( ! this . validate ()) {
return
}
2025-01-17 16:55:34 -03:00
let url = '{{$urls->api}}/ventas/facturas/add'
2025-01-17 00:07:45 -03:00
if ( this . saved ) {
2025-01-17 16:55:34 -03:00
url = `{{$urls->api}}/ventas/facturas/${this.props.id}/edit`
2025-01-17 00:07:45 -03:00
}
const method = 'post'
const body = new FormData ()
2025-01-17 16:55:34 -03:00
body . set ( 'venta_id' , this . props . venta . id )
2025-01-17 00:07:45 -03:00
body . set ( 'index' , this . props . index )
body . set ( 'proporcion' , this . props . proporcion )
body . set ( 'cliente' , JSON . stringify ( this . props . receptor ))
2025-01-17 16:55:34 -03:00
body . set ( 'terreno' , this . props . detalle . terreno )
body . set ( 'unidades' , JSON . stringify ( this . props . unidades . map ( unidad => {
return { unidad_id : unidad . unidad . props . id , precio : unidad . precio , prorrateo : unidad . prorrateo }
})))
2025-01-17 00:07:45 -03:00
body . set ( 'fecha' , [ this . props . fecha . getFullYear (), this . props . fecha . getMonth () + 1 , this . props . fecha . getDate ()] . join ( '-' ))
body . set ( 'detalle' , JSON . stringify ( this . props . detalle ))
body . set ( 'total' , JSON . stringify ( this . props . total ))
2025-01-17 16:55:34 -03:00
body . set ( 'uf' , JSON . stringify ({ fecha : [ this . props . uf . fecha . getFullYear (), this . props . uf . fecha . getMonth () + 1 , this . props . uf . fecha . getDate ()] . join ( '-' ), valor : this . props . uf . valor }))
2025-02-03 22:17:57 -03:00
2025-01-17 00:07:45 -03:00
return APIClient . fetch ( url , { method , body }) . then ( response => {
if ( ! response ) {
return
}
return response . json () . then ( json => {
if ( json . success ) {
this . props . id = json . factura . id
facturas . draw () . facturas ()
}
})
})
}
update () {
return {
venta : venta => {
this . props . venta = venta . props
this . props . fecha = venta . props . facturas . fecha
this . props . uf . fecha = venta . props . uf . fecha
this . props . uf . valor = venta . props . uf . valor
this . update () . propietario ( venta . props . propietarios . find ( propietario => propietario . props . index === this . props . index ))
this . update () . unidades ( venta . props . unidades )
2025-02-24 15:47:58 -03:00
this . props . detalle . total = venta . props . valor * this . props . proporcion * venta . props . uf . valor
this . update () . detalle ( venta . props . facturas . terreno . valor * this . prorrateo )
2025-01-17 00:07:45 -03:00
this . props . detalle . descuento = venta . prorrateo * this . props . proporcion
2025-02-24 15:47:58 -03:00
this . update () . total ()
2025-01-17 00:07:45 -03:00
},
detalle : terreno => {
this . props . detalle . terreno = terreno * this . props . proporcion
this . props . detalle . bruto = this . props . detalle . total - this . props . detalle . terreno
this . props . detalle . neto = this . props . detalle . bruto / 1.19
this . props . detalle . iva = this . props . detalle . neto * 0.19
this . props . detalle . base = this . props . detalle . neto + this . props . detalle . terreno
},
2025-02-24 15:47:58 -03:00
total : () => {
this . props . total . exento = this . props . detalle . terreno
this . props . total . neto = ( this . props . unidades . reduce (( sum , unidad ) => sum + unidad . precio , 0 ) - this . props . total . exento ) / 1.19
this . props . total . iva = this . props . total . neto * 0.19
this . props . total . total = this . props . total . neto + this . props . total . iva + this . props . total . exento
},
2025-01-17 00:07:45 -03:00
unidades : unidades => {
this . props . unidades = []
unidades . forEach ( unidad => {
this . props . unidades . push ({
unidad : unidad ,
2025-02-03 22:17:57 -03:00
descripcion : unidad . changeDescripcion ( this . props . proporcion || 1 ),
2025-02-24 15:47:58 -03:00
precio : unidad . props . valor * this . props . uf . valor ,
prorrateo : unidad . props . prorrateo
2025-01-17 00:07:45 -03:00
})
})
},
propietario : propietario => {
this . props . proporcion = propietario . props . proporcion
this . props . receptor = {
2025-02-03 22:17:57 -03:00
rut : propietario . props . rut ? ? '' ,
nombre : propietario . props . nombre ? ? '' ,
direccion : propietario . props . direccion ? ? '' ,
comuna : propietario . comuna ? ? ''
2025-01-17 00:07:45 -03:00
}
}
}
}
}
</ script >