Subir cartolas procesadas

This commit is contained in:
Juan Pablo Vial
2024-07-17 22:33:33 -04:00
parent 390e79ad6d
commit 8e7f43e487
48 changed files with 1692 additions and 530 deletions

View File

@ -52,8 +52,7 @@
<th class="right aligned">Cargo</th>
<th class="right aligned">Abono</th>
<th class="right aligned">Saldo</th>
<th>Centro de Costo</th>
<th>Detalle</th>
<th class="right aligned"></th>
<th>Orden</th>
</tr>
</thead>
@ -115,10 +114,85 @@
</div>
</div>
</div>
<div class="ui modal" id="edit_modal" data-cartola="">
<div class="header">
Movimiento
<div class="meta">
<span id="edit_inmobiliaria"></span> |
<span id="edit_cuenta"></span> |
<span id="edit_fecha"></span> |
<span id="edit_glosa"></span> |
<span id="edit_cargo"></span> |
<span id="edit_abono"></span> |
<span id="edit_saldo"></span>
</div>
</div>
<div class="content">
<form class="ui form" id="edit_form">
<input type="hidden" name="movimiento_id" value="" />
<div class="equal width fields">
<div class="field">
<label>Centro de Costo</label>
<div class="ui search selection dropdown">
<input type="hidden" name="centro_costo_id" />
<i class="dropdown icon"></i>
<div class="default text">Centro de Costo</div>
<div class="menu">
@foreach($centrosCostos as $centro)
<div class="item" data-value="{{$centro->id}}">
{{$centro->id}} - {{$centro->descripcion}}
</div>
@endforeach
</div>
</div>
</div>
<div class="field">
<label>Categoría</label>
<div class="ui input">
<input type="text" name="categoria" />
</div>
</div>
<div class="field">
<label>Detalle</label>
<div class="ui input">
<input type="text" name="detalle" />
</div>
</div>
</div>
<div class="equal width fields">
<div class="field">
<label>RUT</label>
<div class="ui right labeled input">
<input type="text" name="rut" />
<div class="ui basic label">-<span id="digito"></span></div>
</div>
</div>
<div class="field">
<label>Nombres</label>
<div class="ui input">
<input type="text" name="nombres" />
</div>
</div>
<div class="field">
<label>Identificador</label>
<div class="ui input">
<input type="text" name="identificador" />
</div>
</div>
</div>
</form>
</div>
<div class="actions">
<button class="ui approve button">
Editar
</button>
</div>
</div>
@endsection
@include('layout.head.styles.datatables')
@include('layout.body.scripts.datatables')
@include('layout.body.scripts.rut')
@push('page_scripts')
<script>
@ -126,7 +200,8 @@
idx
inmobiliaria = {
rut: 0,
razon: ''
razon: '',
sigla: ''
}
cuenta = {
id: 0,
@ -187,46 +262,6 @@
}
}
}
update() {
return {
centro: (idx, centro_id) => {
const id = this.movimientos[idx].id
const url = '{{$urls->api}}/contabilidad/movimiento/' + id + '/detalles'
const body = new FormData()
body.set('centro_id', centro_id)
return fetchAPI(url, {method: 'post', body}).then(response => {
if (!response) {
return
}
response.json().then(json => {
if (!json.status) {
return
}
const dropdown = $(".dropdown[data-idx='" + idx + "']")
dropdown.dropdown('set selected', json.centro.id, true)
})
})
},
detalle: (idx, detalle) => {
const id = this.movimientos[idx].id
const url = '{{$urls->api}}/contabilidad/movimiento/' + id + '/detalles'
const body = new FormData()
body.set('detalle', detalle)
return fetchAPI(url, {method: 'post', body}).then(response => {
if (!response) {
return
}
response.json().then(json => {
if (!json.status) {
return
}
const input = $("input[data-idx='" + idx + "']")
input.val(json.detalle)
})
})
}
}
}
draw() {
return {
form: ($form, inmobiliarias, first = false) => {
@ -326,6 +361,10 @@
onChange: (value, text, $choice) => {
this.inmobiliaria.rut = value
this.inmobiliaria.razon = text
const inmobiliaria = diaria.data.inmobiliarias.find(inmobiliaria => inmobiliaria.rut === parseInt(value))
if (typeof inmobiliaria !== 'undefined') {
this.inmobiliaria.sigla = inmobiliaria.sigla
}
this.get().bancos(value).then(() => {
$cuentas_dropdown.dropdown('change values', this.bancos)
})
@ -380,74 +419,42 @@
cartola: ($tbody, dateFormatter, numberFormatter) => {
this.movimientos.forEach((row, idx) => {
$tbody.append(
$('<tr></tr>').append(
'<td>' + this.inmobiliaria.razon + '</td>' + "\n"
+ '<td>' + this.cuenta.descripcion + '</td>' + "\n"
+ '<td>' + dateFormatter.format(row.fecha) + '</td>' + "\n"
+ '<td>' + row.glosa + '</td>' + "\n"
+ '<td class="right aligned">' + (row.cargo === 0 ? '' : numberFormatter.format(row.cargo)) + '</td>' + "\n"
+ '<td class="right aligned">' + (row.abono === 0 ? '' : numberFormatter.format(row.abono)) + '</td>' + "\n"
+ '<td class="right aligned">' + (row.saldo === 0 ? '' : numberFormatter.format(row.saldo)) + '</td>' + "\n"
).append(
$('<td></td>').append(
this.draw().centroCosto(idx)
)
).append(
$('<td></td>').append(
this.draw().detalle(idx)
)
).append(
$('<td></td>').html(idx + 1)
)
[
'<tr>',
`<td>${this.inmobiliaria.sigla ?? this.inmobiliaria.razon}</td>`,
`<td>${this.cuenta.descripcion}</td>`,
`<td>${dateFormatter.format(row.fecha)}</td>`,
`<td>${row.glosa}</td>`,
`<td class="right aligned">${row.cargo === 0 ? '' : numberFormatter.format(row.cargo)}</td>`,
`<td class="right aligned">${row.abono === 0 ? '' : numberFormatter.format(row.abono)}</td>`,
`<td class="right aligned">${row.saldo === 0 ? '' : numberFormatter.format(row.saldo)}</td>`,
'<td class="center aligned">',
`<button class="ui icon button edit_movimiento" data-movimiento="${idx}">`,
'<i class="edit icon"></i>',
'</button>',
'</td>',
`<td>${(idx + 1).toString()}</td>`,
].join("\n")
)
})
Array.from(document.getElementsByClassName('edit_movimiento')).forEach(button => {
button.addEventListener('click', function(clickEvent) {
const idx = clickEvent.currentTarget.dataset['movimiento']
const movimiento = this.movimientos[idx]
const $modal = $('#edit_modal')
$modal.attr('data-cartola', this.idx)
$modal.find('#edit_inmobiliaria').html(this.inmobiliaria.razon)
$modal.find('#edit_cuenta').html(this.cuenta.descripcion)
$modal.find('#edit_fecha').html(dateFormatter.format(movimiento.fecha))
$modal.find('#edit_glosa').html(movimiento.glosa)
$modal.find('#edit_cargo').html(numberFormatter.format(movimiento.cargo))
$modal.find('#edit_abono').html(numberFormatter.format(movimiento.abono))
$modal.find('#edit_saldo').html(numberFormatter.format(movimiento.saldo))
$modal.find('input[name="movimiento_id"]').val(movimiento.id)
$modal.modal('show')
}.bind(this))
})
},
centroCosto: idx => {
const centros = JSON.parse('{!! json_encode($centrosCostos) !!}')
const menu = $('<div></div>').addClass('menu')
centros.forEach(centro => {
menu.append(
'<div class="item" data-value="' + centro.id + '">'
+ centro.id + ' - ' + centro.descripcion
+ '</div>'
)
})
const dropdown = $('<div></div>').addClass('ui search selection dropdown').attr('data-idx', idx).html(
'<input type="hidden" name="centro" />' + "\n" +
'<i class="dropdown icon"></i>' + "\n" +
'<div class="default text">Centro de Costo</div>' + "\n"
).append(menu)
dropdown.dropdown({
onChange: (value, text, $element) => {
const idx = $element.parent().parent().data('idx')
this.update().centro(idx, value)
}
})
if (this.movimientos[idx].centro !== '') {
const cid = centros.findIndex(centro => centro.descripcion === this.movimientos[idx].centro)
dropdown.dropdown('set selected', centros[cid].id, true)
}
return dropdown
},
detalle: idx => {
const detalle = document.createElement('input')
detalle.type = 'text'
detalle.name = 'detalle' + idx
detalle.placeholder = 'Detalle'
detalle.setAttribute('data-idx', idx)
const input = document.createElement('div')
input.className = 'ui input'
input.appendChild(detalle)
if (this.movimientos[idx].detalle !== '') {
detalle.value = this.movimientos[idx].detalle
}
detalle.addEventListener('blur', event => {
const idx = event.currentTarget.dataset['idx']
this.update().detalle(idx, event.currentTarget.value)
})
return $(input)
}
}
}
}
@ -460,8 +467,7 @@
'cargo',
'abono',
'saldo',
'centro',
'detalle',
'edit',
'orden'
];
@endphp
@ -471,17 +477,18 @@
inmobiliarias: JSON.parse('{!! json_encode($inmobiliarias) !!}'),
cartolasIdx: [],
cartolas: [],
movimientos: [],
},
dataTableConfig: {
order: [[{{array_search('orden', $columns)}}, 'asc']],
columnDefs: [
{
targets: [{{implode(',', array_keys(array_filter($columns, function($column) {return in_array($column, ['fecha', 'cargo', 'abono', 'saldo']);})))}}],
width: '{{round((1/(count($columns) + 3 - 1)) * 100,2)}}%'
targets: [{{implode(',', array_keys(array_filter($columns, function($column) {return in_array($column, ['fecha', 'cargo', 'abono', 'saldo', 'edit']);})))}}],
width: '{{round((1/(count($columns) + 2)) * 100, 2)}}%'
},
{
targets: [{{implode(',', array_keys(array_filter($columns, function($column) {return in_array($column, ['sociedad', 'cuenta', 'glosa', 'centro', 'detalle']);})))}}],
width: '{{round((1/(count($columns) + 3 - 1)) * 2 * 100, 2)}}%'
targets: [{{implode(',', array_keys(array_filter($columns, function($column) {return in_array($column, ['sociedad', 'cuenta', 'glosa']);})))}}],
width: '{{round((1/(count($columns) + 2)) * 2 * 100, 2)}}%'
},
{
targets: [{{array_search('orden', $columns)}}],
@ -605,8 +612,12 @@
cargo: row.cargo,
abono: row.abono,
saldo: row.saldo,
centro: row.detalles?.centro_costo.descripcion ?? '',
detalle: row.detalles?.detalle ?? ''
centro: row.detalles?.centro_costo?.id ?? '',
detalle: row.detalles?.detalle ?? '',
categoria: row.detalles?.categoria ?? '',
rut: row.detalles?.rut ?? '',
nombres: row.detalles?.nombres ?? '',
identificador: row.detalles?.identificador ?? ''
}
})
const ayer = new Date(this.data.cartolas[cartolaIdx].fecha.getTime())
@ -638,6 +649,51 @@
$(this.ids.table.base).DataTable(this.dataTableConfig)
this.cartolas().add()
$(this.ids.editModal).modal({
onApprove: $element => {
const idx = $(this.ids.editModal).data('cartola')
const id = $(this.ids.editModal).find('input[name="movimiento_id"]').val()
const body = new FormData($(this.ids.editModal).find('form')[0])
body.set('idx', idx)
if (body.has('rut')) {
body.set('rut', body.get('rut').replace(/\D/g, ''))
body.set('digito', Rut.digitoVerificador(body.get('rut')))
}
const url = '{{$urls->api}}/contabilidad/movimiento/' + id + '/detalles'
return fetchAPI(url, {method: 'post', body}).then(response => {
if (!response) {
return
}
response.json().then(json => {
if (!json.status || json.movimiento === null || json.movimiento.detalles === null) {
return
}
const idx = diaria.data.cartolas.findIndex(cartola => cartola.idx = json.input.idx)
const movimiento = diaria.data.cartolas[idx].movimientos.find(movimiento => movimiento.id === id)
movimiento.centro = json.movimiento.detalles.centro_costo.id
movimiento.categoria = json.movimiento.detalles.categoria
movimiento.detalle = json.movimiento.detalles.detalle
movimiento.rut = json.movimiento.detalles.rut
movimiento.digito = json.movimiento.detalles.digito
movimiento.nombres = json.movimiento.detalles.nombres
movimiento.identificador = json.movimiento.detalles.identificador
})
})
}
})
$(this.ids.editModal).find('.dropdown').dropdown()
$(this.ids.editModal).find('[name="rut"]').on('input', function(event) {
const rut = event.currentTarget.value
const rut_clean = rut.replace(/\D/g, '')
event.currentTarget.value = Rut.format(rut_clean)
document.getElementById('digito').innerHTML = Rut.digitoVerificador(rut_clean)
}.bind(this))
const rut = $(this.ids.editModal).find('[name="rut"]')
const rut_clean = rut.val().replace(/\D/g, '')
rut.val(Rut.format(rut_clean))
document.getElementById('digito').innerHTML = Rut.digitoVerificador(rut_clean)
}
}
const manual = {
@ -807,7 +863,8 @@
add: '#add_button',
process: '#process_button'
},
loader: '#loader'
loader: '#loader',
editModal: '#edit_modal'
})
manual.setup({
modal: '#manual_modal',

View File

@ -0,0 +1,300 @@
@extends('layout.base')
@section('page_content')
<div class="ui container">
<h1 class="ui header">
Importar Cartola Diaria
</h1>
<div class="ui grid">
<div class="right aligned sixteen wide column">
<button class="ui green icon button" id="add_button">
Agregar
<i class="plus icon"></i>
</button>
</div>
</div>
<form class="ui form" id="cartola_form">
</form>
<div class="ui two columns grid">
<div class="column">
<button class="ui icon button" id="process_button">
<i class="file excel icon"></i>
Procesar
</button>
</div>
<div class="right aligned column">
<div class="ui inline active loader" id="loader"></div>
</div>
</div>
</div>
<table class="ui celled table" id="movimientos" style="display: none;">
<thead>
<tr>
<th>Fecha</th>
<th>Glosa</th>
<th class="right aligned">Cargo</th>
<th class="right aligned">Abono</th>
<th class="right aligned">Saldo</th>
<th class="center aligned">Centro de Costo</th>
<th>Categoría</th>
<th>Detalle</th>
<th>RUT</th>
<th>Nombres</th>
<th class="center aligned">Identificador</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
@endsection
@push('page_scripts')
<script>
class Cartola {
props
ids
constructor({index, ids}) {
this.props = {
index
}
this.ids = ids
}
get sociedad() {
return $(`#${this.ids.sociedad}${this.props.index}`).dropdown('get value')
}
get banco() {
return $(`#${this.ids.banco}${this.props.index}`).dropdown('get value')
}
get file() {
return $(`#archivo${this.props.index}`)[0].files[0]
}
get data() {
return {
sociedad_rut: this.sociedad,
banco_id: this.banco,
file: this.file,
index: this.props.index
}
}
draw({sociedades, bancos}) {
const output = [
`<div class="fields" data-idx="${this.props.index}">`,
'<div class="six wide field">',
'<label>Sociedad</label>',
`<div class="ui search selection dropdown" id="${this.ids.sociedad}${this.props.index}">`,
`<input type="hidden" name="sociedad_rut${this.props.index}" />`,
'<i class="dropdown icon"></i>',
'<div class="default text">Sociedad</div>',
'<div class="menu">',
...sociedades.map(sociedad => {
return [
`<div class="item" data-value="${sociedad.rut}">${sociedad.razon}</div>`
].join("\n")
}),
'</div>',
'</div>',
'</div>',
'<div class="two wide field">',
'<label>Banco</label>',
`<div class="ui search selection dropdown" id="${this.ids.banco}${this.props.index}">`,
`<input type="hidden" name="banco_id${this.props.index}" />`,
'<i class="dropdown icon"></i>',
'<div class="default text">Banco</div>',
'<div class="menu">',
...bancos.map(banco => {
return [
`<div class="item" data-value="${banco.id}">${banco.nombre}</div>`
].join("\n")
}),
'</div>',
'</div>',
'</div>',
'<div class="six wide field">',
'<label>Archivo</label>',
'<div class="ui file action input">',
`<input type="file" name="archivo${this.props.index}" id="archivo${this.props.index}" />`,
'<div class="ui icon button" id="archivo_button">',
'<i class="file icon"></i>',
'Seleccionar',
'</div>',
'</div>',
'</div>',
]
if (this.props.index > 1) {
output.push(...[
'<div class="field">',
'<label>&nbsp;</label>',
`<button class="ui red icon button remove" data-idx="${this.props.index}">`,
'<i class="trash icon"></i>',
'</button>',
'</div>',
])
}
output.push('</div>')
return output.join("\n")
}
activate() {
$(`#${this.ids.sociedad}${this.props.index}`).dropdown()
$(`#${this.ids.banco}${this.props.index}`).dropdown()
if (this.props.index > 1) {
$(`.${this.ids.buttons.remove}[data-idx="${this.props.index}"]`).click(clickEvent => {
const index = $(clickEvent.currentTarget).data('idx')
this.remove(index)
})
}
}
remove(idx) {
$(`.fields[data-idx=${idx}]`).remove()
cartolas.data.cartolas = cartolas.data.cartolas.filter(cartola => cartola.props.index !== idx)
cartolas.draw().form()
}
}
class Movimiento {
props
constructor({fecha, glosa, cargo, abono, saldo, categoria, detalle, centro_costo, rut, nombres, identificador}) {
this.props = {
fecha,
glosa,
cargo,
abono,
saldo,
categoria,
detalle,
centro_costo,
rut,
nombres,
identificador
}
}
draw({formatters}) {
const fecha = new Date(this.props.fecha)
return [
'<tr>',
`<td>${formatters.date.format(fecha)}</td>`,
`<td>${this.props.glosa}</td>`,
`<td class="right aligned">${formatters.number.format(this.props.cargo ?? 0)}</td>`,
`<td class="right aligned">${formatters.number.format(this.props.abono ?? 0)}</td>`,
`<td class="right aligned">${formatters.number.format(this.props.saldo)}</td>`,
`<td class="center aligned">${this.props.centro_costo ?? ''}</td>`,
`<td>${this.props.categoria ?? ''}</td>`,
`<td>${this.props.detalle ?? ''}</td>`,
`<td>${this.props.rut ?? ''}</td>`,
`<td>${this.props.nombres ?? ''}</td>`,
`<td class="center aligned">${this.props.identificador ?? ''}</td>`,
'</tr>'
].join("\n")
}
}
const cartolas = {
ids: {},
data: {
sociedades: {!! json_encode($inmobiliarias) !!},
bancos: {!! json_encode($bancos) !!},
cartolas: [],
movimientos: [],
},
formatters: {
number: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 0, maximumFractionDigits: 0}),
date: new Intl.DateTimeFormat('es-CL', {dateStyle: 'short', timeStyle: 'short'})
},
add() {
return {
cartola: () => {
const idx = cartolas.data.cartolas.length + 1
const cartola = new Cartola({index: idx, ids: cartolas.ids.cartolas})
cartolas.data.cartolas.push(cartola)
cartolas.draw().form()
}
}
},
draw() {
return {
form: () => {
const form = $(this.ids.form)
form.empty()
form.append(`<input type="hidden" name="cartolas" value="${this.data.cartolas.length}" />`)
this.data.cartolas.forEach(cartola => {
form.append(cartola.draw({sociedades: this.data.sociedades, bancos: this.data.bancos}))
cartola.activate()
})
},
movimientos: () => {
const table = $(this.ids.movimientos)
const tbody = table.find('tbody')
tbody.empty()
this.data.movimientos.forEach(movimiento => {
tbody.append(movimiento.draw({formatters: this.formatters}))
})
table.show()
}
}
},
import() {
return {
cartolas: () => {
const url = '{{$urls->api}}/contabilidad/cartolas/importar'
const method = 'post'
const body = new FormData()
this.data.cartolas.forEach(cartola => {
Object.entries(cartola.data).forEach(([key, value]) => {
body.append(key, value)
})
})
fetchAPI(url, {method, body}).then(response => {
if (!response) {
return
}
return response.json().then(data => {
this.data.movimientos = data.movimientos.map(movimiento => new Movimiento(movimiento))
this.draw().movimientos()
})
}).catch(error => {
const table = $(this.ids.movimientos)
const tbody = table.find('tbody')
tbody.empty()
table.hide()
}).finally(() => {
$(this.ids.loader).hide()
})
}
}
},
setup(ids) {
this.ids = ids
this.add().cartola()
$(this.ids.buttons.add).click(() => {
this.add().cartola()
})
$(this.ids.loader).hide()
$(this.ids.form).submit(submitEvent => {
submitEvent.preventDefault()
$(this.ids.loader).show()
this.import().cartolas()
return false
})
$(this.ids.buttons.process).click(() => {
$(this.ids.form).submit()
})
}
}
$(document).ready(() => {
cartolas.setup({
form: '#cartola_form',
buttons: {
add: '#add_button',
process: '#process_button'
},
movimientos: '#movimientos',
loader: '#loader',
cartolas: {
sociedad: 'sociedad',
banco: 'banco',
buttons: {
remove: 'remove'
}
}
})
})
</script>
@endpush

View File

@ -0,0 +1,53 @@
@extends('layout.base')
@section('page_content')
<div class="ui container">
<h1 class="ui header">Importar Informe de Tesorería</h1>
<form class="ui form" id="import_form" enctype="multipart/form-data">
<div class="ten wide field">
<label>Informe</label>
<div class="ui file action input">
<input type="file" name="file[]" id="file" accept=".xlsx" multiple placeholder="Informe" />
<label for="file" class="ui icon button">
<i class="file icon"></i>
</label>
</div>
</div>
<button class="ui icon button">
<i class="arrow up icon"></i>
Subir
</button>
</form>
</div>
@endsection
@push('page_scripts')
<script>
$(document).ready(() => {
$('#import_form').submit(submitEvent => {
submitEvent.preventDefault()
let formData = new FormData()
formData.append('file', $('#file')[0].files[0])
const url = '{{$urls->api}}/contabilidad/tesoreria/import'
const method = 'post'
const body = new FormData(submitEvent.currentTarget)
fetchAPI(url, {method, body}).then(response => {
if (!response) {
return
}
return response.json().then(data => {
console.log(data)
})
}).catch(error => {
console.error(error)
})
return false
})
})
</script>
@endpush

View File

@ -16,7 +16,13 @@
<a class="item" href="{{$urls->base}}/contabilidad/centros_costos/asignar">Asignar en Cartola</a>
</div>
</div>
<a class="item" href="{{$urls->base}}/contabilidad/cartolas/diaria">Cartola Diaria</a>
<div class="item">
<i class="dropdown icon"></i>
<a class="text" href="{{$urls->base}}/contabilidad/cartolas/diaria">Cartola Diaria</a>
<div class="menu">
<a class="item" href="{{$urls->base}}/contabilidad/cartolas/importar">Importar</a>
</div>
</div>
<a class="item" href="{{$urls->base}}/contabilidad/depositos">Depósitos a Plazo</a>
<a class="item" href="{{$urls->base}}/contabilidad/movimientos">Movimientos</a>
</div>

View File

@ -15,7 +15,7 @@
if (rut.length === 0) {
return ''
}
rut.replace(/\./g, '')
rut.replace(/\D/g, '')
return rut.replace(/^(\d{1,2})(\d{3})(\d{3})$/, '$1.$2.$3')
}
static validar(rut, digito) {