Merge pull request 'feature/grabar-factura' (#11) from feature/grabar-factura into develop
Reviewed-on: #11
This commit is contained in:
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
class CreateFacturaVenta extends Phinx\Migration\AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$this->execute('SET unique_checks=0; SET foreign_key_checks=0;');
|
||||
$this->execute("ALTER DATABASE CHARACTER SET 'utf8mb4';");
|
||||
$this->execute("ALTER DATABASE COLLATE='utf8mb4_general_ci';");
|
||||
|
||||
$this->table('factura_venta')
|
||||
->addColumn('factura_id', 'integer', ['length' => 10, 'null' => false, 'signed' => false])
|
||||
->addColumn('venta_id', 'integer', ['length' => 10, 'null' => false, 'signed' => false])
|
||||
->addColumn('valor', 'double', ['null' => false])
|
||||
->addForeignKey('factura_id', 'factura_proyecto_operador', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
|
||||
->addForeignKey('venta_id', 'venta', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
|
||||
->create();
|
||||
$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
class CreateVentaDatosFacturas extends Phinx\Migration\AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$this->execute('SET unique_checks=0; SET foreign_key_checks=0;');
|
||||
$this->execute("ALTER DATABASE CHARACTER SET 'utf8mb4';");
|
||||
$this->execute("ALTER DATABASE COLLATE='utf8mb4_general_ci';");
|
||||
|
||||
$this->table('venta_datos_facturas')
|
||||
->addColumn('venta_id', 'integer', ['length' => 10, 'signed' => false])
|
||||
->addColumn('fecha', 'date')
|
||||
->addColumn('uf', 'string', ['length' => 50]) // fecha, valor
|
||||
->addColumn('ipc', 'string', ['length' => 50]) // fecha, valor
|
||||
->addColumn('terreno', 'integer', ['signed' => false])
|
||||
->addColumn('unidades', 'text') // id, precios, prorrateo
|
||||
->addForeignKey('venta_id', 'venta', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
|
||||
->create();
|
||||
$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');
|
||||
}
|
||||
}
|
@ -11,11 +11,12 @@ class CreateFacturas extends Phinx\Migration\AbstractMigration
|
||||
$this->execute("ALTER DATABASE COLLATE='utf8mb4_general_ci';");
|
||||
|
||||
$this->table('facturas')
|
||||
->addColumn('venta_id', 'integer', ['length' => 10, 'null' => false, 'signed' => false])
|
||||
->addColumn('index', 'integer', ['length' => 10, 'null' => false, 'signed' => false])
|
||||
->addColumn('proporcion', 'double', ['null' => false, 'signed' => false])
|
||||
->addColumn('data', 'text', ['null' => false])
|
||||
->addColumn('venta_id', 'integer', ['length' => 10, 'signed' => false])
|
||||
->addColumn('index', 'integer', ['signed' => false])
|
||||
->addColumn('proporcion', 'double', ['signed' => false]) // %
|
||||
->addColumn('cliente_rut', 'integer', ['length' => 10, 'signed' => false])
|
||||
->addForeignKey('venta_id', 'venta', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
|
||||
->addForeignKey('cliente_rut', 'personas', 'rut', ['delete' => 'cascade', 'update' => 'cascade'])
|
||||
->create();
|
||||
$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');
|
||||
}
|
||||
|
@ -4,3 +4,6 @@ use Incoviba\Controller\API\Ventas\Facturas;
|
||||
$app->group('/facturas', function($app) {
|
||||
$app->post('/add[/]', [Facturas::class, 'add']);
|
||||
});
|
||||
$app->group('/facturas/{factura_id}', function($app) {
|
||||
$app->post('/edit[/]', [Facturas::class, 'edit']);
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
405
app/resources/views/ventas/facturacion/show/factura.blade.php
Normal file
405
app/resources/views/ventas/facturacion/show/factura.blade.php
Normal file
@ -0,0 +1,405 @@
|
||||
<script>
|
||||
class Factura {
|
||||
props = {
|
||||
id: 0,
|
||||
venta: null,
|
||||
index: 0,
|
||||
proporcion: 0,
|
||||
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
|
||||
}
|
||||
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">',
|
||||
'<div class="ui center aligned orange segment">',
|
||||
'<strong>',
|
||||
`RUT:${this.props.emisor.rut.toUpperCase()}<br/>`,
|
||||
'FACTURA ELECTRÓNICA<br/>',
|
||||
`<span class="ui red text">N° ${this.props.venta.id}${this.props.index}</span>`,
|
||||
'</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">',
|
||||
'<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>',
|
||||
'</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}) => {
|
||||
const descuento = this.props.detalle.terreno * unidad.prorrateo
|
||||
const bruto = unidad.precio - descuento
|
||||
const neto = bruto / 1.19
|
||||
const data = [
|
||||
no,
|
||||
unidad.descripcion,
|
||||
'1 UNID',
|
||||
formatters.pesos.format(neto),
|
||||
'AF',
|
||||
formatters.pesos.format(neto)
|
||||
]
|
||||
|
||||
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}) => {
|
||||
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(this.props.total.neto)+'</td>',
|
||||
'</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>',
|
||||
'<td class="right aligned" id="iva">'+formatters.pesos.format(this.props.total.iva)+'</td>',
|
||||
'</tr>',
|
||||
'<tr>',
|
||||
'<td class="grey">Monto Total</td>',
|
||||
'<td class="right aligned"><strong id="total">'+formatters.pesos.format(this.props.total.total)+'</strong></td>',
|
||||
'</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">',
|
||||
`<div class="ui green message guardar" data-index="${this.props.index}">`,
|
||||
'<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: () => {
|
||||
document.querySelector(`.guardar[data-index="${this.props.index}"]`).addEventListener('click', clickEvent => {
|
||||
const index = clickEvent.currentTarget.getAttribute('data-index')
|
||||
facturas.venta.save().factura({index: index - 1})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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 === '');
|
||||
|
||||
}
|
||||
save() {
|
||||
if (!this.validate()) {
|
||||
return
|
||||
}
|
||||
let url = '{{$urls->api}}/ventas/facturas/add'
|
||||
if (this.saved) {
|
||||
url = `{{$urls->api}}/ventas/facturas/${this.props.id}/edit`
|
||||
}
|
||||
const method = 'post'
|
||||
const body = new FormData()
|
||||
body.set('venta_id', this.props.venta.id)
|
||||
body.set('index', this.props.index)
|
||||
body.set('proporcion', this.props.proporcion)
|
||||
body.set('cliente', JSON.stringify(this.props.receptor))
|
||||
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}
|
||||
})))
|
||||
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))
|
||||
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}))
|
||||
|
||||
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)
|
||||
this.props.total.total = this.props.detalle.total = venta.props.valor * this.props.proporcion * venta.props.uf.valor
|
||||
this.update().detalle(venta.props.facturas.terreno.valor * venta.prorrateo)
|
||||
this.props.total.exento = this.props.detalle.terreno
|
||||
this.props.total.iva = this.props.detalle.iva
|
||||
this.props.total.neto = this.props.unidades.reduce((sum, unidad) => sum + unidad.precio, 0)
|
||||
this.props.detalle.descuento = venta.prorrateo * this.props.proporcion
|
||||
},
|
||||
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
|
||||
},
|
||||
unidades: unidades => {
|
||||
this.props.unidades = []
|
||||
unidades.forEach(unidad => {
|
||||
this.props.unidades.push({
|
||||
unidad: unidad,
|
||||
descripcion: unidad.changeDescripcion(this.props.proporcion || 1),
|
||||
precio: unidad.props.valor * this.props.uf.valor * this.props.proporcion,
|
||||
prorrateo: unidad.props.prorrateo * this.props.proporcion
|
||||
})
|
||||
})
|
||||
},
|
||||
propietario: propietario => {
|
||||
this.props.proporcion = propietario.props.proporcion
|
||||
|
||||
this.props.receptor = {
|
||||
rut: propietario.props.rut ?? '',
|
||||
nombre: propietario.props.nombre ?? '',
|
||||
direccion: propietario.props.direccion ?? '',
|
||||
comuna: propietario.comuna ?? ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,193 @@
|
||||
<script>
|
||||
class Propietario {
|
||||
props = {
|
||||
index: 0,
|
||||
proporcion: 0,
|
||||
rut: '',
|
||||
nombre: '',
|
||||
direccion: '',
|
||||
comuna: ''
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
this.props = props
|
||||
}
|
||||
|
||||
get comuna() {
|
||||
const comuna = $('#comuna_propietario'+this.props.index).dropdown('get text')
|
||||
if (typeof comuna === 'string') {
|
||||
return comuna
|
||||
}
|
||||
return this.props.comuna ?? ''
|
||||
}
|
||||
update() {
|
||||
return {
|
||||
proporcion: (valor) => {
|
||||
this.props.proporcion = valor
|
||||
facturas.venta.update().facturas()
|
||||
facturas.draw().facturas()
|
||||
},
|
||||
rut: rut => {
|
||||
this.props.rut = rut
|
||||
facturas.venta.update().facturas()
|
||||
facturas.draw().facturas()
|
||||
},
|
||||
nombre: nombre => {
|
||||
this.props.nombre = nombre
|
||||
facturas.venta.update().facturas()
|
||||
facturas.draw().facturas()
|
||||
},
|
||||
direccion: direccion => {
|
||||
this.props.direccion = direccion
|
||||
facturas.venta.update().facturas()
|
||||
facturas.draw().facturas()
|
||||
},
|
||||
comuna: comuna => {
|
||||
this.props.comuna = comuna
|
||||
facturas.venta.update().facturas()
|
||||
facturas.draw().facturas()
|
||||
},
|
||||
}
|
||||
}
|
||||
watch() {
|
||||
return {
|
||||
propietario: () => {
|
||||
this.watch().proporcion()
|
||||
this.watch().rut()
|
||||
this.watch().nombre()
|
||||
this.watch().direccion()
|
||||
this.watch().comuna()
|
||||
},
|
||||
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: () => {
|
||||
document.getElementById('rut'+this.props.index).addEventListener('input', inputEvent => {
|
||||
const rut = inputEvent.currentTarget.value
|
||||
if (rut === this.props.rut) {
|
||||
return
|
||||
}
|
||||
this.update().rut(rut)
|
||||
})
|
||||
},
|
||||
nombre: () => {
|
||||
document.getElementById('propietario'+this.props.index).addEventListener('input', inputEvent => {
|
||||
const nombre = inputEvent.currentTarget.value
|
||||
if (nombre === this.props.nombre) {
|
||||
return
|
||||
}
|
||||
this.update().nombre(nombre)
|
||||
})
|
||||
},
|
||||
direccion: () => {
|
||||
document.getElementById('direccion_propietario'+this.props.index).addEventListener('input', inputEvent => {
|
||||
const direccion = inputEvent.currentTarget.value
|
||||
if (direccion === this.props.direccion) {
|
||||
return
|
||||
}
|
||||
this.update().direccion(direccion)
|
||||
})
|
||||
},
|
||||
comuna: () => {
|
||||
if ($('#comuna_propietario'+this.props.index).dropdown('get value')) {
|
||||
return
|
||||
}
|
||||
$('#comuna_propietario'+this.props.index).dropdown({
|
||||
fireOnInit: true,
|
||||
forceSelection: true,
|
||||
onChange: (value, text) => {
|
||||
this.update().comuna(value)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
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('</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="three wide field">',
|
||||
`<label for="comuna_propietario${this.props.index}">Comuna</label>`,
|
||||
`<div class="ui search selection dropdown" id="comuna_propietario${this.props.index}">`,
|
||||
`<input type="hidden" name="comuna_propietario${this.props.index}" value="${this.props.comuna}" />`,
|
||||
'<i class="dropdown icon"></i>',
|
||||
'<div class="default text">Comuna</div>',
|
||||
'<div class="menu">',
|
||||
@foreach ($comunas as $comuna)
|
||||
'<div class="item" data-value="{{ $comuna->id }}">{{ $comuna->descripcion }}</div>',
|
||||
@endforeach
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join("\n")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
125
app/resources/views/ventas/facturacion/show/unidad.blade.php
Normal file
125
app/resources/views/ventas/facturacion/show/unidad.blade.php
Normal file
@ -0,0 +1,125 @@
|
||||
<script>
|
||||
class Unidad {
|
||||
props = {
|
||||
id: 0,
|
||||
tipo: '',
|
||||
descripcion: '',
|
||||
prorrateo: 0,
|
||||
propiedad_unidad_id: 0,
|
||||
valor: 0
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
this.props = props
|
||||
}
|
||||
|
||||
changeDescripcion(proporcion = 1) {
|
||||
return this.descripcion = [this.props.tipo, this.props.descripcion, `[UF ${facturas.formatters.ufs.format(this.props.valor * proporcion)}]`].join(' ')
|
||||
}
|
||||
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)
|
||||
})
|
||||
})
|
||||
},
|
||||
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).then(() => {
|
||||
facturas.venta.update().totalUnidades()
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
472
app/resources/views/ventas/facturacion/show/venta.blade.php
Normal file
472
app/resources/views/ventas/facturacion/show/venta.blade.php
Normal file
@ -0,0 +1,472 @@
|
||||
<script>
|
||||
class Venta {
|
||||
props = {
|
||||
id: 0,
|
||||
fecha: null,
|
||||
last: {
|
||||
november: null
|
||||
},
|
||||
valor: 0,
|
||||
inmobiliaria: {
|
||||
rut: '',
|
||||
nombre: '',
|
||||
direccion: '',
|
||||
comuna: ''
|
||||
},
|
||||
proyecto: {
|
||||
id: 0
|
||||
},
|
||||
uf: {
|
||||
fecha: null,
|
||||
valor: 0
|
||||
},
|
||||
ipc: {
|
||||
fecha: null,
|
||||
valor: 0
|
||||
},
|
||||
unidades: [],
|
||||
facturas: {
|
||||
fecha: null,
|
||||
terreno: {
|
||||
fecha: null,
|
||||
valor: 0
|
||||
},
|
||||
facturas: []
|
||||
},
|
||||
propietarios: [],
|
||||
estado: {
|
||||
fecha: null
|
||||
}
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
this.props = props
|
||||
}
|
||||
|
||||
get prorrateo() {
|
||||
return this.props.unidades.reduce((sum, unidad) => sum + unidad.props.prorrateo, 0.0)
|
||||
}
|
||||
|
||||
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 propietarios = []
|
||||
for (let i = 0; i < diff; i ++) {
|
||||
propietarios.push(this.add().propietario({
|
||||
rut: '',
|
||||
nombre: '',
|
||||
proporcion: (p*100).toFixed(0)/100,
|
||||
direccion: '',
|
||||
comuna: ''
|
||||
}))
|
||||
}
|
||||
document.getElementById('propietarios').innerHTML = this.draw().propietarios()
|
||||
propietarios.forEach(propietario => {
|
||||
this.add().factura(propietario)
|
||||
})
|
||||
this.watch().propietarios()
|
||||
this.update().facturas()
|
||||
return
|
||||
}
|
||||
for (let i = this.props.propietarios.length - 1; i >= count; i --) {
|
||||
this.remove().propietario(i)
|
||||
}
|
||||
},
|
||||
fecha: date => {
|
||||
this.props.uf.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.valor = json.uf
|
||||
|
||||
return this.update().ipc()
|
||||
})
|
||||
})
|
||||
},
|
||||
fechaFacturas: date => {
|
||||
if (this.props.facturas.fecha === date) {
|
||||
return
|
||||
}
|
||||
this.props.facturas.fecha = date
|
||||
this.update().facturas()
|
||||
return new Promise(resolve => resolve())
|
||||
},
|
||||
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')
|
||||
|
||||
this.update().facturas()
|
||||
},
|
||||
terreno: newValue => {
|
||||
const date = this.props.last.november
|
||||
|
||||
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.success) {
|
||||
return
|
||||
}
|
||||
this.props.facturas.terreno.fecha = new Date(json.terreno.fecha)
|
||||
this.props.facturas.terreno.valor = parseInt(json.terreno.valor)
|
||||
|
||||
document.getElementById('terreno').parentElement.parentElement.remove()
|
||||
|
||||
this.update().ipc()
|
||||
})
|
||||
})
|
||||
},
|
||||
ipc: () => {
|
||||
const mesAnterior = new Date([this.props.facturas.fecha.getFullYear(), this.props.facturas.fecha.getMonth()-1, '1'].join('-'))
|
||||
if (this.props.last.november.getMonth() === mesAnterior.getMonth() && this.props.last.november.getFullYear() === mesAnterior.getFullYear()) {
|
||||
return
|
||||
}
|
||||
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.getFullYear(), mesAnterior.getMonth()+1, mesAnterior.getDate()].join('-'))
|
||||
return fetchAPI(url, {method, body}).then(response => {
|
||||
if (!response) {
|
||||
return
|
||||
}
|
||||
return response.json().then(json => {
|
||||
this.props.ipc = json.ipc
|
||||
this.props.facturas.terreno.valor *= (1 + parseFloat(json.ipc.valor))
|
||||
this.update().facturas()
|
||||
})
|
||||
})
|
||||
},
|
||||
facturas: () => {
|
||||
this.props.facturas.facturas.forEach(factura => {
|
||||
factura.update().venta(this)
|
||||
})
|
||||
},
|
||||
form: () => {
|
||||
// fecha
|
||||
// unidades
|
||||
// valor total unidades
|
||||
// terreno
|
||||
// propietarios
|
||||
// ipc no disponible
|
||||
}
|
||||
}
|
||||
}
|
||||
watch() {
|
||||
return {
|
||||
venta: () => {
|
||||
this.watch().fecha()
|
||||
this.watch().fechaFacturas()
|
||||
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).then(() => {
|
||||
facturas.draw().facturas()
|
||||
})
|
||||
}
|
||||
$('#fecha_uf').calendar(cdo)
|
||||
},
|
||||
fechaFacturas: () => {
|
||||
const cdo = structuredClone(calendar_date_options)
|
||||
cdo['initialDate'] = this.props.estado.fecha
|
||||
cdo['onChange'] = (date, text, mode) => {
|
||||
this.update().fechaFacturas(date).then(() => {
|
||||
facturas.draw().facturas()
|
||||
})
|
||||
}
|
||||
$('#fecha_facturas').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.facturas.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()
|
||||
})
|
||||
},
|
||||
facturas: () => {
|
||||
this.props.facturas.facturas.forEach(factura => {
|
||||
factura.watch().save()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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().fechaFactura())
|
||||
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")
|
||||
},
|
||||
fechaFactura: () => {
|
||||
return [
|
||||
'<div class="three wide field">',
|
||||
'<label for="fecha_uf">Fecha Facturas</label>',
|
||||
'<div class="ui calendar" id="fecha_facturas">',
|
||||
'<div class="ui right icon input">',
|
||||
'<input type="text" name="fecha_facturas" />',
|
||||
'<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.facturas.terreno.fecha === 'undefined' || this.props.facturas.terreno.fecha === null || this.props.facturas.terreno.fecha.getTime() < 0 || this.props.facturas.terreno.fecha < this.props.last.november) {
|
||||
output.push(...[
|
||||
'<div class="four wide field">',
|
||||
'<label for="terreno">Valor Terreno al '+this.props.last.november.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.facturas.terreno.fecha && this.props.facturas.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.facturas.forEach(factura => {
|
||||
output.push(factura.draw().factura({formatters}))
|
||||
})
|
||||
return output.join("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
add() {
|
||||
return {
|
||||
propietario: ({rut, nombre, proporcion, direccion, comuna}) => {
|
||||
const index = this.props.propietarios.length + 1
|
||||
const propietario = new Propietario({index, proporcion, rut, nombre, direccion, comuna})
|
||||
this.props.propietarios.push(propietario)
|
||||
return propietario
|
||||
},
|
||||
factura: propietario => {
|
||||
const factura = new Factura({
|
||||
id: 0,
|
||||
venta: this.props,
|
||||
index: propietario.props.index,
|
||||
proporcion: propietario.props.proporcion,
|
||||
emisor: {
|
||||
rut: this.props.inmobiliaria.rut,
|
||||
nombre: this.props.inmobiliaria.nombre,
|
||||
direccion: this.props.inmobiliaria.direccion,
|
||||
comuna: this.props.inmobiliaria.comuna
|
||||
},
|
||||
receptor: {
|
||||
rut: propietario.props.rut ?? '',
|
||||
nombre: propietario.props.nombre ?? '',
|
||||
direccion: propietario.props.direccion ?? '',
|
||||
comuna: propietario.props.comuna ?? ''
|
||||
},
|
||||
unidades: [],
|
||||
detalle: {
|
||||
total: this.props.valor * this.props.uf.valor * propietario.props.proporcion,
|
||||
base: 0,
|
||||
terreno: 0,
|
||||
neto: 0,
|
||||
iva: 0,
|
||||
bruto: 0,
|
||||
descuento: 0,
|
||||
},
|
||||
total: {
|
||||
neto: 0,
|
||||
exento: 0,
|
||||
iva: 0,
|
||||
total: this.props.valor * this.props.uf.valor * propietario.props.proporcion
|
||||
},
|
||||
uf: this.props.uf
|
||||
})
|
||||
factura.update().venta(this)
|
||||
this.props.facturas.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.facturas.splice(index, 1)
|
||||
document.getElementById('facturas').querySelectorAll("[data-index='"+(index+1)+"']").forEach(factura => {
|
||||
factura.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
save() {
|
||||
return {
|
||||
factura: ({index}) => {
|
||||
const factura = this.props.facturas.facturas[index]
|
||||
return factura.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -18,11 +18,18 @@ class Facturas extends Controller
|
||||
$output = [
|
||||
'input' => $data,
|
||||
'factura' => null,
|
||||
'saved' => false
|
||||
'success' => false
|
||||
];
|
||||
try {
|
||||
/*foreach (['cliente', 'unidades', 'detalle', 'total', 'uf'] as $key) {
|
||||
if (!isset($data[$key]) or empty($data[$key])) {
|
||||
continue;
|
||||
}
|
||||
$data[$key] = json_decode($data[$key], true);
|
||||
}*/
|
||||
$data['cliente'] = json_decode($data['cliente'], true);
|
||||
$output['factura'] = $facturaService->add($data);
|
||||
$output['saved'] = true;
|
||||
$output['success'] = true;
|
||||
} catch (Implement\Exception\EmptyResult) {
|
||||
$output['error'] = 'No se pudo agregar la factura';
|
||||
return $this->withJson($response, $output, 400);
|
||||
|
@ -5,9 +5,9 @@ use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
trait withJson
|
||||
{
|
||||
public function withJson(ResponseInterface $response, array|object $data = []): ResponseInterface
|
||||
public function withJson(ResponseInterface $response, array|object $data = [], int $statusCode = 200): ResponseInterface
|
||||
{
|
||||
$response->getBody()->write(json_encode($data));
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
return $response->withStatus($statusCode)->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Incoviba\Common\Alias\View;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
|
||||
class Facturacion extends Ideal\Controller
|
||||
@ -29,6 +30,7 @@ class Facturacion extends Ideal\Controller
|
||||
Service\Venta $ventaService, Service\Proyecto\Terreno $terrenoService,
|
||||
Service\IPC $ipcService, Service\UF $ufService,
|
||||
Service\Venta\Factura $facturasService,
|
||||
Repository\Comuna $comunaRepository,
|
||||
int $venta_id): ResponseInterface
|
||||
{
|
||||
$venta = $ventaService->getById($venta_id);
|
||||
@ -48,7 +50,8 @@ class Facturacion extends Ideal\Controller
|
||||
}
|
||||
}
|
||||
$facturas = $facturasService->getByVenta($venta->id);
|
||||
$comunas = $comunaRepository->fetchAll('descripcion');
|
||||
|
||||
return $view->render($response, 'ventas.facturacion.show', compact('venta', 'terreno', 'uf', 'ipc', 'facturas'));
|
||||
return $view->render($response, 'ventas.facturacion.show', compact('venta', 'terreno', 'uf', 'ipc', 'facturas', 'comunas'));
|
||||
}
|
||||
}
|
||||
|
@ -8,30 +8,16 @@ 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;
|
||||
public Model\Persona $cliente;
|
||||
|
||||
public ?DateTimeInterface $fecha = null;
|
||||
public ?array $unidades = null; // [[unidad, descripcion, precio, prorrateo]]
|
||||
public ?int $terreno = null;
|
||||
public ?object $uf = null; // [fecha, valor]
|
||||
public ?object $ipc = null; // [fecha, valor]
|
||||
|
||||
protected array $estados;
|
||||
public function estados(): array
|
||||
@ -42,6 +28,52 @@ class Factura extends Ideal\Model
|
||||
return $this->estados ?? [];
|
||||
}
|
||||
|
||||
public function total(): float
|
||||
{
|
||||
return $this->venta->valor * $this->uf->valor * $this->proporcion;
|
||||
}
|
||||
public function bruto(): float
|
||||
{
|
||||
return $this->total() - $this->terreno * $this->proporcion;
|
||||
}
|
||||
public function iva(): float
|
||||
{
|
||||
return $this->neto() * .19;
|
||||
}
|
||||
public function neto(): float
|
||||
{
|
||||
return $this->bruto() / 1.19;
|
||||
}
|
||||
public function base(): float
|
||||
{
|
||||
return $this->neto() + $this->terreno * $this->proporcion;
|
||||
}
|
||||
public function detalle(): array
|
||||
{
|
||||
return [
|
||||
'base' => $this->base(),
|
||||
'terreno' => $this->terreno * $this->proporcion,
|
||||
'neto' => $this->neto(),
|
||||
'iva' => $this->iva(),
|
||||
'bruto' => $this->bruto(),
|
||||
'total' => $this->total(),
|
||||
'descuento' => array_reduce($this->unidades, function($sum, $unidad) {
|
||||
return $sum + $unidad->prorrateo * $this->proporcion;
|
||||
}, 0)
|
||||
];
|
||||
}
|
||||
public function totales(): array
|
||||
{
|
||||
return [
|
||||
'neto' => array_reduce($this->unidades, function($sum, $unidad) {
|
||||
return $sum + $unidad->precio * $this->proporcion;
|
||||
}),
|
||||
'exento' => $this->terreno * $this->proporcion,
|
||||
'iva' => $this->iva(),
|
||||
'total' => $this->total()
|
||||
];
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return array_merge(parent::jsonSerialize(), [
|
||||
@ -49,38 +81,26 @@ class Factura extends Ideal\Model
|
||||
'index' => $this->index,
|
||||
'proporcion' => $this->proporcion,
|
||||
'emisor' => [
|
||||
'rut' => $this->emisorRut,
|
||||
'nombre' => $this->emisorNombre,
|
||||
'direccion' => $this->emisorDireccion
|
||||
'rut' => $this->venta->proyecto()->inmobiliaria()->rut(),
|
||||
'nombre' => $this->venta->proyecto()->inmobiliaria()->nombreCompleto(),
|
||||
'direccion' => $this->venta->proyecto()->direccion()->simple(),
|
||||
'comuna' => $this->venta->proyecto()->direccion()->comuna->descripcion
|
||||
],
|
||||
'receptor' => [
|
||||
'rut' => $this->receptorRut,
|
||||
'nombre' => $this->receptorNombre,
|
||||
'direccion' => $this->receptorDireccion,
|
||||
'comuna' => $this->receptorComuna
|
||||
'rut' => $this->cliente->rutCompleto(),
|
||||
'nombre' => $this->cliente->nombreCompleto(),
|
||||
'direccion' => $this->cliente->datos()->direccion?->simple(),
|
||||
'comuna' => $this->cliente->datos()->direccion?->comuna->descripcion
|
||||
],
|
||||
'fecha' => $this->fecha->format('Y-m-d'),
|
||||
'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
|
||||
],
|
||||
'detalle' => $this->detalle(),
|
||||
'total' => $this->totales(),
|
||||
'uf' => [
|
||||
'fecha' => $this->fechaUF->format('Y-m-d'),
|
||||
'valor' => $this->valorUF
|
||||
'fecha' => $this->uf?->fecha->format('Y-m-d'),
|
||||
'valor' => $this->uf?->valor
|
||||
],
|
||||
'estados' => $this->estados()
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Incoviba\Repository\Venta;
|
||||
|
||||
use PDO;
|
||||
use DateTimeImmutable;
|
||||
use Incoviba\Common\Define;
|
||||
use Incoviba\Common\Ideal;
|
||||
@ -8,10 +9,13 @@ use Incoviba\Common\Implement;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResult;
|
||||
use Incoviba\Model;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
|
||||
class Factura extends Ideal\Repository
|
||||
{
|
||||
public function __construct(Implement\Connection $connection, protected Repository\Venta $ventaRepository)
|
||||
public function __construct(Implement\Connection $connection, protected Repository\Venta $ventaRepository,
|
||||
protected Service\Persona $personaService,
|
||||
protected Repository\Persona $personaRepository, protected Unidad $unidadRepository)
|
||||
{
|
||||
parent::__construct($connection);
|
||||
$this->setTable('facturas');
|
||||
@ -19,89 +23,175 @@ class Factura extends Ideal\Repository
|
||||
|
||||
public function create(?array $data = null): Model\Venta\Factura
|
||||
{
|
||||
$map = (new Implement\Repository\MapperParser(['index']))
|
||||
$map = (new Implement\Repository\MapperParser(['index', 'proporcion']))
|
||||
->register('venta_id', (new Implement\Repository\Mapper())
|
||||
->setProperty('venta')
|
||||
->setFunction(function($data) {
|
||||
return $this->ventaRepository->fetchById($data['venta_id']);
|
||||
}))
|
||||
->register('cliente_rut', (new Implement\Repository\Mapper())
|
||||
->setProperty('cliente')
|
||||
->setFunction(function($data) {
|
||||
return $this->personaService->getById($data['cliente_rut']);
|
||||
}));
|
||||
$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 $this->createDatos($factura, $data);
|
||||
}
|
||||
public function createDatos(Model\Venta\Factura &$factura, ?array $data = null): Model\Venta\Factura
|
||||
{
|
||||
try {
|
||||
$result = $this->getDatos($factura->venta->id);
|
||||
if ($result['fecha'] !== null) {
|
||||
$factura->fecha = new DateTimeImmutable($result['fecha']);
|
||||
}
|
||||
if ($result['uf'] !== null) {
|
||||
$factura->uf = json_decode($result['uf']);
|
||||
$factura->uf->fecha = new DateTimeImmutable($factura->uf->fecha);
|
||||
}
|
||||
if ($result['ipc'] !== null) {
|
||||
$factura->ipc = json_decode($result['ipc']);
|
||||
$factura->ipc->fecha = new DateTimeImmutable($factura->ipc->fecha);
|
||||
}
|
||||
if ($result['unidades'] !== null) {
|
||||
$factura->unidades = array_map(function($datos) use ($factura) {
|
||||
$unidad = $this->unidadRepository->fetchById($datos->unidad_id);
|
||||
return (object) [
|
||||
'unidad' => $unidad,
|
||||
'descripcion' => implode(' ', [ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion), $unidad->descripcion]),
|
||||
'precio' => $datos->precio,
|
||||
'prorrateo' => $datos->prorrateo
|
||||
];
|
||||
}, json_decode($result['unidades']));
|
||||
}
|
||||
if ($result['terreno'] !== null) {
|
||||
$factura->terreno = $result['terreno'];
|
||||
}
|
||||
} catch (EmptyResult) {
|
||||
if (isset($data['fecha'])) {
|
||||
$factura->fecha = new DateTimeImmutable($data['fecha']);
|
||||
}
|
||||
if (isset($data['uf'])) {
|
||||
$factura->uf = json_decode($data['uf']);
|
||||
$factura->uf->fecha = new DateTimeImmutable($factura->uf->fecha);
|
||||
}
|
||||
if (isset($data['ipc'])) {
|
||||
$factura->ipc = json_decode($data['ipc']);
|
||||
$factura->ipc->fecha = new DateTimeImmutable($factura->ipc->fecha);
|
||||
}
|
||||
if (isset($data['unidades'])) {
|
||||
$factura->unidades = array_map(function($datos) use ($factura) {
|
||||
$unidad = $this->unidadRepository->fetchById($datos->unidad_id);
|
||||
return (object) [
|
||||
'unidad' => $unidad,
|
||||
'descripcion' => implode(' ', [ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion), $unidad->descripcion]),
|
||||
'precio' => $datos->precio,
|
||||
'prorrateo' => $datos->prorrateo
|
||||
];
|
||||
}, json_decode($data['unidades']));
|
||||
}
|
||||
if (isset($data['terreno'])) {
|
||||
$factura->terreno = (int) $data['terreno'];
|
||||
}
|
||||
}
|
||||
|
||||
return $factura;
|
||||
}
|
||||
protected function getDatos(int $venta_id): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
->select()
|
||||
->from('venta_datos_facturas')
|
||||
->where('venta_id = :venta_id');
|
||||
$result = $this->connection->execute($query, ['venta_id' => $venta_id])->fetch(PDO::FETCH_ASSOC);
|
||||
if ($result === false) {
|
||||
throw new EmptyResult($query);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
public function save(Define\Model $model): Model\Venta\Factura
|
||||
{
|
||||
$model->id = $this->saveNew([
|
||||
'venta_id',
|
||||
'index',
|
||||
'data'
|
||||
'proporcion',
|
||||
'cliente_rut'
|
||||
], [
|
||||
$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
|
||||
]
|
||||
])
|
||||
$model->proporcion,
|
||||
$model->cliente->rut
|
||||
]);
|
||||
return $model;
|
||||
return $this->saveDatos($model);
|
||||
}
|
||||
protected function saveDatos(Model\Venta\Factura $factura): Model\Venta\Factura
|
||||
{
|
||||
try {
|
||||
$this->getDatos($factura->venta->id);
|
||||
} catch (EmptyResult) {
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
->insert()
|
||||
->into('venta_datos_facturas')
|
||||
->columns([
|
||||
'venta_id',
|
||||
'fecha',
|
||||
'unidades',
|
||||
'terreno',
|
||||
'uf',
|
||||
'ipc'
|
||||
])
|
||||
->values([
|
||||
'venta_id' => $factura->venta->id,
|
||||
'fecha' => $factura->fecha?->format('Y-m-d'),
|
||||
'unidades' => (isset($factura->unidades)) ? json_encode(array_map(function($unidad) {
|
||||
return [
|
||||
'unidad_id' => $unidad->unidad->id,
|
||||
'precio' => $unidad->precio,
|
||||
'prorrateo' => $unidad->prorrateo
|
||||
];
|
||||
}, $factura?->unidades)) : null,
|
||||
'terreno' => $factura?->terreno,
|
||||
'uf' => (isset($factura->uf)) ? json_encode(['fecha' => $factura->uf?->fecha->format('Y-m-d'), 'valor' => $factura->uf?->valor]) : null,
|
||||
'ipc' => (isset($factura->ipc)) ? json_encode(['fecha' => $factura->ipc?->fecha->format('Y-m-d'), 'valor' => $factura->ipc?->valor]) : null
|
||||
]);
|
||||
$this->connection->execute($query);
|
||||
}
|
||||
|
||||
return $factura;
|
||||
}
|
||||
public function edit(Define\Model $model, array $new_data): Model\Venta\Factura
|
||||
{
|
||||
return $this->update($model, ['venta_id', 'index', 'data'], $new_data);
|
||||
$model = $this->editDatos($model, $new_data);
|
||||
return $this->update($model, ['venta_id', 'index', 'proporcion', 'cliente_rut'], $new_data);
|
||||
}
|
||||
protected function editDatos(Model\Venta\Factura $factura, array $new_data): Model\Venta\Factura
|
||||
{
|
||||
$dataFields = [
|
||||
'fecha',
|
||||
'unidades',
|
||||
'terreno',
|
||||
'uf',
|
||||
'ipc'
|
||||
];
|
||||
$fields = [];
|
||||
$values = [];
|
||||
foreach ($dataFields as $field) {
|
||||
if (isset($new_data[$field])) {
|
||||
$fields[] = "{$field} = ?";
|
||||
$values[] = is_array($new_data[$field]) ? json_encode($new_data[$field]) : $new_data[$field];
|
||||
}
|
||||
}
|
||||
if (count($fields) === 0) {
|
||||
return $factura;
|
||||
}
|
||||
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
->update('venta_datos_facturas')
|
||||
->set($fields)
|
||||
->where('venta_id = ?')
|
||||
->limit(1);
|
||||
$this->connection->execute($query, array_merge($values, [$factura->venta->id]));
|
||||
|
||||
return $this->fetchById($factura->id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +213,7 @@ class Factura extends Ideal\Repository
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
->select()
|
||||
->from($this->getTable())
|
||||
->where('venta_id = :venta_id AND index = :index');
|
||||
->where('venta_id = :venta_id AND `index` = :index');
|
||||
return $this->fetchOne($query, ['venta_id' => $venta_id, 'index' => $index]);
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ use Psr\Log\LoggerInterface;
|
||||
|
||||
class Persona extends Ideal\Service
|
||||
{
|
||||
public function __construct(LoggerInterface $logger, protected Repository\Persona $personaRepository,
|
||||
protected Repository\Persona\Datos $datosPersonaRepository)
|
||||
public function __construct(LoggerInterface $logger,
|
||||
protected Repository\Persona $personaRepository,
|
||||
protected Repository\Persona\Datos $datosPersonaRepository,
|
||||
protected Repository\Venta\Propietario $propietarioRepository)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
@ -24,11 +26,21 @@ class Persona extends Ideal\Service
|
||||
try {
|
||||
$persona = $this->personaRepository->fetchById($data['rut']);
|
||||
} catch (Implement\Exception\EmptyResult) {
|
||||
try {
|
||||
$propietario = $this->propietarioRepository->fetchById($data['rut']);
|
||||
$data['nombres'] = $propietario->nombres;
|
||||
$data['apellido_paterno'] = $propietario->apellidos['paterno'];
|
||||
$data['apellido_materno'] = $propietario->apellidos['materno'];
|
||||
$data['direccion_id'] = $propietario->datos->direccion->id;
|
||||
} catch (Implement\Exception\EmptyResult) {}
|
||||
$persona = $this->personaRepository->create($data);
|
||||
$persona = $this->personaRepository->save($persona);
|
||||
}
|
||||
if (isset($data['email']) or isset($data['telefono'])) {
|
||||
if (isset($data['direccion_id']) or isset($data['email']) or isset($data['telefono'])) {
|
||||
$datosData = ['persona_rut' => $persona->rut];
|
||||
if (isset($data['direccion_id'])) {
|
||||
$datosData['direccion_id'] = $data['direccion_id'];
|
||||
}
|
||||
if (isset($data['email'])) {
|
||||
$datosData['email'] = $data['email'];
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ namespace Incoviba\Service;
|
||||
|
||||
use Predis\ClientInterface;
|
||||
use Incoviba\Common\Implement\Exception\EmptyRedis;
|
||||
use Predis\Connection\ConnectionException;
|
||||
|
||||
class Redis
|
||||
{
|
||||
@ -10,13 +11,21 @@ class Redis
|
||||
|
||||
public function get(string $name): mixed
|
||||
{
|
||||
try {
|
||||
if (!$this->client->exists($name)) {
|
||||
throw new EmptyRedis($name);
|
||||
}
|
||||
return $this->client->get($name);
|
||||
} catch (ConnectionException $exception) {
|
||||
throw new EmptyRedis($name, $exception);
|
||||
}
|
||||
}
|
||||
public function set(string $name, mixed $value, int $expirationTTL = 60 * 60 * 24): void
|
||||
{
|
||||
try {
|
||||
$this->client->set($name, $value, 'EX', $expirationTTL);
|
||||
} catch (ConnectionException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,15 @@ use Incoviba\Common\Ideal;
|
||||
use Incoviba\Common\Implement;
|
||||
use Incoviba\Model;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
|
||||
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)
|
||||
protected Repository\Venta\Factura\Estado\Tipo $tipoRepository,
|
||||
protected Service\Persona $personaService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
@ -57,7 +59,13 @@ class Factura extends Ideal\Service
|
||||
if ($factura !== null) {
|
||||
return $factura;
|
||||
}
|
||||
$factura = $this->facturaRepository->save($this->facturaRepository->create($data));
|
||||
list($data['cliente']['rut'], $data['cliente']['digito']) = explode('-', $data['cliente']['rut']);
|
||||
$data['cliente']['rut'] = (int) str_replace('.', '', $data['cliente']['rut']);
|
||||
$client = $this->personaService->add($data['cliente']);
|
||||
$data['cliente_rut'] = $client->rut;
|
||||
unset($data['cliente']);
|
||||
$factura = $this->facturaRepository->create($data);
|
||||
$factura = $this->facturaRepository->save($factura);
|
||||
$tipo = $this->tipoRepository->fetchByDescripcion('generada');
|
||||
$this->estadoRepository->save($this->estadoRepository->create([
|
||||
'factura_id' => $factura->id,
|
||||
|
Reference in New Issue
Block a user