Optimizacion de monedas

This commit is contained in:
Juan Pablo Vial
2024-05-16 21:23:24 -04:00
parent 256a3d2459
commit fa978728ce
5 changed files with 228 additions and 96 deletions

View File

@ -4,5 +4,6 @@ use Incoviba\Controller\API\Money;
$app->group('/money', function($app) {
$app->post('/ipc[/]', [Money::class, 'ipc']);
$app->post('/uf[/]', [Money::class, 'uf']);
$app->post('/many[/]', [Money::class, 'getMany']);
$app->post('[/]', [Money::class, 'get']);
});

View File

@ -1,5 +1,14 @@
@extends('layout.base')
@push('page_styles')
<style>
#data {
margin-left: 1rem;
margin-right: 1rem;
}
</style>
@endpush
@section('page_content')
<div class="ui container">
<h2 class="ui header">Matriz Facturación</h2>
@ -33,61 +42,150 @@
@push('page_scripts')
<script>
const money = {
data: {
ufs: {},
ipcs: {}
},
ufs: {},
ipcs: {},
sent: {
uf: {},
ipc: {}
},
get() {
return {
uf: date => {
if (typeof this.sent.uf[date.toISOString()] !== 'undefined') {
return this.sent.uf[date.toISOString()]
queue: {
size: 0,
array: () => {
const array = []
Object.entries(money.queue).forEach(([type, dates]) => {
if (['uf', 'ipc'].includes(type) === false) {
return
}
const url = '{{$urls->api}}/money/uf'
const data = new FormData()
data.set('fecha', date.toISOString())
const options = {
method: 'post',
body: data
if (type === 'uf') {
Object.entries(dates).forEach(([dateString, ventas]) => {
const temp = []
ventas.forEach(venta => {
temp.push(venta)
})
array.push({
type,
date: dateString,
ventas: temp
})
})
return
}
return this.sent.uf[date.toISOString()] = fetchAPI(url, options).then(response => {
if (response.ok) {
return response.json()
}
}).then(json => {
return this.ufs[json.input.fecha] = json.uf
})
},
ipc: ({end, start}) => {
const dateKey = [start.getFullYear(), (start.getMonth()+1), end.getFullYear(), (end.getMonth()+1)].join('-')
if (typeof this.sent.ipc[dateKey] !== 'undefined') {
return this.sent.ipc[dateKey]
}
let mult = 1
if (start > end) {
const tmp = structuredClone(end)
end = structuredClone(start)
start = tmp
mult = -1
}
const url = '{{$urls->api}}/money/ipc'
const data = new FormData()
data.set('start', [start.getFullYear() + ((start.getMonth() > 0) ? 0 : -1), start.getMonth(), start.getDate()].join('-'))
data.set('end', [end.getFullYear() + ((end.getMonth() > 0) ? 0 : -1), end.getMonth(), end.getDate()].join('-'))
const options = {
method: 'post',
body: data
}
return this.sent.ipc[dateKey] = fetchAPI(url, options).then(response => {
if (!response) {
return
}
return response.json().then(json => {
return this.ipcs[dateKey] = json.ipc * mult
Object.entries(dates).forEach(([dateString, data]) => {
const temp = []
data.ventas.forEach(venta => {
temp.push(venta)
})
array.push({
type,
start: data.start,
end: data.end,
ventas: temp
})
})
})
return array
}
},
enqueue() {
return {
uf: ({date, venta_id}) => {
const type = 'uf'
const data = {
date: [date.getFullYear(), date.getMonth()+1, date.getDate()].join('-'),
}
if (!(type in this.queue)) {
this.queue[type] = {}
}
if (!(data.date in this.queue[type])) {
this.queue[type][data.date] = []
}
if (this.queue[type][data.date].includes(data)) {
return
}
this.queue[type][data.date].push(venta_id)
this.queue['size'] ++
},
ipc: ({start, end, venta_id}) => {
const type = 'ipc'
let mult = 1
if (start > end) {
mult = -1
let temp = end
end = start
start = temp
}
const data = {
start: [start.getFullYear(), start.getMonth()+1, start.getDate()].join('-'),
end: [end.getFullYear(), end.getMonth()+1, end.getDate()].join('-'),
}
if (!(type in this.queue)) {
this.queue[type] = {}
}
if (!([data.start, data.end].join('|') in this.queue[type])) {
this.queue[type][[data.start, data.end].join('|')] = {
start: data.start,
end: data.end,
ventas: []
}
}
if (this.queue[type][[data.start, data.end].join('|')].ventas.includes(data)) {
return
}
this.queue[type][[data.start, data.end].join('|')].ventas.push({venta_id, mult})
this.queue['size'] ++
},
}
},
get() {
return {
many: () => {
const url = '{{$urls->api}}/money/many'
const method = 'post'
const chunkSize = 100
const promises = []
for (let i = 0; i < this.queue.size; i += chunkSize) {
const chunk = this.queue.array().slice(i, i + chunkSize)
const missing = chunk.filter(data => {
if (data.type === 'uf') {
return !(data.date in this.data.ufs)
}
if (data.type === 'ipc') {
const dateKey = [data.start, data.end].join('|')
return !(dateKey in this.data.ipcs)
}
})
if (missing.length === 0) {
continue
}
const body = new FormData()
body.set('dates', JSON.stringify(missing))
const options = {
method,
body
}
promises.push(fetchAPI(url, options).then(response => {
if (!response) {
return
}
return response.json().then(json => {
json.values.forEach(data => {
if (data.type === 'uf') {
this.data.ufs[data.date] = data.value
}
if (data.type === 'ipc') {
const dateKey = [data.start, data.end].join('|')
this.data.ipcs[dateKey] = data.value
}
})
return json.values
})
}))
}
return Promise.all(promises)
}
}
}
@ -152,13 +250,14 @@
const venta = this.principal.descripcion
const cantidad = this.unidades.length
this.unidades.forEach(unidad => {
const valor = (unidad.valor > 0) ? unidad.valor : unidad.precio
const values = [
'<a href="{{$urls->base}}/ventas/factura/' + this.id + '">' + venta + '</a>',
cantidad,
unidad.tipo,
unidad.descripcion,
dateFormatter.format(this.fecha),
ufFormatter.format(unidad.precio) + ' UF',
ufFormatter.format(valor) + ' UF',
unidad.prorrateo,
'',
'',
@ -173,21 +272,21 @@
if (this.escritura !== null) {
const ipc = (this.ipc >= 0) ? (1 + this.ipc) : 1 / (1 + this.ipc)
const descuento = valor_terreno * ipc * unidad.prorrateo
const precio_venta = unidad.precio * this.uf
const precio_venta = valor * this.uf
const precio_bruto = precio_venta - descuento
const precio_neto = precio_bruto / 1.19
const iva = precio_bruto - precio_neto
let i = 7
values[i++] = pesosFormatter.format(descuento)
values[i++] = pesosFormatter.format(precio_venta)
values[i++] = pesosFormatter.format(precio_bruto)
values[i++] = pesosFormatter.format(iva)
values[i++] = pesosFormatter.format(precio_neto)
values[i++] = (iva / precio_venta * 100).toFixed(2) + '%'
values[i++] = (!isNaN(descuento)) ? pesosFormatter.format(descuento) : '<div class="ui active inline loader"></div>'
values[i++] = (!isNaN(precio_venta)) ? pesosFormatter.format(precio_venta) : ''
values[i++] = (!isNaN(precio_bruto)) ? pesosFormatter.format(precio_bruto) : ''
values[i++] = (!isNaN(iva)) ? pesosFormatter.format(iva) : ''
values[i++] = (!isNaN(precio_neto)) ? pesosFormatter.format(precio_neto) : ''
values[i++] = (!isNaN(iva) && !isNaN(precio_venta)) ? (iva / precio_venta * 100).toFixed(2) + '%' : ''
values[i++] = dateFormatter.format(this.escritura)
values[i++] = ufFormatter.format(this.uf)
values[i++] = ufFormatter.format(this.ipc >= 0 ? this.ipc * 100 : -this.ipc * 100) + '%'
values[i++] = (!isNaN(this.uf)) ? ufFormatter.format(this.uf) : ''
values[i++] = (!isNaN(this.ipc)) ? (ufFormatter.format(this.ipc >= 0 ? this.ipc * 100 : -this.ipc * 100) + '%') : ''
}
const row = $('<tr></tr>')
values.forEach(value => {
@ -234,12 +333,26 @@
ventas.push(this.get().chunk({idx, chunk}))
}
Promise.all(ventas).then(() => {
const promises = []
promises.push(...this.get().ufs(idx))
promises.push(...this.get().ipcs(idx))
Promise.all(promises).then(() => {
this.draw().ventas(idx)
this.sent = false
money.get().many().then(response => {
response.forEach(dates => {
dates.forEach(values => {
values.ventas.forEach(venta => {
let vidx = 0
if (values.type === 'uf') {
vidx = this.data[idx].ventas.findIndex(v => v.id === venta)
this.data[idx].ventas[vidx].uf = values.value
return
}
if (values.type === 'ipc') {
vidx = this.data[idx].ventas.findIndex(v => v.id === venta.venta_id)
this.data[idx].ventas[vidx].ipc = values.value * venta.mult
}
})
})
})
this.draw().ventas(idx)
this.sent = false
})
})
})
@ -262,33 +375,6 @@
});
})
},
ufs: idx => {
const promises = []
Object.entries(this.queues.uf).forEach(([dateString, ventas]) => {
const date = new Date(dateString)
promises.push(money.get().uf(date).then(uf => {
ventas.forEach(id => {
const vidx = this.data[idx].ventas.findIndex(venta => venta.id === id)
this.data[idx].ventas[vidx].uf = uf
})
}))
})
return promises
},
ipcs: idx => {
const fecha_terreno = (typeof this.data[idx].terreno.date === 'undefined') ? new Date() : new Date(this.data[idx].terreno.date)
const promises = []
Object.entries(this.queues.ipc).forEach(([dateString, ventas]) => {
const date = new Date(dateString)
promises.push(money.get().ipc({end: date, start: fecha_terreno}).then(ipc => {
ventas.forEach(id => {
const vidx = this.data[idx].ventas.findIndex(venta => venta.id === id)
this.data[idx].ventas[vidx].ipc = ipc
})
}))
})
return promises
}
}
},
add() {
@ -296,10 +382,11 @@
venta: ({proyecto_idx, venta}) => {
const v = new Venta(venta)
this.data[proyecto_idx].ventas.push(v)
const fecha_terreno = (typeof this.data[proyecto_idx].terreno.fecha === 'undefined') ? new Date() : new Date(this.data[proyecto_idx].terreno.fecha)
if (v.escritura !== null) {
const dateString = v.escritura.toString()
this.register().uf({dateString, venta_id: v.id})
this.register().ipc({dateString, venta_id: v.id})
money.enqueue().uf({date: v.escritura, venta_id: v.id})
money.enqueue().ipc({start: v.escritura, end: fecha_terreno, venta_id: v.id})
}
},
}
@ -342,18 +429,17 @@
this.table = null
parent.html('')
}
const table = $('<table></table>').addClass('ui table')
const table = $('<table></table>').addClass('ui compact table')
table.append(this.draw().thead())
table.append(this.draw().tbody(proyecto_idx))
parent.show()
parent.html(table)
$(this.ids.proyectos).hide()
if (this.table === null) {
this.table = table.DataTable({
orders: [
[0, 'asc']
]
})
const dtD = structuredClone(datatables_defaults)
dtD['pageLength'] = 100
dtD['order'] = [[0, 'asc']]
this.table = table.DataTable(dtD)
}
},
thead: () => {

View File

@ -97,4 +97,45 @@ class Money
$output['ipc'] = $ipcService->get($start, $end);
return $this->withJson($response, $output);
}
public function getMany(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
Service\Money $moneyService): ResponseInterface
{
$input = $request->getParsedBody();
$output = [
'input' => $input,
'values' => []
];
$data = json_decode($input['dates']);
$ufs = [];
$ipcs = [];
try {
$ufs = (array) $this->fetchRedis($redisService, 'ufs');
} catch (EmptyRedis) {}
try {
$ipcs = (array) $this->fetchRedis($redisService, 'ipcs');
} catch (EmptyRedis) {}
foreach ($data as $date) {
if ($date->type === 'uf') {
if (!in_array($date->date, $ufs)) {
$uf = $moneyService->getUF(new DateTimeImmutable($date->date));
$ufs[$date->date] = $uf;
}
$date->value = $ufs[$date->date];
$output['values'] []= $date;
continue;
}
if ($date->type === 'ipc') {
$dateString = "{$date->start}|{$date->end}";
if (!in_array($dateString, $ipcs)) {
$ipc = $moneyService->getIPC(new DateTimeImmutable($date->start), new DateTimeImmutable($date->end));
$ipcs[$dateString] = $ipc;
}
$date->value = $ipcs[$dateString];
$output['values'] []= $date;
}
}
$this->saveRedis($redisService, 'ufs', $ufs, $this->time);
$this->saveRedis($redisService, 'ipcs', $ipcs, $this->time);
return $this->withJson($response, $output);
}
}

View File

@ -43,6 +43,9 @@ class Money
}
public function getIPC(DateTimeInterface $start, DateTimeInterface $end): float
{
if ($start >= $end) {
return 0;
}
try {
return $this->getProvider('ipc')->getVar($start, $end);
} catch (EmptyResponse) {

View File

@ -110,6 +110,7 @@ class Venta extends Service
'tipo' => ucwords($unidad->proyectoTipoUnidad->tipoUnidad->descripcion),
'descripcion' => $unidad->descripcion,
'prorrateo' => $unidad->prorrateo,
'valor' => $unidad->valor,
'precio' => (isset($unidad->currentPrecio)) ? $unidad->currentPrecio->valor : 0
];
}