Compare commits
58 Commits
032d00fbb6
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| fd357ca1c1 | |||
| d89c1b5815 | |||
| 423f8ddf4c | |||
| 9ed918ee03 | |||
| 5f348e954e | |||
| c9056451eb | |||
| 07a1607554 | |||
| a0019b51a5 | |||
| 49f5aa21f9 | |||
| 111c7df6b4 | |||
| 2b120a3bf5 | |||
| caf0dfe4ab | |||
| a6037e4e14 | |||
| 539ac3c0e8 | |||
| c3a7e7636f | |||
| a41f306d3f | |||
| 81ac48afbf | |||
| 54969eeeab | |||
| e278695137 | |||
| c99690e7b6 | |||
| bc5967b2be | |||
| 4760b08673 | |||
| c254ac11fe | |||
| f440d771db | |||
| 270a07bb77 | |||
| f8ea44460d | |||
| 59115bd631 | |||
| 2edcdacbe0 | |||
| ebaf708f79 | |||
| 14d75b1ce9 | |||
| ab60c9db1b | |||
| 04a725e517 | |||
| 6e32b1debc | |||
| 5e89e9c830 | |||
| ecbff7bfcd | |||
| e4100e7f21 | |||
| b75c4b7efc | |||
| 9794070925 | |||
| 1ca087e19b | |||
| 4bd3c7af9b | |||
| 815d81c9c3 | |||
| 434c1c6d06 | |||
| b986e63cc0 | |||
| 74c6347367 | |||
| 5ea300837e | |||
| 71309e13a0 | |||
| beb73ba6df | |||
| 06558d778d | |||
| 538833a5a4 | |||
| 7bee4e3bb4 | |||
| 0c640d6cab | |||
| 6d871d77fe | |||
| b0267320a1 | |||
| 6ddc48ec60 | |||
| 331ee1e584 | |||
| 24c17debf3 | |||
| 552fd0aa06 | |||
| 60faf293d4 |
@ -2,6 +2,7 @@
|
||||
namespace Incoviba\Common\Define\Cartola;
|
||||
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
|
||||
interface Banco
|
||||
{
|
||||
@ -9,6 +10,7 @@ interface Banco
|
||||
* Process bank movements for database inserts
|
||||
* @param UploadedFileInterface $file
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
public function process(UploadedFileInterface $file): array;
|
||||
|
||||
|
||||
@ -13,4 +13,5 @@ interface Provider
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float;
|
||||
public function supported(string $money_symbol): bool;
|
||||
}
|
||||
|
||||
@ -3,10 +3,16 @@ namespace Incoviba\Common\Ideal\Cartola;
|
||||
|
||||
use Incoviba\Common\Define;
|
||||
use Incoviba\Common\Ideal\Service;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
abstract class Banco extends Service implements Define\Cartola\Banco
|
||||
{
|
||||
/**
|
||||
* @param UploadedFileInterface $file
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
public function process(UploadedFileInterface $file): array
|
||||
{
|
||||
$filename = $this->processUploadedFile($file);
|
||||
@ -40,6 +46,7 @@ abstract class Banco extends Service implements Define\Cartola\Banco
|
||||
* Process the temp file from getFilename and remove it
|
||||
* @param string $filename
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
protected function processFile(string $filename): array
|
||||
{
|
||||
@ -88,6 +95,7 @@ abstract class Banco extends Service implements Define\Cartola\Banco
|
||||
* Translate uploaded file data to database data
|
||||
* @param string $filename
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
abstract protected function parseFile(string $filename): array;
|
||||
}
|
||||
|
||||
16
app/common/Ideal/Money/Provider.php
Normal file
16
app/common/Ideal/Money/Provider.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Incoviba\Common\Ideal\Money;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Define;
|
||||
|
||||
abstract class Provider implements Define\Money\Provider
|
||||
{
|
||||
public function __construct(protected LoggerInterface $logger) {}
|
||||
|
||||
protected array $supportedMap = [];
|
||||
public function supported(string $money_symbol): bool
|
||||
{
|
||||
return in_array(strtolower($money_symbol), array_keys($this->supportedMap), true);
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,8 @@ abstract class Repository implements Define\Repository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Define\Model
|
||||
* @throws EmptyResult
|
||||
*/
|
||||
public function fetchById(int $id): Define\Model
|
||||
|
||||
@ -35,11 +35,11 @@ abstract class API extends Ideal\Service
|
||||
|
||||
/**
|
||||
* @param Define\Model $model
|
||||
* @param array $new_data
|
||||
* @param array $newData
|
||||
* @return Define\Model
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
abstract public function edit(Define\Model $model, array $new_data): Define\Model;
|
||||
abstract public function edit(Define\Model $model, array $newData): Define\Model;
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
|
||||
@ -10,21 +10,23 @@ class Exception implements ProcessorInterface
|
||||
public function __invoke(LogRecord $record): LogRecord
|
||||
{
|
||||
$context = $record->context;
|
||||
foreach ($context as $key => $value) {
|
||||
if (is_a($value, Throwable::class)) {
|
||||
$exception = $value;
|
||||
$output = $this->processException($exception);
|
||||
$context[$key] = $output;
|
||||
$new_record = new LogRecord(
|
||||
$record->datetime,
|
||||
$record->channel,
|
||||
$record->level,
|
||||
$record->message,
|
||||
$context,
|
||||
$record->extra
|
||||
);
|
||||
$record = $new_record;
|
||||
$changed = false;
|
||||
array_walk_recursive($context, function (&$item) use (&$changed) {
|
||||
if (is_a($item, Throwable::class)) {
|
||||
$item = $this->processException($item);
|
||||
$changed = true;
|
||||
}
|
||||
});
|
||||
if ($changed) {
|
||||
$new_record = new LogRecord(
|
||||
$record->datetime,
|
||||
$record->channel,
|
||||
$record->level,
|
||||
$record->message,
|
||||
$context,
|
||||
$record->extra
|
||||
);
|
||||
$record = $new_record;
|
||||
}
|
||||
if (is_a($record->message, Throwable::class)) {
|
||||
$exception = $record->message;
|
||||
@ -56,10 +58,10 @@ class Exception implements ProcessorInterface
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'trace' => $exception->getTraceAsString(),
|
||||
'trace' => $exception->getTrace(),
|
||||
];
|
||||
if ($exception->getPrevious() !== null) {
|
||||
$output['previous'] = $this->processException($exception);
|
||||
$output['previous'] = $this->processException($exception->getPrevious());
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ $app->group('/ventas', function($app) {
|
||||
include_once $file->getRealPath();
|
||||
}
|
||||
$app->get('/add[/]', [Ventas::class, 'add']);
|
||||
$app->post('/add[/]', [Ventas::class, 'add']);
|
||||
$app->get('[/]', Ventas::class);
|
||||
})->add($app->getContainer()->get(Incoviba\Middleware\Authentication::class));
|
||||
$app->group('/venta/{proyecto_nombre:[A-za-zÑñ\+\ %0-9]+}/{unidad_descripcion:[0-9]+}', function($app) {
|
||||
|
||||
@ -338,6 +338,7 @@
|
||||
data.errors.forEach(errorData => {
|
||||
console.error(errorData)
|
||||
})
|
||||
throw Error('Error al importar cartolas')
|
||||
}
|
||||
this.data.movimientos = data.movimientos.map(movimiento => new Movimiento(movimiento))
|
||||
this.draw().movimientos()
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
@section('page_content')
|
||||
<div class="ui container">
|
||||
<h4 class="ui header">Bienvenid@ {{$user->name}}</h4>
|
||||
<h4 class="ui header">Bienvenid@ {{ $user->name }}</h4>
|
||||
<div class="ui basic fitted segment">
|
||||
<span id="cuotas_hoy"></span>
|
||||
<span id="cuotas_pendientes"></span>
|
||||
|
||||
4
app/resources/views/layout/body/scripts/xlsx.blade.php
Normal file
4
app/resources/views/layout/body/scripts/xlsx.blade.php
Normal file
@ -0,0 +1,4 @@
|
||||
@push('page_scripts')
|
||||
<!-- use version 0.20.3 -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
|
||||
@endpush
|
||||
@ -5,6 +5,6 @@
|
||||
@else
|
||||
<title>Incoviba</title>
|
||||
@endif
|
||||
<link rel="icon" href="{{$urls->images}}/Isotipo 16.png" />
|
||||
<link rel="icon" href="{{ $urls->images }}/Isotipo 16.png" />
|
||||
@include('layout.head.styles')
|
||||
</head>
|
||||
|
||||
@ -33,12 +33,12 @@
|
||||
<label for="proyecto">Proyecto</label>
|
||||
<div class="four wide field">
|
||||
<div class="ui fluid search selection dropdown" id="proyecto">
|
||||
<input type="hidden" name="proyecto" />
|
||||
<input type="hidden" name="proyecto" value="{{ $proyecto_id ?? '' }}" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Proyecto</div>
|
||||
<div class="menu">
|
||||
@foreach($proyectos as $proyecto)
|
||||
<div class="item" data-value="{{$proyecto->id}}">{{$proyecto->descripcion}}</div>
|
||||
<div class="item {{ (isset($proyecto_id) && $proyecto_id == $proyecto->id) ? 'active selected' : '' }}" data-value="{{$proyecto->id}}">{{$proyecto->descripcion}}</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@ -68,7 +68,7 @@
|
||||
<label for="valor">Valor</label>
|
||||
<div class="inline field">
|
||||
<div class="ui right labeled input">
|
||||
<input type="text" name="valor" id="valor" required />
|
||||
<input type="text" name="valor" id="valor" value="{{ $valor ?? '' }}" required />
|
||||
<div class="ui label">UF</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -145,7 +145,7 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button class="ui button" type="submit">Agregar</button>
|
||||
<button class="ui button" type="submit" id="submit_button">Agregar</button>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@ -255,14 +255,18 @@
|
||||
}
|
||||
class Comuna {
|
||||
id
|
||||
data
|
||||
data = {
|
||||
region: 0,
|
||||
provincias: []
|
||||
}
|
||||
propietario
|
||||
promises = {
|
||||
provincias: null,
|
||||
comunas: {}
|
||||
}
|
||||
|
||||
constructor(id) {
|
||||
this.id = id
|
||||
this.data = {
|
||||
region: 0,
|
||||
provincias: []
|
||||
}
|
||||
|
||||
$(this.id).dropdown({
|
||||
forceSelection: true
|
||||
@ -272,13 +276,23 @@
|
||||
get() {
|
||||
return {
|
||||
provincias: () => {
|
||||
if (this.data.region in this.propietario.data.comunas) {
|
||||
this.promises.provincias = new Promise(resolve => resolve(this.propietario.data.comunas[this.data.region])).then(data => {
|
||||
this.data.provincias = data.provincias
|
||||
this.draw().comunas()
|
||||
})
|
||||
return this.promises.provincias
|
||||
}
|
||||
this.propietario.data.comunas[this.data.region] = {region: this.data.region, provincias: []}
|
||||
const uri = '{{$urls->api}}/region/' + this.data.region + '/provincias'
|
||||
return fetchAPI(uri).then(response => {
|
||||
this.promises.provincias = APIClient.fetch(uri).then(response => {
|
||||
this.promises.provincias = null
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
}).then(data => {
|
||||
this.data.provincias = data.provincias
|
||||
this.propietario.data.comunas[this.data.region].provincias = data.provincias
|
||||
const promises = []
|
||||
this.data.provincias.forEach(provincia => {
|
||||
promises.push(this.get().comunas(provincia.id))
|
||||
@ -287,17 +301,21 @@
|
||||
this.draw().comunas()
|
||||
})
|
||||
})
|
||||
return this.data.provincias
|
||||
},
|
||||
comunas: provincia_id => {
|
||||
const uri = '{{$urls->api}}/provincia/' + provincia_id + '/comunas'
|
||||
return fetchAPI(uri).then(response => {
|
||||
this.promises.comunas[provincia_id] = fetchAPI(uri).then(response => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
}).then(data => {
|
||||
this.promises.comunas[data.provincia_id] = null
|
||||
const i = this.data.provincias.findIndex(provincia => provincia.id === data.provincia_id)
|
||||
this.data.provincias[i].comunas = data.comunas
|
||||
this.propietario.data.comunas[this.data.region].provincias[i].comunas = data.comunas
|
||||
})
|
||||
return this.promises.comunas[provincia_id]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -347,6 +365,11 @@
|
||||
}
|
||||
class PersonaNatural {
|
||||
valid
|
||||
parent
|
||||
components = {
|
||||
rut: null,
|
||||
region: null
|
||||
}
|
||||
constructor({valid}) {
|
||||
this.valid = valid
|
||||
}
|
||||
@ -418,9 +441,10 @@
|
||||
return lines.join("\n")
|
||||
}
|
||||
activate() {
|
||||
new RutHandler({id: '#rut', alert_id: '#alert_rut', valid: this.valid})
|
||||
this.components.rut = new RutHandler({id: '#rut', alert_id: '#alert_rut', valid: this.valid})
|
||||
const comuna = new Comuna('#comuna')
|
||||
new Region({id: '#region', comuna})
|
||||
comuna.propietario = this.parent
|
||||
this.components.region = new Region({id: '#region', comuna})
|
||||
}
|
||||
}
|
||||
class PersonaTributaria {
|
||||
@ -572,6 +596,15 @@
|
||||
}
|
||||
class Propietario {
|
||||
ids
|
||||
components = {
|
||||
persona: null
|
||||
}
|
||||
data = {
|
||||
comunas: {}
|
||||
}
|
||||
promises = {
|
||||
propietario: null
|
||||
}
|
||||
|
||||
constructor({id, id_tipo, id_cantidad}) {
|
||||
this.ids = {
|
||||
@ -591,8 +624,6 @@
|
||||
document.getElementById(this.ids.cantidad).disabled = !document.getElementById(this.ids.tipo).checked
|
||||
|
||||
this.draw()
|
||||
|
||||
$(this.ids.span).find('#rut')
|
||||
}
|
||||
get tipo() {
|
||||
return document.getElementById(this.ids.tipo).checked ? (document.getElementById(this.ids.cantidad).checked ? 2 : 0) : 1
|
||||
@ -613,7 +644,8 @@
|
||||
return {
|
||||
propietario: rut => {
|
||||
const uri = '{{$urls->api}}/ventas/propietario/' + rut.split('-')[0]
|
||||
return fetchAPI(uri).then(response => {
|
||||
this.promises.propietario = APIClient.fetch(uri).then(response => {
|
||||
this.promises.propietario = null
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
@ -627,7 +659,18 @@
|
||||
parent.find("[name='numero']").val(data.propietario.direccion.numero)
|
||||
parent.find("[name='extra']").val(data.propietario.direccion.extra)
|
||||
parent.find('#region').dropdown('set selected', data.propietario.direccion.comuna.provincia.region.id)
|
||||
parent.find('#comuna').dropdown('set selected', data.propietario.direccion.comuna.id)
|
||||
let persona = this.components.persona
|
||||
if (!('components' in persona)) {
|
||||
persona = persona.persona
|
||||
}
|
||||
const promise = persona.components.region.comuna.promises.provincias
|
||||
if (promise === null) {
|
||||
parent.find('#comuna').dropdown('set selected', data.propietario.direccion.comuna.id)
|
||||
} else {
|
||||
promise.then(() => {
|
||||
parent.find('#comuna').dropdown('set selected', data.propietario.direccion.comuna.id)
|
||||
})
|
||||
}
|
||||
parent.find("[name='email']").val(data.propietario.email)
|
||||
parent.find("[name='telefono']").val(data.propietario.telefono)
|
||||
|
||||
@ -645,6 +688,8 @@
|
||||
}
|
||||
draw() {
|
||||
const propietario = this.get().propietario(this.tipo)
|
||||
this.components.persona = propietario
|
||||
propietario.parent = this
|
||||
$(this.ids.span).html(propietario.draw())
|
||||
propietario.activate()
|
||||
}
|
||||
@ -655,6 +700,9 @@
|
||||
data
|
||||
unidades
|
||||
added
|
||||
promises = {
|
||||
unidades: null
|
||||
}
|
||||
|
||||
constructor({unidades_id, proyecto_id}) {
|
||||
this.ids = {
|
||||
@ -702,7 +750,8 @@
|
||||
return {
|
||||
unidades: () => {
|
||||
const uri = '{{$urls->api}}/proyecto/' + this.data.id + '/unidades'
|
||||
return fetchAPI(uri).then(response => {
|
||||
this.promises.unidades = APIClient.fetch(uri).then(response => {
|
||||
this.promises.unidades = null
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
@ -712,6 +761,7 @@
|
||||
}
|
||||
this.unidades = data.unidades
|
||||
})
|
||||
return this.promises.unidades
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -733,6 +783,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
return unidad
|
||||
}
|
||||
remove(number) {
|
||||
number = parseInt(number)
|
||||
@ -750,6 +801,9 @@
|
||||
}
|
||||
class Unidad{
|
||||
number
|
||||
components = {
|
||||
dropdown: null
|
||||
}
|
||||
constructor({number}) {
|
||||
this.number = number
|
||||
}
|
||||
@ -772,12 +826,16 @@
|
||||
})
|
||||
dropdown.append(menu)
|
||||
dropdown.dropdown()
|
||||
this.components.dropdown = dropdown
|
||||
return dropdown
|
||||
}
|
||||
}
|
||||
|
||||
class Payment {
|
||||
ids
|
||||
components = {
|
||||
checkbox: null
|
||||
}
|
||||
|
||||
constructor({id, checkbox_id}) {
|
||||
this.ids = {
|
||||
@ -785,7 +843,8 @@
|
||||
checkbox: checkbox_id
|
||||
}
|
||||
|
||||
document.getElementById(this.ids.checkbox).onchange = event => {
|
||||
this.components.checkbox = document.getElementById(this.ids.checkbox)
|
||||
this.components.checkbox.onchange = event => {
|
||||
this.toggle()
|
||||
}
|
||||
this.toggle()
|
||||
@ -807,33 +866,138 @@
|
||||
console.error(errors)
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
const cdo = structuredClone(calendar_date_options)
|
||||
cdo['maxDate'] = new Date()
|
||||
$('#fecha_venta_calendar').calendar(cdo)
|
||||
new Propietario({id: '#propietario', id_tipo: 'persona_propietario', id_cantidad: 'cantidad_propietario'})
|
||||
new Proyecto({unidades_id: '#unidades', proyecto_id: '#proyecto'})
|
||||
const venta = {
|
||||
ids: {
|
||||
form: ''
|
||||
},
|
||||
components: {
|
||||
$date: null,
|
||||
project: null,
|
||||
buyer: null,
|
||||
payments: {},
|
||||
$form: null
|
||||
},
|
||||
fill: {
|
||||
reservation: () => {
|
||||
venta.fill.date()
|
||||
venta.fill.buyer()
|
||||
venta.fill.project()
|
||||
venta.fill.payments()
|
||||
},
|
||||
date: () => {
|
||||
const dateString = '{{ isset($date) ? ($date?->format('Y-m-d') ?? '') : '' }}'
|
||||
if (dateString === '') {
|
||||
return;
|
||||
}
|
||||
const date = new Date(dateString)
|
||||
date.setDate(date.getDate() + 1)
|
||||
venta.components.$date.calendar('set date', date)
|
||||
},
|
||||
buyer: () => {
|
||||
const fullRut = '{{ isset($propietario) ? $propietario['full'] : '' }}'
|
||||
if (fullRut === '') {
|
||||
return;
|
||||
}
|
||||
const buyer = $(venta.ids.buyerId)
|
||||
const rut = buyer.find('#rut')
|
||||
rut.val(fullRut)
|
||||
let persona = venta.components.buyer.components.persona
|
||||
if (!('components' in persona)) {
|
||||
persona = persona.persona
|
||||
}
|
||||
const promise = persona.components.region.comuna.promises.provincias
|
||||
if (promise === null) {
|
||||
venta.fill.buyerRut(buyer, rut)
|
||||
return;
|
||||
}
|
||||
promise.then(() => {
|
||||
venta.fill.buyerRut(buyer, rut)
|
||||
})
|
||||
},
|
||||
buyerRut: (buyer, rut) => {
|
||||
rut.trigger('change')
|
||||
const promise = venta.components.buyer.promises.propietario
|
||||
if (promise === null) {
|
||||
venta.fill.buyerData(buyer)
|
||||
return;
|
||||
}
|
||||
promise.then(() => {
|
||||
venta.fill.buyerData(buyer)
|
||||
})
|
||||
},
|
||||
buyerData: buyer => {
|
||||
const email = buyer.find('[name="email"]')
|
||||
if (email.val() === '') {
|
||||
email.val('{{ $propietario['email'] ?? '' }}')
|
||||
}
|
||||
const telefono = buyer.find('[name="telefono"]')
|
||||
if (telefono.val() === '') {
|
||||
telefono.val('{{ $propietario['telefono'] ?? '' }}')
|
||||
}
|
||||
},
|
||||
project: () => {
|
||||
const projectId = {{ isset($proyecto_id) ? $proyecto_id : 0 }};
|
||||
if (projectId === 0) {
|
||||
return;
|
||||
}
|
||||
$(venta.ids.projectId).dropdown('set selected', projectId);
|
||||
|
||||
const payments = [
|
||||
'pie',
|
||||
'subsidio',
|
||||
'credito',
|
||||
'bono_pie'
|
||||
]
|
||||
payments.forEach(payment => {
|
||||
new Payment({
|
||||
id: '#' + payment,
|
||||
checkbox_id: 'has_' + payment
|
||||
})
|
||||
})
|
||||
|
||||
$('#add_form').submit(event => {
|
||||
event.preventDefault()
|
||||
const button = $(event.currentTarget).find(".ui.button[type='submit']")
|
||||
button.prop('disabled', true)
|
||||
button.css('cursor', 'wait')
|
||||
const body = new FormData(event.currentTarget)
|
||||
const unidades = event.currentTarget.querySelectorAll("[name^='unidad']")
|
||||
const promise = venta.components.project.promises.unidades
|
||||
if (promise === null) {
|
||||
venta.fill.units()
|
||||
return;
|
||||
}
|
||||
promise.then(() => {
|
||||
venta.fill.units()
|
||||
})
|
||||
},
|
||||
units: () => {
|
||||
const unitString = '{!! isset($unidades) ? json_encode($unidades) : '' !!}';
|
||||
if (unitString === '') {
|
||||
return;
|
||||
}
|
||||
const unitTypes = JSON.parse(unitString)
|
||||
Object.entries(unitTypes).forEach(([type, units]) => {
|
||||
units.forEach(unit_id => {
|
||||
const unit = venta.components.project.add(type)
|
||||
unit.components.dropdown.dropdown('set selected', unit_id)
|
||||
})
|
||||
})
|
||||
},
|
||||
payments: () => {
|
||||
const formaPagoString = '{!! isset($forma_pago) ? json_encode($forma_pago) : '' !!}';
|
||||
if (formaPagoString === '') {
|
||||
return;
|
||||
}
|
||||
const formaPago = JSON.parse(formaPagoString)
|
||||
Object.entries(formaPago).forEach(([key, value]) => {
|
||||
const payment = venta.components.payments[key]
|
||||
const event = new Event('change', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
payment.components.checkbox.dispatchEvent(event)
|
||||
if (key in venta.fill.payment) {
|
||||
venta.fill.payment[key](value)
|
||||
return
|
||||
}
|
||||
console.debug(`No fill defined for payment ${key}`)
|
||||
})
|
||||
},
|
||||
payment: {
|
||||
pie: data => {
|
||||
document.querySelector('[name="pie"]').value = data.valor
|
||||
document.querySelector('[name="cuotas"]').value = data.cuotas
|
||||
},
|
||||
credito: data => {
|
||||
document.querySelector('[name="credito"]').value = data
|
||||
}
|
||||
}
|
||||
},
|
||||
submit: form => {
|
||||
const body = new FormData(form)
|
||||
const unidades = form.querySelectorAll("[name^='unidad']")
|
||||
const emptyUnidades = []
|
||||
unidades.forEach(unidad => {
|
||||
if (unidad.value.trim() === '') {
|
||||
@ -846,7 +1010,7 @@
|
||||
return;
|
||||
}
|
||||
const uri = '{{$urls->api}}/ventas/add'
|
||||
return fetchAPI(uri, {method: 'post', body}).then(response => {
|
||||
return APIClient.fetch(uri, {method: 'post', body}).then(response => {
|
||||
if (!response) {
|
||||
return false
|
||||
}
|
||||
@ -861,6 +1025,74 @@
|
||||
return false
|
||||
})
|
||||
})
|
||||
},
|
||||
setup({dateId, buyerId, buyerTypeId, buyerNId, unitsId, projectId, fromReservation = false}) {
|
||||
this.ids = {
|
||||
dateId,
|
||||
buyerId,
|
||||
buyerTypeId,
|
||||
buyerNId,
|
||||
unitsId,
|
||||
projectId
|
||||
}
|
||||
|
||||
this.components.$date = $(dateId)
|
||||
const cdo = structuredClone(calendar_date_options)
|
||||
cdo['maxDate'] = new Date()
|
||||
this.components.$date.calendar(cdo)
|
||||
|
||||
this.components.project = new Proyecto({unidades_id: unitsId, proyecto_id: projectId})
|
||||
this.components.buyer = new Propietario({id: buyerId, id_tipo: buyerTypeId, id_cantidad: buyerNId})
|
||||
|
||||
const payments = [
|
||||
'pie',
|
||||
'subsidio',
|
||||
'credito',
|
||||
'bono_pie'
|
||||
]
|
||||
payments.forEach(payment => {
|
||||
this.components.payments[payment] = new Payment({
|
||||
id: '#' + payment,
|
||||
checkbox_id: 'has_' + payment
|
||||
})
|
||||
})
|
||||
|
||||
this.components.$form = $(this.ids.form)
|
||||
this.components.$form.submit(event => {
|
||||
event.preventDefault();
|
||||
const button = $(event.currentTarget).find(".ui.button[type='submit']");
|
||||
button.prop('disabled', true);
|
||||
button.css('cursor', 'wait');
|
||||
const form = event.currentTarget;
|
||||
venta.submit(form);
|
||||
return false;
|
||||
})
|
||||
document.getElementById('submit_button').addEventListener('click', clickEvent => {
|
||||
clickEvent.preventDefault();
|
||||
const button = $(clickEvent.currentTarget)
|
||||
button.prop('disabled', true);
|
||||
button.css('cursor', 'wait');
|
||||
const form = document.getElementById('add_form')
|
||||
venta.submit(form)
|
||||
return false;
|
||||
})
|
||||
|
||||
if (fromReservation) {
|
||||
this.fill.reservation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
const fromReservation = {{ $from_reservation ? 'true' : 'false' }};
|
||||
venta.setup({
|
||||
dateId: '#fecha_venta_calendar',
|
||||
buyerId: '#propietario',
|
||||
buyerTypeId: 'persona_propietario',
|
||||
buyerNId: 'cantidad_propietario',
|
||||
unitsId: '#unidades',
|
||||
projectId: '#proyecto',
|
||||
fromReservation: fromReservation
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -44,6 +44,7 @@
|
||||
facturas: () => {
|
||||
document.getElementById(this.ids.facturas).innerHTML = this.venta.draw().facturas(this.formatters)
|
||||
this.venta.watch().facturas()
|
||||
$('[data-html]').popup()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -220,9 +220,47 @@
|
||||
},
|
||||
resumen: ({no, classes, formatters}) => {
|
||||
const emptyTerreno = '<div class="ui tiny red horizontal circular label">0</div>'
|
||||
const calculoTerreno = [
|
||||
[
|
||||
`Base Terreno (${formatters.date.format(this.props.terreno.fecha)})`,
|
||||
`\$${formatters.pesos.format(this.props.terreno.valor)}`
|
||||
],
|
||||
[
|
||||
`IPC (${formatters.date.format(this.props.fecha)})`,
|
||||
`${formatters.percent.format({{ $ipc }})}%`
|
||||
],
|
||||
[
|
||||
'Prorrateo',
|
||||
`${formatters.percent.format(this.props.detalle.descuento * 100)}%`
|
||||
],
|
||||
[
|
||||
'Proporcion',
|
||||
`${(this.props.proporcion * 100).toFixed(2).replace('.', ',')}%`
|
||||
],
|
||||
[
|
||||
'Terreno Aplicado',
|
||||
`\$${formatters.pesos.format(this.props.detalle.terreno)}`
|
||||
]
|
||||
]
|
||||
const tableClasses = [
|
||||
'',
|
||||
" align='right'"
|
||||
]
|
||||
const calculoTerrenoTable = [
|
||||
'<table>'
|
||||
]
|
||||
calculoTerreno.forEach(items => {
|
||||
const row = []
|
||||
items.forEach((cell, idx) => {
|
||||
row.push(`<td${tableClasses[idx]}>${cell}</td>`)
|
||||
})
|
||||
calculoTerrenoTable.push(`<tr>${row.join('')}</tr>`)
|
||||
})
|
||||
calculoTerrenoTable.push('</table>')
|
||||
|
||||
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 />' +
|
||||
'Valor con Terreno: $' + formatters.pesos.format(this.props.detalle.base) + ' - Menos valor terreno: <span data-variation="wide multiline" data-html="' + calculoTerrenoTable.join('') + '">$' + ((this.props.detalle.terreno > 0) ? formatters.pesos.format(-this.props.detalle.terreno) : emptyTerreno) + '</span><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 />' +
|
||||
|
||||
@ -13,6 +13,31 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui three column grid">
|
||||
<div class="column">
|
||||
<button class="ui button" id="import_template">
|
||||
<i class="file icon"></i>
|
||||
Descargar plantilla
|
||||
</button>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="ui checkbox import_filled">
|
||||
<input type="checkbox" name="filled" />
|
||||
<label>¿Con unidades del proyecto?</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="ui radio checkbox import_format">
|
||||
<input type="radio" name="format" value="csv" />
|
||||
<label>CSV</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="ui radio checkbox import_format">
|
||||
<input type="radio" name="format" value="xlsx" checked />
|
||||
<label>XLSX</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input class="ui invisible file input" type="file" id="import_file" name="file" />
|
||||
<label class="ui placeholder segment" for="import_file">
|
||||
<div class="ui icon header">
|
||||
@ -32,8 +57,131 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('layout.body.scripts.xlsx')
|
||||
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class ImportTemplate {
|
||||
ids = {
|
||||
button: '',
|
||||
filled: '',
|
||||
format: ''
|
||||
}
|
||||
components = {
|
||||
button: null,
|
||||
$filled: null,
|
||||
$format: null
|
||||
}
|
||||
data = {
|
||||
filename: '',
|
||||
filled: false,
|
||||
format: '',
|
||||
columns: [],
|
||||
csv: {
|
||||
separator: '',
|
||||
},
|
||||
}
|
||||
download(event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (this.data.format === 'csv') {
|
||||
this.downloadCsv()
|
||||
}
|
||||
if (this.data.format === 'xlsx') {
|
||||
this.downloadXlsx()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
downloadCsv() {
|
||||
const data = this.buildCsvData()
|
||||
const blob = new Blob([data.map(row => row.join(this.data.csv.separator)).join('\n')], {type: 'text/csv'})
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = this.data.filename + '.csv'
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
downloadXlsx() {
|
||||
const workbook = XLSX.utils.book_new()
|
||||
const worksheet = XLSX.utils.json_to_sheet(this.buildData())
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Plantilla de Precios')
|
||||
XLSX.writeFile(workbook, this.data.filename + '.xlsx', { compression: true })
|
||||
}
|
||||
buildData() {
|
||||
const data = [this.data.columns]
|
||||
if (this.data.filled) {
|
||||
const dateFormatter = new Intl.DateTimeFormat('es-CL')
|
||||
const date = precios.components.modals.import.components.$calendar.calendar('get date')
|
||||
precios.data.precios.forEach(tipologia => {
|
||||
tipologia.lineas.forEach(linea => {
|
||||
linea.unidades.forEach(unidad => {
|
||||
data.push({
|
||||
'Fecha*': date ? dateFormatter.format(date) : '',
|
||||
'Proyecto*': precios.data.proyecto,
|
||||
'Tipo*': tipologia.tipo,
|
||||
Unidad: unidad.nombre,
|
||||
Valor: ''
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
buildCsvData() {
|
||||
const data = this.buildData()
|
||||
const output = []
|
||||
output.push(Object.keys(data[0]))
|
||||
data.forEach(row => {
|
||||
output.push(Object.values(row))
|
||||
})
|
||||
return output
|
||||
}
|
||||
constructor() {
|
||||
this.ids.button = 'import_template'
|
||||
this.ids.filled = 'import_filled'
|
||||
this.ids.format = 'import_format'
|
||||
|
||||
this.data.filename = 'Plantilla de Precios'
|
||||
this.data.format = 'xlsx'
|
||||
this.data.columns = {
|
||||
'Fecha*': 'Fecha es opcional. Se toma la fecha seleccionada',
|
||||
'Proyecto*': 'Proyecto es opcional. Se toma el proyecto seleccionado',
|
||||
'Tipo*': 'Tipo es optional. Se toma departamento.',
|
||||
Unidad: '',
|
||||
Valor: ''
|
||||
}
|
||||
this.data.csv.separator = ';'
|
||||
|
||||
this.setup()
|
||||
}
|
||||
setup() {
|
||||
this.components.button = document.getElementById(this.ids.button)
|
||||
this.components.button.addEventListener('click', this.download.bind(this))
|
||||
|
||||
this.components.$filled = $(`.${this.ids.filled}`)
|
||||
this.components.$filled.checkbox({
|
||||
fireOnInit: true,
|
||||
onChange: () => {
|
||||
const checkbox = this.components.$filled.find('[name="filled"]').toArray()[0]
|
||||
this.data.filled = checkbox.checked
|
||||
}
|
||||
})
|
||||
this.components.$filled.checkbox('set unchecked')
|
||||
|
||||
this.components.$format = $(`.${this.ids.format}`)
|
||||
this.components.$format.checkbox({
|
||||
fireOnInit: true,
|
||||
onChange: () => {
|
||||
const radios = this.components.$format.find('[name="format"]').toArray()
|
||||
const checkedRadio = radios.filter(elem => elem.checked)[0]
|
||||
this.data.format = checkedRadio.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
class ImportModal {
|
||||
ids = {
|
||||
modal: '',
|
||||
@ -47,7 +195,8 @@
|
||||
project: null,
|
||||
$calendar: null,
|
||||
file: null,
|
||||
$file: null
|
||||
$file: null,
|
||||
template: null
|
||||
}
|
||||
constructor() {
|
||||
this.ids.modal = 'import_modal'
|
||||
@ -74,7 +223,9 @@
|
||||
const body = new FormData()
|
||||
body.set('project_id', this.components.project.value)
|
||||
const date = this.components.$calendar.calendar('get date')
|
||||
body.set('date', [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'))
|
||||
if (date) {
|
||||
body.set('date', [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'))
|
||||
}
|
||||
body.set('file', this.components.file.files[0])
|
||||
APIClient.fetch(url, {method, body}).then(response => response.json()).then(json => {
|
||||
if (json.status === true) {
|
||||
@ -91,17 +242,23 @@
|
||||
this.import()
|
||||
}
|
||||
})
|
||||
|
||||
this.components.form = this.components.$modal.find('form')
|
||||
this.components.form.submit(event => {
|
||||
event.preventDefault()
|
||||
this.import()
|
||||
return false
|
||||
})
|
||||
|
||||
this.components.project = document.getElementById(this.ids.project)
|
||||
|
||||
this.components.$calendar = $(`#${this.ids.calendar}`)
|
||||
const cdo = structuredClone(calendar_date_options)
|
||||
cdo['maxDate'] = new Date()
|
||||
this.components.$calendar.calendar(cdo)
|
||||
|
||||
this.template = new ImportTemplate()
|
||||
|
||||
this.components.file = document.getElementById(this.ids.file)
|
||||
this.components.$file = $(this.components.file.parentNode.querySelector('label'))
|
||||
this.components.$file.css('cursor', 'pointer')
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
'<td colspan="3">Todas las Unidades</td>',
|
||||
`<td class="right aligned">`,
|
||||
`<button class="ui red icon button remove_button project" data-id="${project.id}">`,
|
||||
'<i class="plus icon"></i>',
|
||||
'<i class="trash icon"></i>',
|
||||
'</button>',
|
||||
`</td>`
|
||||
].join("\n")
|
||||
|
||||
@ -101,7 +101,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('layout.body.scripts.rut')
|
||||
@include('ventas.reservations.modal.common.scripts.promotions')
|
||||
@include('ventas.reservations.modal.common.scripts.units')
|
||||
@include('ventas.reservations.modal.common.scripts.payments')
|
||||
@include('ventas.reservations.modal.common.scripts.modal')
|
||||
|
||||
@include('ventas.reservations.modal.add')
|
||||
@include('ventas.reservations.modal.edit')
|
||||
@include('ventas.reservations.modal.comment')
|
||||
@endsection
|
||||
|
||||
@ -123,7 +130,7 @@
|
||||
}
|
||||
component_id = ''
|
||||
component = null
|
||||
current_project = null;
|
||||
current_project = null
|
||||
title_id = ''
|
||||
title_component = null
|
||||
|
||||
@ -158,6 +165,7 @@
|
||||
|
||||
reservations.get.reservations(project_id)
|
||||
reservations.components.modals.add.load(project_id)
|
||||
reservations.components.modals.edit.load(project_id)
|
||||
this.hide()
|
||||
|
||||
this.title_component.innerHTML = event.currentTarget.innerHTML
|
||||
@ -294,56 +302,153 @@
|
||||
return this
|
||||
}
|
||||
}
|
||||
get columnsData() {
|
||||
return this.reservations.map(reservation => {
|
||||
const date = new Date(Date.parse(reservation.date) + 24 * 60 * 60 * 1000)
|
||||
return {
|
||||
id: reservation.id,
|
||||
unidades: reservation.summary,
|
||||
cliente: reservation.buyer.nombreCompleto,
|
||||
fecha: this.formatters.date.format(date),
|
||||
oferta: `${this.formatters.ufs.format(reservation.offer)} UF`,
|
||||
valida: reservation.valid ? '<span class="ui green text">Si</span>' : '<span class="ui red text">No</span>',
|
||||
operador: reservation.broker?.name ?? '',
|
||||
}
|
||||
})
|
||||
}
|
||||
draw() {
|
||||
if (this.reservations.length === 0) {
|
||||
this.empty()
|
||||
return
|
||||
}
|
||||
const tbody = this.component.querySelector('tbody')
|
||||
tbody.innerHTML = ''
|
||||
this.columnsData.forEach(column => {
|
||||
const tr = document.createElement('tr')
|
||||
const contents = []
|
||||
const id = column.id
|
||||
this.columnNames.forEach(name => {
|
||||
contents.push(`<td>${column[name]}</td>`)
|
||||
})
|
||||
const actions = this.drawActions(id)
|
||||
if (actions !== '') {
|
||||
contents.push(actions)
|
||||
}
|
||||
tr.innerHTML = contents.join("\n")
|
||||
tbody.appendChild(tr)
|
||||
})
|
||||
this.show()
|
||||
|
||||
this.watch()
|
||||
get columnsData() {
|
||||
const columnValues = {
|
||||
id: reservation => reservation.id,
|
||||
unidades: reservation => reservation.summary,
|
||||
cliente: reservation => reservation.buyer.nombreCompleto,
|
||||
fecha: reservation => this.formatters.date.format(new Date(Date.parse(reservation.date) + 24 * 60 * 60 * 1000)),
|
||||
oferta: reservation => `${this.formatters.ufs.format(reservation.offer)} UF`,
|
||||
valida: reservation => reservation.valid ? '<span class="ui green text">Si</span>' : '<span class="ui red text">No</span>',
|
||||
operador: reservation => reservation.broker?.name ?? '',
|
||||
}
|
||||
const tooltipVariation = 'very wide multiline'
|
||||
const tooltips = this.draw().tooltip()
|
||||
return this.reservations.map(reservation => {
|
||||
const data = {}
|
||||
Object.entries(columnValues).forEach(([key, value]) => {
|
||||
if (key in tooltips) {
|
||||
const processor = tooltips[key]
|
||||
const content = processor(reservation)//.replaceAll(' ', ' ')
|
||||
data[key] = `data-html="${content}" data-variation="${tooltipVariation}">${value(reservation)}`
|
||||
return
|
||||
}
|
||||
data[key] = value(reservation)
|
||||
})
|
||||
return data
|
||||
})
|
||||
}
|
||||
drawActions(id) {
|
||||
return `
|
||||
<td class="right aligned">
|
||||
<button class="ui green mini icon button approve" data-id="${id}" title="Aprobar">
|
||||
<i class="check icon"></i>
|
||||
</button>
|
||||
<button class="ui red mini icon button reject" data-id="${id}" title="Rechazar">
|
||||
<i class="trash icon"></i>
|
||||
</button>
|
||||
</td>`
|
||||
|
||||
draw() {
|
||||
return {
|
||||
tbody: () => {
|
||||
if (this.reservations.length === 0) {
|
||||
this.empty()
|
||||
return
|
||||
}
|
||||
const tbody = this.component.querySelector('tbody')
|
||||
tbody.innerHTML = ''
|
||||
this.columnsData.forEach(column => {
|
||||
const tr = document.createElement('tr')
|
||||
const contents = []
|
||||
const id = column.id
|
||||
this.columnNames.forEach(name => {
|
||||
let td = `<td>${column[name]}</td>`
|
||||
if (column[name].includes('data-content') || column[name].includes('data-html')) {
|
||||
td = `<td ${column[name]}</td>`
|
||||
}
|
||||
contents.push(td)
|
||||
})
|
||||
const actions = this.draw().actions(id)
|
||||
if (actions !== '') {
|
||||
contents.push(actions)
|
||||
}
|
||||
tr.innerHTML = contents.join("\n")
|
||||
tbody.appendChild(tr)
|
||||
})
|
||||
this.show()
|
||||
|
||||
$(this.component).find('[data-content],[data-html]').popup()
|
||||
|
||||
this.watch()
|
||||
},
|
||||
actions: id => {
|
||||
return [
|
||||
'<td class="right aligned">',
|
||||
this.draw().actionButtons().edit(id),
|
||||
this.draw().actionButtons().approve(id),
|
||||
this.draw().actionButtons().reject(id),
|
||||
'</td>'
|
||||
].join("\n")
|
||||
},
|
||||
actionButtons: () => {
|
||||
return {
|
||||
edit: id => {
|
||||
return [
|
||||
`<button class="ui blue mini icon button edit" data-id="${id}" title="Editar">`,
|
||||
'<i class="edit icon"></i>',
|
||||
'</button>'
|
||||
].join("\n")
|
||||
},
|
||||
approve: id => {
|
||||
return [
|
||||
`<button class="ui green mini icon button approve" data-id="${id}" title="Aprobar">`,
|
||||
'<i class="check icon"></i>',
|
||||
'</button>'
|
||||
].join("\n")
|
||||
},
|
||||
reject: id => {
|
||||
return [
|
||||
`<button class="ui red mini icon button reject" data-id="${id}" title="Rechazar">`,
|
||||
'<i class="trash icon"></i>',
|
||||
'</button>'
|
||||
].join("\n")
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: () => {
|
||||
return {
|
||||
oferta: reservation => {
|
||||
const formatter = new Intl.NumberFormat('es-CL', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})
|
||||
|
||||
const output = []
|
||||
const table = ['<table>']
|
||||
reservation.units.forEach(unit => {
|
||||
const type = unit.unit.proyecto_tipo_unidad.tipo_unidad.descripcion
|
||||
table.push(`<tr><td>${type.charAt(0).toUpperCase() + type.slice(1)} ${unit.unit.descripcion}:</td><td align='right'>${formatter.format(unit.value)} UF</td></tr>`)
|
||||
})
|
||||
if (reservation.broker !== null) {
|
||||
table.push('<tr><td>-----</td></tr>')
|
||||
let commission = reservation.broker.commission
|
||||
table.push(`<tr><td>Broker:</td><td align='right'>${reservation.broker.name}</td><td align='right'>(${commission})</td></tr>`)
|
||||
}
|
||||
if (reservation.promotions.length > 0) {
|
||||
table.push('<tr><td>-----</td></tr>')
|
||||
reservation.promotions.forEach(promotion => {
|
||||
let value = 0
|
||||
switch (promotion.type) {
|
||||
case {{ \Incoviba\Model\Venta\Promotion\Type::FIXED }}:
|
||||
value = `${formatter.format(promotion.amount)} UF`
|
||||
break
|
||||
case {{ \Incoviba\Model\Venta\Promotion\Type::VARIABLE }}:
|
||||
value = `${formatter.format(promotion.amount * 100)} %`
|
||||
break
|
||||
}
|
||||
table.push(`<tr><td>${promotion.description}:</td><td align='right'>${value}</td></tr>`)
|
||||
})
|
||||
}
|
||||
table.push('</table>')
|
||||
output.push(table.join(''))
|
||||
return output.join("<br />")
|
||||
},
|
||||
cliente: reservation => {
|
||||
const formatter = new Intl.NumberFormat('es-CL', {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
})
|
||||
return [
|
||||
`RUT: ${formatter.format(reservation.buyer.rut)}-${reservation.buyer.digito}`
|
||||
].join("<br />")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch() {
|
||||
if (Object.keys(this.actions).length === 0) {
|
||||
return
|
||||
@ -361,6 +466,7 @@
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
empty() {
|
||||
const tbody = this.component.querySelector('tbody')
|
||||
tbody.innerHTML = ''
|
||||
@ -371,15 +477,19 @@
|
||||
|
||||
this.show()
|
||||
}
|
||||
|
||||
show() {
|
||||
this.component.style.display = this.display.reservations
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.component.style.display = 'none'
|
||||
}
|
||||
|
||||
find(id) {
|
||||
return this.reservations.find(reservation => reservation.id === parseInt(id))
|
||||
}
|
||||
|
||||
send = {
|
||||
get(url) {
|
||||
return APIClient.fetch(url).then(response => response.json()).then(json => {
|
||||
@ -406,28 +516,104 @@
|
||||
}
|
||||
|
||||
get columnsData() {
|
||||
const data = super.columnsData;
|
||||
const data = super.columnsData
|
||||
return data.map(row => {
|
||||
delete(row['valida'])
|
||||
delete (row['valida'])
|
||||
return row
|
||||
})
|
||||
}
|
||||
drawActions(id) {
|
||||
return `
|
||||
<td class="right aligned">
|
||||
<button class="ui green mini icon button edit" data-id="${id}" title="Promesar">
|
||||
<i class="right chevron icon"></i>
|
||||
</button>
|
||||
<button class="ui red mini icon button remove" data-id="${id}" title="Abandonar">
|
||||
<i class="trash icon"></i>
|
||||
</button>
|
||||
</td>`
|
||||
|
||||
draw() {
|
||||
const draw = super.draw()
|
||||
const actionButtons = draw.actionButtons()
|
||||
draw.actionButtons = () => {
|
||||
actionButtons['promise'] = id => {
|
||||
return [
|
||||
`<button class="ui green mini icon button promise" data-id="${id}" title="Promesar">`,
|
||||
'<i class="right chevron icon"></i>',
|
||||
'</button>'
|
||||
].join("\n")
|
||||
}
|
||||
actionButtons['remove'] = id => {
|
||||
return [
|
||||
`<button class="ui red mini icon button remove" data-id="${id}" title="Abandonar">`,
|
||||
'<i class="trash icon"></i>',
|
||||
'</button>'
|
||||
].join("\n")
|
||||
}
|
||||
return actionButtons
|
||||
}
|
||||
draw.actions = (id) => {
|
||||
return [
|
||||
'<td class="right aligned">',
|
||||
this.draw().actionButtons().edit(id),
|
||||
this.draw().actionButtons().promise(id),
|
||||
this.draw().actionButtons().remove(id),
|
||||
'</td>'
|
||||
].join("\n")
|
||||
}
|
||||
return draw
|
||||
}
|
||||
|
||||
actions = {
|
||||
edit: event => {
|
||||
event.preventDefault()
|
||||
const id = event.currentTarget.dataset.id
|
||||
reservations.components.modals.edit.load(id)
|
||||
reservations.components.modals.edit.show({type: 'active', reservation_id: id})
|
||||
return false
|
||||
},
|
||||
promise: event => {
|
||||
event.preventDefault()
|
||||
const reservationId = event.currentTarget.dataset.id
|
||||
const reservation = this.find(reservationId)
|
||||
|
||||
// Create a form to submit the data via POST
|
||||
const form = document.createElement('form')
|
||||
form.method = 'POST'
|
||||
form.action = '{{ $urls->base }}/ventas/add'
|
||||
|
||||
// Add CSRF token
|
||||
const csrfToken = Math.random().toString(36).substring(2)
|
||||
const csrfInput = document.createElement('input')
|
||||
csrfInput.type = 'hidden'
|
||||
csrfInput.name = '_token'
|
||||
csrfInput.value = csrfToken
|
||||
form.appendChild(csrfInput)
|
||||
|
||||
// Add reservation data
|
||||
const addInput = (name, value) => {
|
||||
if (value) {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'hidden'
|
||||
input.name = name
|
||||
input.value = value
|
||||
form.appendChild(input)
|
||||
}
|
||||
}
|
||||
|
||||
addInput('from_reservation', 'true')
|
||||
addInput('reservation_id', reservationId)
|
||||
|
||||
if (reservation.cliente) {
|
||||
addInput('cliente_nombre', reservation.cliente.nombre)
|
||||
addInput('cliente_rut', reservation.cliente.rut)
|
||||
addInput('cliente_email', reservation.cliente.email)
|
||||
addInput('cliente_telefono', reservation.cliente.telefono)
|
||||
}
|
||||
|
||||
if (reservation.propiedad) {
|
||||
addInput('proyecto_id', reservation.propiedad.proyecto_id)
|
||||
// Add other property-related parameters as needed
|
||||
}
|
||||
|
||||
if (reservation.oferta) {
|
||||
addInput('valor', reservation.oferta.valor)
|
||||
// Add other offer-related parameters as needed
|
||||
}
|
||||
|
||||
// Submit the form
|
||||
document.body.appendChild(form)
|
||||
form.submit()
|
||||
return false
|
||||
},
|
||||
remove: event => {
|
||||
@ -448,7 +634,89 @@
|
||||
super({component_id, formatters})
|
||||
}
|
||||
|
||||
draw() {
|
||||
const draw = super.draw()
|
||||
const tooltip = draw.tooltip()
|
||||
tooltip['valida'] = reservation => {
|
||||
const formatter = new Intl.NumberFormat('es-CL', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})
|
||||
|
||||
const output = []
|
||||
const table = [
|
||||
'<table>',
|
||||
'<thead>',
|
||||
'<tr>',
|
||||
'<th></th>',
|
||||
'<th>Base Oferta</th>',
|
||||
'<th>Precio</th>',
|
||||
'</thead>'
|
||||
]
|
||||
const rowAlters = [
|
||||
'',
|
||||
" align='right'",
|
||||
" align='right'",
|
||||
" align='right'"
|
||||
]
|
||||
reservation.units.forEach(unit => {
|
||||
let type = unit.unit.proyecto_tipo_unidad.tipo_unidad.descripcion
|
||||
type = type.charAt(0).toUpperCase() + type.slice(1)
|
||||
const base = unit.base ?? (unit.value ?? 0)
|
||||
const price = unit.unit.current_precio?.valor ?? 0
|
||||
const diff = (base - price) / price * 100
|
||||
const rowsData = [
|
||||
[
|
||||
`${type} ${unit.unit.descripcion}`,
|
||||
`${formatter.format(base)} UF`,
|
||||
`${formatter.format(price)} UF`,
|
||||
`(${formatter.format(diff)} %)`
|
||||
]
|
||||
]
|
||||
const rowContent = []
|
||||
rowsData.forEach(row => {
|
||||
const content = []
|
||||
row.forEach((cell, idx) => {
|
||||
content.push(`<td${rowAlters[idx]}>${cell}</td>`)
|
||||
})
|
||||
rowContent.push(`<tr>${content.join('')}</tr>`)
|
||||
})
|
||||
table.push(rowContent.join(''))
|
||||
})
|
||||
if (reservation.broker !== null) {
|
||||
table.push('<tr><td>-----</td></tr>')
|
||||
let commission = reservation.broker.commission
|
||||
table.push(`<tr><td>Broker:</td><td align='right'>${reservation.broker.name}</td><td align='right'>(${commission})</td></tr>`)
|
||||
}
|
||||
reservation.promotions.forEach(promotion => {
|
||||
let value = 0
|
||||
switch (promotion.type) {
|
||||
case {{ \Incoviba\Model\Venta\Promotion\Type::FIXED }}:
|
||||
value = `${formatter.format(promotion.amount)} UF`
|
||||
break
|
||||
case {{ \Incoviba\Model\Venta\Promotion\Type::VARIABLE }}:
|
||||
value = `${formatter.format(promotion.amount * 100)} %`
|
||||
break
|
||||
}
|
||||
table.push(`<tr><td>${promotion.description}:</td><td align='right'>${value}</td></tr>`)
|
||||
})
|
||||
table.push('</table>')
|
||||
output.push(table.join(''))
|
||||
return output.join("<br />")
|
||||
}
|
||||
draw['tooltip'] = () => {
|
||||
return tooltip
|
||||
}
|
||||
return draw
|
||||
}
|
||||
|
||||
actions = {
|
||||
edit: event => {
|
||||
event.preventDefault()
|
||||
const id = event.currentTarget.dataset.id
|
||||
reservations.components.modals.edit.show({type: 'pending', reservation_id: id})
|
||||
return false
|
||||
},
|
||||
approve: event => {
|
||||
event.preventDefault()
|
||||
const id = event.currentTarget.dataset.id
|
||||
@ -482,10 +750,17 @@
|
||||
return data[idx]
|
||||
})
|
||||
}
|
||||
drawActions(id) {
|
||||
return ''
|
||||
|
||||
draw() {
|
||||
const draw = super.draw()
|
||||
draw.actions = (id) => {
|
||||
return ''
|
||||
}
|
||||
return draw
|
||||
}
|
||||
|
||||
watch() {
|
||||
}
|
||||
watch() {}
|
||||
|
||||
mapState(state) {
|
||||
return {
|
||||
@ -515,6 +790,18 @@
|
||||
loader: '',
|
||||
results: '',
|
||||
},
|
||||
data: {
|
||||
comunas: {},
|
||||
brokers: {},
|
||||
promotions: {},
|
||||
units: {}
|
||||
},
|
||||
locks: {
|
||||
comunas: null,
|
||||
brokers: null,
|
||||
promotions: null,
|
||||
units: null
|
||||
},
|
||||
get: {
|
||||
send: (project_id, url_segment, component) => {
|
||||
const url = `/api/ventas/reservations/project/${project_id}/${url_segment}`
|
||||
@ -522,7 +809,7 @@
|
||||
if (json.reservations.length > 0) {
|
||||
component.set.reservations(json.reservations)
|
||||
}
|
||||
component.draw()
|
||||
component.draw().tbody()
|
||||
})
|
||||
},
|
||||
active: project_id => {
|
||||
@ -559,7 +846,137 @@
|
||||
return current_url.pathname.replace(/project\/\d+/, `project/${project_id}`)
|
||||
}
|
||||
return `${current_url.pathname}/project/${project_id}`
|
||||
}
|
||||
},
|
||||
comunas: region_id => {
|
||||
if (region_id in reservations.data.comunas) {
|
||||
reservations.locks.comunas = null
|
||||
return new Promise(resolve => {
|
||||
resolve(reservations.data.comunas[region_id])
|
||||
})
|
||||
}
|
||||
if (reservations.locks.comunas !== null) {
|
||||
return reservations.locks.comunas
|
||||
}
|
||||
const uri = `/api/region/${region_id}/comunas`
|
||||
reservations.locks.comunas = APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
if (json.comunas.length === 0) {
|
||||
return
|
||||
}
|
||||
reservations.data.comunas[region_id] = json.comunas.map(comuna => {
|
||||
return {
|
||||
text: comuna.descripcion,
|
||||
name: comuna.descripcion,
|
||||
value: comuna.id
|
||||
}
|
||||
})
|
||||
return reservations.data.comunas[region_id]
|
||||
})
|
||||
return reservations.locks.comunas
|
||||
},
|
||||
brokers: project_id => {
|
||||
if (project_id in reservations.data.brokers) {
|
||||
reservations.locks.brokers = null
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(reservations.data.brokers[project_id])
|
||||
})
|
||||
}
|
||||
if (reservations.locks.brokers !== null) {
|
||||
return reservations.locks.brokers
|
||||
}
|
||||
const uri = `/api/proyecto/${project_id}/brokers`
|
||||
reservations.locks.brokers = APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
if (json.contracts.length === 0) {
|
||||
return
|
||||
}
|
||||
const formatter = new Intl.NumberFormat('es-CL', { style: 'percent', minimumFractionDigits: 2 })
|
||||
reservations.data.brokers[project_id] = json.contracts.map(contract => {
|
||||
return {
|
||||
id: contract.id,
|
||||
broker_rut: contract.broker_rut,
|
||||
commission: formatter.format(contract.commission),
|
||||
name: '',
|
||||
text: '',
|
||||
value: contract.broker_rut
|
||||
}
|
||||
})
|
||||
const promises = []
|
||||
json.contracts.forEach(contract => {
|
||||
promises.push(reservations.get.broker(contract.broker_rut))
|
||||
})
|
||||
|
||||
return Promise.all(promises).then(data => {
|
||||
data.forEach(broker => {
|
||||
if (!('rut' in broker)) {
|
||||
return
|
||||
}
|
||||
const idx = reservations.data.brokers[project_id].findIndex(contract => contract.broker_rut === broker.rut)
|
||||
const text = reservations.data.brokers[project_id][idx].text = `${broker.name} - ${reservations.data.brokers[project_id][idx].commission}`
|
||||
reservations.data.brokers[project_id][idx].name = text
|
||||
reservations.data.brokers[project_id][idx].text = text
|
||||
})
|
||||
})
|
||||
})
|
||||
return reservations.locks.brokers
|
||||
},
|
||||
broker: (broker_rut) => {
|
||||
const uri = `/api/proyectos/broker/${broker_rut}`
|
||||
return APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
if (!('broker' in json)) {
|
||||
return []
|
||||
}
|
||||
return json.broker
|
||||
})
|
||||
},
|
||||
promotions: project_id => {
|
||||
if (project_id in reservations.data.promotions) {
|
||||
reservations.locks.promotions = null
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(this.data.promotions[project_id])
|
||||
})
|
||||
}
|
||||
if (reservations.locks.promotions !== null) {
|
||||
return reservations.locks.promotions
|
||||
}
|
||||
const uri = `/api/proyecto/${project_id}/promotions`
|
||||
reservations.locks.promotions = APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
if (json.promotions.length === 0) {
|
||||
return reservations.data.promotions[project_id] = []
|
||||
}
|
||||
return reservations.data.promotions[project_id] = json.promotions
|
||||
})
|
||||
return reservations.locks.promotions
|
||||
},
|
||||
units: project_id => {
|
||||
if (project_id in reservations.data.units) {
|
||||
reservations.locks.units = null
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(reservations.data.units[project_id])
|
||||
})
|
||||
}
|
||||
if (reservations.locks.units !== null) {
|
||||
return reservations.locks.units
|
||||
}
|
||||
const uri = `/api/proyecto/${project_id}/unidades/disponibles`
|
||||
reservations.locks.units = APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
if (json.unidades.length === 0) {
|
||||
reservations.data.units[project_id] = {}
|
||||
return reservations.data.units[project_id]
|
||||
}
|
||||
if (!(project_id in reservations.data.units)) {
|
||||
reservations.data.units[project_id] = {}
|
||||
}
|
||||
json.unidades.forEach(unit => {
|
||||
const type = unit.proyecto_tipo_unidad.tipo_unidad.descripcion
|
||||
if (!(type in reservations.data.units[project_id])) {
|
||||
reservations.data.units[project_id][type] = []
|
||||
}
|
||||
reservations.data.units[project_id][type].push(unit)
|
||||
})
|
||||
|
||||
return reservations.data.units[project_id]
|
||||
})
|
||||
return reservations.locks.units
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
show: () => {
|
||||
@ -648,6 +1065,7 @@
|
||||
this.show.projects()
|
||||
|
||||
this.components.modals.add = new AddReservationModal(configuration.ids.projects)
|
||||
this.components.modals.edit = new EditReservationModal(configuration.ids.projects)
|
||||
this.components.modals.comment = new CommentModal()
|
||||
|
||||
const project_id = {{ $project_id ?? 'null' }};
|
||||
|
||||
@ -1,400 +1,99 @@
|
||||
<div class="ui modal" id="add_reservation_modal">
|
||||
<div class="header">
|
||||
Agregar Cierre
|
||||
</div>
|
||||
<div class="content">
|
||||
<form class="ui form" id="add_reservation_form">
|
||||
<input type="hidden" name="add_project_id" />
|
||||
<div class="three wide required field">
|
||||
<label>Fecha</label>
|
||||
<div class="ui calendar" id="add_date">
|
||||
<div class="ui icon input">
|
||||
<i class="calendar icon"></i>
|
||||
<input type="text" name="add_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide required field">
|
||||
<label>RUT</label>
|
||||
<div class="ui right labeled input" id="add_rut">
|
||||
<input type="text" name="add_buyer_rut" placeholder="RUT" />
|
||||
<div class="ui basic label">-<span id="add_digit"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label></label>
|
||||
<div class="ui inline loader" id="add_rut_loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide required field">
|
||||
<label>Nombre</label>
|
||||
<input type="text" name="add_buyer_name" placeholder="Nombre" />
|
||||
</div>
|
||||
<div class="six wide required field">
|
||||
<label>Apellidos</label>
|
||||
<input type="text" name="add_buyer_last_name" placeholder="Apellido Paterno" />
|
||||
</div>
|
||||
<div class="six wide field">
|
||||
<label></label>
|
||||
<input type="text" name="add_buyer_last_name2" placeholder="Apellido Materno" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide field">
|
||||
<label>Dirección</label>
|
||||
<input type="text" name="add_buyer_address_street" placeholder="Calle" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label></label>
|
||||
<input type="text" name="add_buyer_address_number" placeholder="N°" />
|
||||
</div>
|
||||
<div class="three wide field">
|
||||
<label></label>
|
||||
<input type="text" name="add_buyer_address_extra" placeholder="Otros Detalles" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide field">
|
||||
<label>Comuna</label>
|
||||
<div class="ui search selection dropdown" id="add_comuna">
|
||||
<input type="hidden" name="comuna" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Comuna</div>
|
||||
<div class="menu"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="seven wide field">
|
||||
<label>Región</label>
|
||||
<div class="ui search selection dropdown" id="add_region">
|
||||
<input type="hidden" name="region" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Región</div>
|
||||
<div class="menu">
|
||||
@foreach($regions as $region)
|
||||
<div class="item" data-value="{{$region->id}}">{{$region->numeral}} - {{$region->descripcion}}</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label>Telefono</label>
|
||||
<input type="text" name="add_buyer_phone" placeholder="Telefono" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Correo</label>
|
||||
<div class="ui labeled input">
|
||||
<input type="text" name="add_buyer_email_name" placeholder="Correo" />
|
||||
<div class="ui basic label">@</div>
|
||||
<input type="text" name="add_buyer_email_domain" placeholder="Dominio" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label>Estado Civil</label>
|
||||
<input type="text" name="add_buyer_marital_status" placeholder="Estado Civil" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Profesión</label>
|
||||
<input type="text" name="add_buyer_profession" placeholder="Profesión" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Fecha de Nacimiento</label>
|
||||
<div class="ui calendar" id="add_birthdate">
|
||||
<div class="ui icon input">
|
||||
<i class="calendar icon"></i>
|
||||
<input type="text" name="birthdate" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="six wide field">
|
||||
<label>Operador *</label>
|
||||
<div class="ui clearable search selection dropdown" id="add_broker">
|
||||
<input type="hidden" name="add_broker" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Operador</div>
|
||||
<div class="menu"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Agregar Promoción</label>
|
||||
<button type="button" class="ui icon button" id="add_promotion">
|
||||
<i class="plus icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="add_promotions"></div>
|
||||
<h4 class="ui dividing header">Unidades <span id="add_project_name"></span></h4>
|
||||
<div class="fields" id="add_unit_buttons"></div>
|
||||
<div id="add_units"></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui cancel button">
|
||||
Cancelar
|
||||
</div>
|
||||
<div class="ui green ok button">
|
||||
Agregar
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('layout.body.scripts.rut')
|
||||
@include('ventas.reservations.modal.common.modal', ['prefix' => 'add', 'modalTitle' => 'Agregar Cierre', 'okText' => 'Agregar'])
|
||||
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class AddModalPromotions {
|
||||
ids = {
|
||||
button: '',
|
||||
elements: ''
|
||||
}
|
||||
data = {
|
||||
promotions: []
|
||||
}
|
||||
components = {
|
||||
button: null,
|
||||
promotions: null,
|
||||
}
|
||||
display = {
|
||||
button: ''
|
||||
}
|
||||
|
||||
class AddModalPromotions extends ModalPromotions {
|
||||
constructor() {
|
||||
this.ids = {
|
||||
button: 'add_promotion',
|
||||
elements: 'add_promotions'
|
||||
}
|
||||
this.setup()
|
||||
}
|
||||
add() {
|
||||
const idx = Math.max(this.data.promotions.length, 0, Math.max(...this.data.promotions) + 1)
|
||||
this.data.promotions.push(idx)
|
||||
this.draw.promotions()
|
||||
}
|
||||
reset() {
|
||||
this.data.promotions = []
|
||||
this.draw.promotions()
|
||||
}
|
||||
remove(idx) {
|
||||
this.data.promotions = this.data.promotions.filter(promotion => promotion !== idx)
|
||||
this.draw.promotions()
|
||||
}
|
||||
draw = {
|
||||
promotion: idx => {
|
||||
const promotions = this.data.promotions.map(promotion => {
|
||||
return `<div class="item" data-value="${promotion.id}">${promotion.name}</div>`
|
||||
})
|
||||
return [
|
||||
`<div class="fields promotion" data-id="${idx}">`,
|
||||
'<div class="three wide field">',
|
||||
'<label>Promoción</label>',
|
||||
`<div class="ui search selection dropdown">`,
|
||||
'<input type="hidden" name="add_promotions[]" />',
|
||||
'<i class="dropdown icon"></i>',
|
||||
'<div class="default text">Promoción</div>',
|
||||
`<div class="menu">${promotions.join('')}</div>`,
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="two wide field">',
|
||||
'<label></label>',
|
||||
`<button class="ui red tiny icon button remove_promotion" type="button" data-id="${idx}"><i class="trash icon"></i></button>`,
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
},
|
||||
promotions: () => {
|
||||
if (this.data.promotions.length === 0) {
|
||||
this.components.button.parentElement.style.display = 'none'
|
||||
this.components.promotions.innerHTML = ''
|
||||
return
|
||||
}
|
||||
this.components.button.parentElement.style.display = this.display.button
|
||||
this.components.promotions.innerHTML = this.data.promotions.map((promotion, idx) => {
|
||||
return this.draw.promotion(idx)
|
||||
}).join('')
|
||||
this.components.promotions.querySelectorAll('.dropdown').forEach(dropdown => {
|
||||
$(dropdown).dropdown()
|
||||
})
|
||||
this.components.promotions.querySelectorAll('.remove_promotion').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const idx = Number(button.dataset.id)
|
||||
this.remove(idx)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
setup() {
|
||||
this.components.button = document.getElementById(this.ids.button)
|
||||
this.components.promotions = document.getElementById(this.ids.elements)
|
||||
this.components.button.addEventListener('click', () => {
|
||||
this.add()
|
||||
})
|
||||
this.display.button = this.components.button.parentElement.style.display
|
||||
this.draw.promotions()
|
||||
super('add')
|
||||
}
|
||||
}
|
||||
class AddModalUnits {
|
||||
parent = null
|
||||
ids = {
|
||||
buttons_holder: '',
|
||||
units: ''
|
||||
class AddModalUnits extends ModalUnits {
|
||||
constructor() {
|
||||
super({prefix: 'add', parent: null});
|
||||
}
|
||||
data = {
|
||||
button_map: {},
|
||||
types: {},
|
||||
units: [],
|
||||
}
|
||||
components = {
|
||||
buttons_holder: null,
|
||||
units: null,
|
||||
}
|
||||
class AddModalPayments extends ModalPayments {
|
||||
constructor() {
|
||||
super('add');
|
||||
}
|
||||
|
||||
constructor(parent) {
|
||||
this.parent = parent
|
||||
this.ids = {
|
||||
buttons_holder: 'add_unit_buttons',
|
||||
units: 'add_units'
|
||||
}
|
||||
this.data.button_map = {
|
||||
'departamento': 'building',
|
||||
'estacionamiento': 'car',
|
||||
'bodega': 'warehouse',
|
||||
'terraza': 'vector square'
|
||||
}
|
||||
this.setup()
|
||||
}
|
||||
class AddReservationModal extends ReservationModal {
|
||||
constructor(projects_id) {
|
||||
super({projects_id, prefix: 'add', components: {
|
||||
promotions: new AddModalPromotions(),
|
||||
units: new AddModalUnits(),
|
||||
payments: new AddModalPayments()
|
||||
}});
|
||||
}
|
||||
get promotions() {
|
||||
return this.parent.data.promotions[this.parent.data.current_project]
|
||||
}
|
||||
draw = {
|
||||
button: type => {
|
||||
return [
|
||||
'<div class="field">',
|
||||
`<button class="ui icon button" type="button" data-type="${type}" title="${type.charAt(0).toUpperCase() + type.slice(1)}">`,
|
||||
'<i class="plus icon"></i>',
|
||||
`<i class="${this.data.button_map[type]} icon"></i>`,
|
||||
'</button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
},
|
||||
buttons: () => {
|
||||
this.components.buttons_holder.innerHTML = Object.keys(this.data.types).map(type => {
|
||||
return this.draw.button(type)
|
||||
}).join('')
|
||||
this.components.buttons_holder.querySelectorAll('.button').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const type = button.dataset.type
|
||||
this.add(type)
|
||||
})
|
||||
})
|
||||
},
|
||||
units: () => {
|
||||
if (this.data.units.length === 0) {
|
||||
this.components.units.innerHTML = ''
|
||||
return
|
||||
|
||||
add() {
|
||||
const url = '/api/ventas/reservation/add'
|
||||
const form = document.getElementById(this.ids.form)
|
||||
let body = new FormData(form)
|
||||
const date = this.components.$date.calendar('get date')
|
||||
body.set(`${this.prefix}_date`, [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'))
|
||||
body.set(`${this.prefix}_buyer_rut`, Rut.clean(this.components.rut.querySelector('input').value))
|
||||
body.set(`${this.prefix}_buyer_digit`, this.components.digit.textContent)
|
||||
body.set(`${this.prefix}_buyer_address_comuna_id`, this.components.$comuna.dropdown('get value'))
|
||||
body.set(`${this.prefix}_broker_rut`, Rut.clean(this.components.$broker.dropdown('get value')))
|
||||
body.set(`${this.prefix}_buyer_email`, form.querySelector(`input[name='${this.prefix}_buyer_email_name']`).value + '@' + form.querySelector(`input[name='${this.prefix}_buyer_email_domain']`).value)
|
||||
const birthdate = this.components.$birthdate.calendar('get date')
|
||||
body.set(`${this.prefix}_buyer_birthdate`, [birthdate.getFullYear(), birthdate.getMonth() + 1, birthdate.getDate()].join('-'))
|
||||
|
||||
if (this.components.payments.components.pie.$checkbox.checkbox('is unchecked')) {
|
||||
body.delete(`${this.prefix}_payment_pie`)
|
||||
body.delete(`${this.prefix}_payment_cuotas`)
|
||||
}
|
||||
body.delete(`${this.prefix}_payment_has_pie`)
|
||||
if (this.components.payments.components.credit.$checkbox.checkbox('is unchecked')) {
|
||||
body.delete(`${this.prefix}_payment_credit`)
|
||||
}
|
||||
body.delete(`${this.prefix}_payment_has_credit`)
|
||||
|
||||
body.delete('comuna')
|
||||
body.delete('region')
|
||||
body.delete('broker')
|
||||
body.delete(`${this.prefix}_broker`)
|
||||
body.delete('birthdate')
|
||||
body.delete(`${this.prefix}_buyer_email_name`)
|
||||
body.delete(`${this.prefix}_buyer_email_domain`)
|
||||
|
||||
body.keys().forEach(key => {
|
||||
if (body.get(key) === '') {
|
||||
body.delete(key)
|
||||
}
|
||||
this.components.units.innerHTML = this.data.units.map(unit => {
|
||||
return this.draw.unit(unit)
|
||||
}).join('')
|
||||
this.components.units.querySelectorAll('.dropdown.add_units').forEach(dropdown => {
|
||||
$(dropdown).dropdown({
|
||||
onChange: (value, text, $selectedItem) => {
|
||||
const unitPromotions = this.promotions.filter(promotion => promotion.units.length > 0)
|
||||
const promotions = unitPromotions.filter(promotion => promotion.units.filter(unit => unit.id === parseInt(value)).length > 0)
|
||||
$selectedItem.parent().parent().parent().parent().find('.add_promotions_unit')
|
||||
.dropdown('change values', promotions.map(promotion => {
|
||||
return {
|
||||
value: promotion.id,
|
||||
name: promotion.description,
|
||||
text: promotion.description,
|
||||
}
|
||||
}))
|
||||
}
|
||||
})
|
||||
})
|
||||
this.components.units.querySelectorAll('.dropdown.add_promotions_unit').forEach(dropdown => {
|
||||
$(dropdown).dropdown()
|
||||
})
|
||||
this.components.units.querySelectorAll('.remove_unit').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const idx = Number(button.dataset.id)
|
||||
this.remove(idx)
|
||||
})
|
||||
})
|
||||
},
|
||||
unit: unit => {
|
||||
let promotions = ''
|
||||
if (unit.type === 'departamento') {
|
||||
if (this.promotions.filter(promotion => promotion.units.length > 0).length > 0) {
|
||||
promotions = [
|
||||
'<div class="three wide field">',
|
||||
'<label>Promociones</label>',
|
||||
'<div class="ui multiple search selection dropdown add_promotions_unit">',
|
||||
'<input type="hidden" name="add_units_promotions[]" />',
|
||||
'<i class="dropdown icon"></i>',
|
||||
'<div class="default text">Promociones</div>',
|
||||
'<div class="menu">',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
}
|
||||
}
|
||||
return [
|
||||
'<div class="fields">',
|
||||
'<div class="four wide field">',
|
||||
`<label>${unit.type.charAt(0).toUpperCase() + unit.type.slice(1)}</label>`,
|
||||
`<div class="ui search selection dropdown add_units">`,
|
||||
'<input type="hidden" name="add_units[]" />',
|
||||
'<i class="dropdown icon"></i>',
|
||||
`<div class="default text">${unit.type.charAt(0).toUpperCase() + unit.type.slice(1)}</div>`,
|
||||
'<div class="menu">',
|
||||
this.data.types[unit.type].map(unit => {
|
||||
return `<div class="item" data-value="${unit.value}">${unit.name}</div>`
|
||||
}).join(''),
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="three wide field">',
|
||||
'<label>Valor</label>',
|
||||
'<div class="ui right labeled input">',
|
||||
'<input type="number" name="add_units_value[]" placeholder="Valor" />',
|
||||
'<div class="ui basic label">UF</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
promotions,
|
||||
'<div class="field">',
|
||||
'<label></label>',
|
||||
`<button class="ui red tiny icon button remove_unit" type="button" data-id="${unit.idx}"><i class="trash icon"></i></button>`,
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join('')
|
||||
})
|
||||
|
||||
body = this.validateBody(body)
|
||||
if (body.keys().toArray().length === 0) {
|
||||
alert('No hay datos.')
|
||||
return
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.data.units = []
|
||||
this.draw.units()
|
||||
}
|
||||
add(type) {
|
||||
const idx = Math.max(this.data.units.length, 0, Math.max(...this.data.units.map(unit => unit.idx)) + 1)
|
||||
this.data.units.push({idx, type})
|
||||
this.draw.units()
|
||||
}
|
||||
remove(idx) {
|
||||
this.data.units = this.data.units.filter(unit => unit.idx !== idx)
|
||||
this.draw.units()
|
||||
|
||||
const method = 'post'
|
||||
return APIClient.fetch(url, {method, body}).then(response => response.json()).then(json => {
|
||||
if (json.success) {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
setup() {
|
||||
this.components.buttons_holder = document.getElementById(this.ids.buttons_holder)
|
||||
this.components.units = document.getElementById(this.ids.units)
|
||||
this.draw.buttons()
|
||||
super.setup()
|
||||
|
||||
this.components.$modal.modal({
|
||||
onApprove: () => {
|
||||
this.add()
|
||||
}
|
||||
})
|
||||
this.components.form.addEventListener('submit', event => {
|
||||
event.preventDefault()
|
||||
this.add()
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
class AddReservationModal {
|
||||
/*class AddReservationModal {
|
||||
ids = {
|
||||
modal: '',
|
||||
form: '',
|
||||
@ -429,6 +128,7 @@
|
||||
project_name: null,
|
||||
unit_buttons: null,
|
||||
units: null,
|
||||
payments: null,
|
||||
$loader: null
|
||||
}
|
||||
data = {
|
||||
@ -480,8 +180,8 @@
|
||||
this.get.promotions(project_id).then(promotions => {
|
||||
this.components.promotions.data.promotions = promotions.map(promotion => {
|
||||
return {
|
||||
text: promotion.name,
|
||||
name: promotion.name,
|
||||
text: promotion.description,
|
||||
name: promotion.description,
|
||||
value: promotion.id
|
||||
}
|
||||
})
|
||||
@ -509,6 +209,7 @@
|
||||
this.components.form.reset()
|
||||
this.components.promotions.reset()
|
||||
this.components.units.reset()
|
||||
this.components.payments.reset()
|
||||
}
|
||||
add() {
|
||||
const url = '/api/ventas/reservation/add'
|
||||
@ -524,6 +225,16 @@
|
||||
const birthdate = this.components.$birthdate.calendar('get date')
|
||||
body.set('add_buyer_birthdate', [birthdate.getFullYear(), birthdate.getMonth() + 1, birthdate.getDate()].join('-'))
|
||||
|
||||
if (this.components.payments.components.pie.$checkbox.checkbox('is unchecked')) {
|
||||
body.delete('add_pie')
|
||||
body.delete('add_cuotas')
|
||||
}
|
||||
body.delete('add_has_pie')
|
||||
if (this.components.payments.components.credit.$checkbox.checkbox('is unchecked')) {
|
||||
body.delete('add_credit')
|
||||
}
|
||||
body.delete('add_has_credit')
|
||||
|
||||
body.delete('comuna')
|
||||
body.delete('region')
|
||||
body.delete('broker')
|
||||
@ -672,6 +383,9 @@
|
||||
}
|
||||
fill = {
|
||||
user: user => {
|
||||
if (typeof user === 'undefined' || user === null) {
|
||||
return
|
||||
}
|
||||
const form = this.components.form
|
||||
form.querySelector('input[name="add_buyer_name"]').value = user.nombres || ''
|
||||
form.querySelector('input[name="add_buyer_last_name"]').value = user.apellidoPaterno || ''
|
||||
@ -753,6 +467,7 @@
|
||||
this.components.projects = document.getElementById(this.ids.projects)
|
||||
this.components.project_name = document.getElementById(this.ids.project_name)
|
||||
this.components.units = new AddModalUnits(this)
|
||||
this.components.payments = new AddModalPayments()
|
||||
|
||||
this.components.$modal.modal({
|
||||
onApprove: () => {
|
||||
@ -799,6 +514,6 @@
|
||||
this.components.$broker.dropdown()
|
||||
this.components.$loader = $(`#${this.ids.loader}`)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@ -0,0 +1,164 @@
|
||||
<form class="ui form" id="{{ $prefix }}_reservation_form">
|
||||
<input type="hidden" name="{{ $prefix }}_project_id" />
|
||||
<div class="three wide required field">
|
||||
<label>Fecha</label>
|
||||
<div class="ui calendar" id="{{ $prefix }}_date">
|
||||
<div class="ui icon input">
|
||||
<i class="calendar icon"></i>
|
||||
<input type="text" name="{{ $prefix }}_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide required field">
|
||||
<label>RUT</label>
|
||||
<div class="ui right labeled input" id="{{ $prefix }}_rut">
|
||||
<input type="text" name="{{ $prefix }}_buyer_rut" placeholder="RUT" />
|
||||
<div class="ui basic label">-<span id="{{ $prefix }}_digit"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label></label>
|
||||
<div class="ui inline loader" id="{{ $prefix }}_rut_loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide required field">
|
||||
<label>Nombre</label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_name" placeholder="Nombre" />
|
||||
</div>
|
||||
<div class="six wide required field">
|
||||
<label>Apellidos</label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_last_name" placeholder="Apellido Paterno" />
|
||||
</div>
|
||||
<div class="six wide field">
|
||||
<label></label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_last_name2" placeholder="Apellido Materno" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide field">
|
||||
<label>Dirección</label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_address_street" placeholder="Calle" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label></label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_address_number" placeholder="N°" />
|
||||
</div>
|
||||
<div class="three wide field">
|
||||
<label></label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_address_extra" placeholder="Otros Detalles" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="three wide field">
|
||||
<label>Comuna</label>
|
||||
<div class="ui search selection dropdown" id="{{ $prefix }}_comuna">
|
||||
<input type="hidden" name="comuna" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Comuna</div>
|
||||
<div class="menu"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="seven wide field">
|
||||
<label>Región</label>
|
||||
<div class="ui search selection dropdown" id="{{ $prefix }}_region">
|
||||
<input type="hidden" name="region" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Región</div>
|
||||
<div class="menu">
|
||||
@foreach($regions as $region)
|
||||
<div class="item" data-value="{{$region->id}}">{{$region->numeral}} - {{$region->descripcion}}</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label>Teléfono</label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_phone" placeholder="Telefono" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Correo</label>
|
||||
<div class="ui labeled input">
|
||||
<input type="text" name="{{ $prefix }}_buyer_email_name" placeholder="Correo" />
|
||||
<div class="ui basic label">@</div>
|
||||
<input type="text" name="{{ $prefix }}_buyer_email_domain" placeholder="Dominio" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label>Estado Civil</label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_marital_status" placeholder="Estado Civil" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Profesión</label>
|
||||
<input type="text" name="{{ $prefix }}_buyer_profession" placeholder="Profesión" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Fecha de Nacimiento</label>
|
||||
<div class="ui calendar" id="{{ $prefix }}_birthdate">
|
||||
<div class="ui icon input">
|
||||
<i class="calendar icon"></i>
|
||||
<input type="text" name="birthdate" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="six wide field">
|
||||
<label>Operador *</label>
|
||||
<div class="ui clearable search selection dropdown" id="{{ $prefix }}_broker">
|
||||
<input type="hidden" name="{{ $prefix }}_broker" />
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Operador</div>
|
||||
<div class="menu"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Agregar Promoción</label>
|
||||
<button type="button" class="ui icon button" id="{{ $prefix }}_promotion">
|
||||
<i class="plus icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="{{ $prefix }}_promotions"></div>
|
||||
<h4 class="ui dividing header">Unidades <span id="{{ $prefix }}_project_name"></span></h4>
|
||||
<div class="fields" id="{{ $prefix }}_unit_buttons"></div>
|
||||
<div id="{{ $prefix }}_units"></div>
|
||||
<h4 class="ui dividing header">Forma de Pago</h4>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="{{ $prefix }}_payment_has_pie" id="{{ $prefix }}_payment_has_pie" />
|
||||
<label>¿Tiene Pie?</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" id="{{ $prefix }}_payment_pie">
|
||||
<label>Pie</label>
|
||||
<div class="ui right labeled input">
|
||||
<input type="text" name="{{ $prefix }}_payment_pie" />
|
||||
<div class="ui basic label">UF</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" id="{{ $prefix }}_payment_cuotas">
|
||||
<label>Cuotas</label>
|
||||
<input type="text" name="{{ $prefix }}_payment_cuotas" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="{{ $prefix }}_payment_has_credit" id="{{ $prefix }}_payment_has_credit" />
|
||||
<label>¿Tiene Crédito?</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" id="{{ $prefix }}_payment_credit">
|
||||
<label>Crédito</label>
|
||||
<div class="ui right labeled input">
|
||||
<input type="text" name="{{ $prefix }}_payment_credit" />
|
||||
<div class="ui basic label">UF</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -0,0 +1,16 @@
|
||||
<div class="ui modal" id="{{ $prefix }}_reservation_modal">
|
||||
<div class="header">
|
||||
{{ $modalTitle }}
|
||||
</div>
|
||||
<div class="content">
|
||||
@include('ventas.reservations.modal.common.form', ['prefix' => $prefix])
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui cancel button">
|
||||
Cancelar
|
||||
</div>
|
||||
<div class="ui green ok button">
|
||||
{{ $okText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,373 @@
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class ReservationModal {
|
||||
prefix
|
||||
ids = {
|
||||
modal: '',
|
||||
form: '',
|
||||
date: '',
|
||||
rut: '',
|
||||
digit: '',
|
||||
birthdate: '',
|
||||
comuna: '',
|
||||
region: '',
|
||||
broker: '',
|
||||
promotion_button: '',
|
||||
promotions: '',
|
||||
projects: '',
|
||||
project_name: '',
|
||||
unit_buttons: '',
|
||||
units: ''
|
||||
}
|
||||
components = {
|
||||
$modal: null,
|
||||
form: null,
|
||||
$date: null,
|
||||
rut: null,
|
||||
digit: null,
|
||||
$birthdate: null,
|
||||
$comuna: null,
|
||||
$region: null,
|
||||
$broker: null,
|
||||
promotion_button: null,
|
||||
promotions: null,
|
||||
projects: null,
|
||||
project_name: null,
|
||||
unit_buttons: null,
|
||||
units: null,
|
||||
payments: null,
|
||||
$loader: null
|
||||
}
|
||||
data = {
|
||||
current_project: null,
|
||||
comunas: {},
|
||||
brokers: {},
|
||||
promotions: {},
|
||||
unit_buttons: [],
|
||||
units: {},
|
||||
added_units: {},
|
||||
current_user: null
|
||||
}
|
||||
maps = {
|
||||
unit_types: {
|
||||
departamento: 'building',
|
||||
estacionamiento: 'car',
|
||||
bodega: 'warehouse',
|
||||
terraza: 'tree',
|
||||
}
|
||||
}
|
||||
constructor({projects_id, prefix, components}) {
|
||||
this.prefix = prefix
|
||||
this.ids = {
|
||||
modal: `${this.prefix}_reservation_modal`,
|
||||
form: `${this.prefix}_reservation_form`,
|
||||
date: `${this.prefix}_date`,
|
||||
rut: `${this.prefix}_rut`,
|
||||
digit: `${this.prefix}_digit`,
|
||||
birthdate: `${this.prefix}_birthdate`,
|
||||
comuna: `${this.prefix}_comuna`,
|
||||
region: `${this.prefix}_region`,
|
||||
broker: `${this.prefix}_broker`,
|
||||
promotion_button: `${this.prefix}_promotion`,
|
||||
promotions: `${this.prefix}_promotions`,
|
||||
projects: projects_id,
|
||||
project_name: `${this.prefix}_project_name`,
|
||||
unit_buttons: `${this.prefix}_unit_buttons`,
|
||||
units: `${this.prefix}_units`,
|
||||
loader: `${this.prefix}_rut_loader`
|
||||
}
|
||||
Object.keys(components).forEach(key => {
|
||||
this.components[key] = components[key]
|
||||
})
|
||||
this.setup()
|
||||
}
|
||||
load(project_id) {
|
||||
this.reset()
|
||||
this.data.current_project = project_id
|
||||
this.components.project_name.textContent = this.components.projects.querySelector(`.item[data-id="${project_id}"]`).textContent
|
||||
this.components.form.querySelector(`input[name="${this.prefix}_project_id"]`).value = project_id
|
||||
|
||||
this.get.brokers(project_id)
|
||||
this.get.promotions(project_id).then(() => {
|
||||
const promotions = structuredClone(reservations.data.promotions[project_id])
|
||||
this.components.promotions.data.promotions = promotions.map(promotion => {
|
||||
return {
|
||||
text: promotion.description,
|
||||
name: promotion.description,
|
||||
value: promotion.id
|
||||
}
|
||||
})
|
||||
this.components.promotions.draw.promotions()
|
||||
})
|
||||
this.get.units(project_id).then(() => {
|
||||
const unitTypes = structuredClone(reservations.data.units[project_id])
|
||||
Object.entries(unitTypes).map(([type, units]) => {
|
||||
units = units.map(unit => {
|
||||
return {
|
||||
text: unit.descripcion,
|
||||
name: unit.descripcion,
|
||||
value: unit.id
|
||||
}
|
||||
})
|
||||
units.sort((a, b) => {
|
||||
return parseInt(a.text) - parseInt(b.text)
|
||||
})
|
||||
unitTypes[type] = units
|
||||
})
|
||||
this.components.units.data.types = unitTypes
|
||||
this.components.units.draw.buttons()
|
||||
})
|
||||
}
|
||||
reset() {
|
||||
this.components.form.reset()
|
||||
this.components.promotions.reset()
|
||||
this.components.units.reset()
|
||||
this.components.payments.reset()
|
||||
}
|
||||
get = {
|
||||
comunas: region_id => {
|
||||
if (region_id in this.data.comunas) {
|
||||
this.components.$comuna.dropdown('change values', this.data.comunas[region_id])
|
||||
if (this.data.current_user !== null && this.data.current_user?.direccion?.comuna !== null) {
|
||||
this.components.$comuna.dropdown('set selected', this.data.current_user.direccion.comuna.id)
|
||||
}
|
||||
return
|
||||
}
|
||||
return reservations.get.comunas(region_id).then(comunas => {
|
||||
this.data.comunas[region_id] = comunas
|
||||
this.components.$comuna.dropdown('change values', this.data.comunas[region_id])
|
||||
|
||||
if (this.data.current_user !== null && this.data.current_user?.direccion?.comuna !== null) {
|
||||
this.components.$comuna.dropdown('set selected', this.data.current_user.direccion.comuna.id)
|
||||
}
|
||||
})
|
||||
},
|
||||
brokers: project_id => {
|
||||
if (project_id in this.data.brokers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(this.data.brokers[project_id])
|
||||
})
|
||||
}
|
||||
return reservations.get.brokers(project_id).then(() => {
|
||||
this.data.brokers[project_id] = reservations.data.brokers[project_id]
|
||||
this.fill.brokers()
|
||||
})
|
||||
},
|
||||
/*broker: (broker_rut) => {
|
||||
const uri = `/api/proyectos/broker/${broker_rut}`
|
||||
return APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
if (!('broker' in json)) {
|
||||
return []
|
||||
}
|
||||
return json.broker
|
||||
})
|
||||
},*/
|
||||
promotions: project_id => {
|
||||
if (project_id in this.data.promotions) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(this.data.promotions[project_id])
|
||||
})
|
||||
}
|
||||
return reservations.get.promotions(project_id).then(promotions => {
|
||||
this.data.promotions[project_id] = promotions
|
||||
})
|
||||
},
|
||||
units: project_id => {
|
||||
if (project_id in this.data.units) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(this.data.units[project_id])
|
||||
})
|
||||
}
|
||||
return reservations.get.units(project_id).then(units => {
|
||||
this.data.units[project_id] = units
|
||||
})
|
||||
},
|
||||
user: rut => {
|
||||
if (this.data.current_user !== null && this.data.current_user?.rut === rut) {
|
||||
return this.data.current_user
|
||||
}
|
||||
this.loader.show()
|
||||
const uri = `/api/persona/${rut}`
|
||||
return APIClient.fetch(uri).then(response => response.json()).then(json => {
|
||||
this.loader.hide()
|
||||
if (!json.success) {
|
||||
return
|
||||
}
|
||||
this.data.current_user = json.persona
|
||||
return json.persona
|
||||
})
|
||||
}
|
||||
}
|
||||
fill = {
|
||||
user: user => {
|
||||
if (typeof user === 'undefined' || user === null) {
|
||||
return
|
||||
}
|
||||
const form = this.components.form
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_name"]`).value = user.nombres || ''
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_last_name"]`).value = user.apellidoPaterno || ''
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_last_name2"]`).value = user.apellidoMaterno || ''
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_address_street"]`).value = user.datos?.direccion?.calle || ''
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_address_number"]`).value = user.datos?.direccion?.numero || ''
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_address_extra"]`).value = user.datos?.direccion?.extra || ''
|
||||
if (parseInt(this.components.$region.dropdown('get value')) !== user.datos?.direccion?.comuna?.provincia?.region?.id) {
|
||||
this.components.$region.dropdown('set selected', user.datos?.direccion?.comuna?.provincia?.region?.id)
|
||||
} else {
|
||||
this.components.$comuna.dropdown('set selected', user.datos?.direccion?.comuna?.id)
|
||||
}
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_phone"]`).value = user.datos?.telefono || ''
|
||||
const email_parts = user.datos?.email?.split('@') || []
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_email_name"]`).value = email_parts[0] || ''
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_email_domain"]`).value = email_parts[1] || ''
|
||||
if (user.datos !== null && 'estadoCivil' in user.datos) {
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_marital_status"]`).value = user.datos?.estadoCivil.charAt(0).toUpperCase() + user.datos?.estadoCivil.slice(1)
|
||||
} else {
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_marital_status"]`).value = ''
|
||||
}
|
||||
if (user.datos !== null &&'ocupacion' in user.datos) {
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_profession"]`).value = user.datos?.ocupacion.charAt(0).toUpperCase() + user.datos?.ocupacion.slice(1).toLowerCase()
|
||||
} else {
|
||||
form.querySelector(`input[name="${this.prefix}_buyer_profession"]`).value = ''
|
||||
}
|
||||
if (user.datos !== null &&'fechaNacimiento' in user.datos) {
|
||||
this.components.$birthdate.calendar('set date', user.datos?.fechaNacimiento)
|
||||
}
|
||||
},
|
||||
brokers: () => {
|
||||
this.components.$broker.dropdown('change values', this.data.brokers[this.data.current_project])
|
||||
},
|
||||
units: () => {
|
||||
const buttons = []
|
||||
Object.entries(this.maps.unit_types).forEach(([type, map]) => {
|
||||
if (!(type in this.data.units[this.data.current_project])) {
|
||||
return
|
||||
}
|
||||
buttons.push(`<div class="field"><div class="ui icon button" data-type="${type}" title="${type.charAt(0).toUpperCase() + type.slice(1)}"><i class="plus icon"></i><i class="${map} icon"></i></div></div>`)
|
||||
})
|
||||
|
||||
this.components.unit_buttons.innerHTML = buttons.join('')
|
||||
this.components.unit_buttons.querySelectorAll('.ui.icon.button').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
this.units.add(button.dataset.type)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
watch = {
|
||||
region: (value, text, $choice) => {
|
||||
this.get.comunas(value)
|
||||
},
|
||||
}
|
||||
loader = {
|
||||
show: () => {
|
||||
this.components.$loader.show()
|
||||
},
|
||||
hide: () => {
|
||||
this.components.$loader.hide()
|
||||
}
|
||||
}
|
||||
validateBody(body) {
|
||||
const fieldMap = {
|
||||
'project_id': this.reservation.project_id,
|
||||
'date': this.reservation.date,
|
||||
'buyer_rut': this.reservation.buyer.rut.toString(),
|
||||
'buyer_digit': this.reservation.buyer.digito,
|
||||
'buyer_name': this.reservation.buyer.nombres,
|
||||
'buyer_last_name': this.reservation.buyer.apellidoPaterno,
|
||||
'buyer_last_name2': this.reservation.buyer.apellidoMaterno,
|
||||
'buyer_address_street': this.reservation.buyer.datos?.direccion?.calle,
|
||||
'buyer_address_number': this.reservation.buyer.datos?.direccion?.numero?.toString(),
|
||||
'buyer_address_extra': this.reservation.buyer.datos?.direccion?.extra,
|
||||
'buyer_address_comuna_id': this.reservation.buyer.datos?.direccion?.comuna?.id?.toString(),
|
||||
'buyer_phone': this.reservation.buyer.datos?.telefono,
|
||||
'buyer_email': this.reservation.buyer.datos?.email,
|
||||
'buyer_birthdate': this.reservation.buyer.datos?.fechaNacimiento,
|
||||
'buyer_marital_status': this.reservation.buyer.datos?.estadoCivil,
|
||||
'buyer_profession': this.reservation.buyer.datos?.ocupacion,
|
||||
'broker_rut': this.reservation.broker?.rut?.toString(),
|
||||
'payment_pie': this.reservation.payment?.pie?.valor?.toString(),
|
||||
'payment_cuotas': this.reservation.payment?.pie?.cuotas?.toString(),
|
||||
'payment_credit': this.reservation.payment?.credito?.valor?.toString(),
|
||||
'promotions[]': this.reservation.promotions?.map(p => p.id.toString()),
|
||||
'units[]': this.reservation.units?.map(u => u.unit.id.toString()),
|
||||
'units_value[]': this.reservation.units?.map(u => u.value.toString()),
|
||||
}
|
||||
Object.entries(fieldMap).forEach(([key, value]) => {
|
||||
if (key.includes('[]') && body.has(`${this.prefix}_${key}`)) {
|
||||
const values = body.getAll(`${this.prefix}_${key}`)
|
||||
if (values.length !== value.length) {
|
||||
return
|
||||
}
|
||||
const diff = values.some(v => !value.includes(v))
|
||||
if (diff.length > 0) {
|
||||
return
|
||||
}
|
||||
if (JSON.stringify(values.sort()) !== JSON.stringify(value.sort())) {
|
||||
return
|
||||
}
|
||||
body.delete(`${this.prefix}_${key}`)
|
||||
return
|
||||
}
|
||||
if (body.has(`${this.prefix}_${key}`) && value === body.get(`${this.prefix}_${key}`)) {
|
||||
body.delete(`${this.prefix}_${key}`)
|
||||
}
|
||||
})
|
||||
|
||||
return body
|
||||
}
|
||||
show() {
|
||||
this.reset()
|
||||
this.components.$modal.modal('show')
|
||||
}
|
||||
setup() {
|
||||
this.components.$modal = $(`#${this.ids.modal}`)
|
||||
this.components.form = document.getElementById(this.ids.form)
|
||||
this.components.$date = $(`#${this.ids.date}`)
|
||||
this.components.rut = document.getElementById(this.ids.rut)
|
||||
this.components.digit = document.getElementById(this.ids.digit)
|
||||
this.components.$birthdate = $(`#${this.ids.birthdate}`)
|
||||
this.components.$comuna = $(`#${this.ids.comuna}`)
|
||||
this.components.$region = $(`#${this.ids.region}`)
|
||||
this.components.$broker = $(`#${this.ids.broker}`)
|
||||
this.components.projects = document.getElementById(this.ids.projects)
|
||||
this.components.project_name = document.getElementById(this.ids.project_name)
|
||||
this.components.units.parent = this
|
||||
|
||||
const cdo = structuredClone(calendar_date_options)
|
||||
cdo['initialDate'] = new Date()
|
||||
cdo['maxDate'] = new Date()
|
||||
this.components.$date.calendar(cdo)
|
||||
const rutInput = this.components.rut.querySelector('input')
|
||||
rutInput.addEventListener('input', event => {
|
||||
const value = event.currentTarget.value.replace(/\D/g, '')
|
||||
if (value.length <= 3) {
|
||||
return
|
||||
}
|
||||
this.components.digit.textContent = Rut.digitoVerificador(value)
|
||||
})
|
||||
rutInput.addEventListener('blur', event => {
|
||||
const value = event.currentTarget.value.replace(/\D/g, '')
|
||||
if (value.length <= 3) {
|
||||
return
|
||||
}
|
||||
event.currentTarget.value = Rut.format(value)
|
||||
this.get.user(value).then(user => {
|
||||
this.fill.user(user)
|
||||
})
|
||||
})
|
||||
const cdo2 = structuredClone(cdo)
|
||||
cdo2['initialDate'].setFullYear(cdo2['initialDate'].getFullYear() - 18)
|
||||
cdo2['maxDate'].setFullYear(cdo2['maxDate'].getFullYear() - 18)
|
||||
this.components.$birthdate.calendar(cdo2)
|
||||
this.components.$region.dropdown({
|
||||
fireOnInit: true,
|
||||
onChange: this.watch.region
|
||||
})
|
||||
this.components.$region.dropdown('set selected', 13)
|
||||
this.components.$comuna.dropdown()
|
||||
this.components.$broker.dropdown()
|
||||
this.components.$loader = $(`#${this.ids.loader}`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@ -0,0 +1,131 @@
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class ModalPayments {
|
||||
prefix
|
||||
ids = {
|
||||
pie: {
|
||||
checkbox: '',
|
||||
value: '',
|
||||
installments: ''
|
||||
},
|
||||
credit: {
|
||||
checkbox: '',
|
||||
value: ''
|
||||
}
|
||||
}
|
||||
components = {
|
||||
pie: {
|
||||
$checkbox: null,
|
||||
value: null,
|
||||
installments: null
|
||||
},
|
||||
credit: {
|
||||
$checkbox: null,
|
||||
value: null
|
||||
}
|
||||
}
|
||||
data = {
|
||||
pie: {
|
||||
value: '',
|
||||
installments: ''
|
||||
},
|
||||
credit: {
|
||||
value: ''
|
||||
}
|
||||
}
|
||||
constructor(prefix) {
|
||||
this.prefix = prefix
|
||||
this.ids = {
|
||||
pie: {
|
||||
checkbox: `${this.prefix}_payment_has_pie`,
|
||||
value: `${this.prefix}_payment_pie`,
|
||||
installments: `${this.prefix}_payment_cuotas`
|
||||
},
|
||||
credit: {
|
||||
checkbox: `${this.prefix}_payment_has_credit`,
|
||||
value: `${this.prefix}_payment_credit`
|
||||
}
|
||||
}
|
||||
this.setup()
|
||||
}
|
||||
reset() {
|
||||
this.components.pie.$checkbox.prop('checked', false)
|
||||
this.components.pie.value.value = ''
|
||||
this.components.pie.installments.value = ''
|
||||
this.components.credit.$checkbox.prop('checked', false)
|
||||
this.components.credit.value.value = ''
|
||||
}
|
||||
show = {
|
||||
pie: () => {
|
||||
this.components.pie.value.style.display = this.data.pie.value
|
||||
this.components.pie.installments.style.display = this.data.pie.installments
|
||||
},
|
||||
credit: () => {
|
||||
this.components.credit.value.style.display = this.data.credit.value
|
||||
}
|
||||
}
|
||||
hide = {
|
||||
pie: () => {
|
||||
this.components.pie.value.style.display = 'none'
|
||||
this.components.pie.installments.style.display = 'none'
|
||||
},
|
||||
credit: () => {
|
||||
this.components.credit.value.style.display = 'none'
|
||||
},
|
||||
all: () => {
|
||||
this.hide.pie()
|
||||
this.hide.credit()
|
||||
}
|
||||
}
|
||||
fill(payment) {
|
||||
if ('pie' in payment && payment.pie !== null) {
|
||||
this.components.pie.$checkbox.prop('checked', true)
|
||||
this.show.pie()
|
||||
this.components.pie.value.querySelector('input').value = payment.pie.valor
|
||||
this.components.pie.installments.querySelector('input').value = payment.pie.cuotas
|
||||
}
|
||||
if ('credito' in payment && payment.credito !== null) {
|
||||
this.components.credit.$checkbox.prop('checked', true)
|
||||
this.show.credit()
|
||||
this.components.credit.value.querySelector('input').value = payment.credito.valor
|
||||
}
|
||||
/*if ('subsidio' in payment && payment.subsidio !== null) {
|
||||
this.components.subsidio.$checkbox.prop('checked', true)
|
||||
this.components.subsidio.subsidy.value.value = payment.subsidio.subsidio.valor
|
||||
this.components.subsidio.savings.value.value = payment.subsidio.ahorro.valor
|
||||
this.show.subsidio()
|
||||
}*/
|
||||
}
|
||||
setup() {
|
||||
this.components.pie.$checkbox = $(`#${this.ids.pie.checkbox}`)
|
||||
this.components.pie.value = document.getElementById(this.ids.pie.value)
|
||||
this.components.pie.installments = document.getElementById(this.ids.pie.installments)
|
||||
this.components.credit.$checkbox = $(`#${this.ids.credit.checkbox}`)
|
||||
this.components.credit.value = document.getElementById(this.ids.credit.value)
|
||||
|
||||
this.components.pie.$checkbox.checkbox()
|
||||
this.components.pie.$checkbox.change(changeEvent => {
|
||||
if (this.components.pie.$checkbox.is(':checked')) {
|
||||
this.show.pie()
|
||||
return
|
||||
}
|
||||
this.hide.pie()
|
||||
})
|
||||
this.components.credit.$checkbox.checkbox()
|
||||
this.components.credit.$checkbox.change(changeEvent => {
|
||||
if (this.components.credit.$checkbox.is(':checked')) {
|
||||
this.show.credit()
|
||||
return
|
||||
}
|
||||
this.hide.credit()
|
||||
})
|
||||
|
||||
this.data.pie.value = this.components.pie.value.style.display
|
||||
this.data.pie.installments = this.components.pie.installments.style.display
|
||||
this.data.credit.value = this.components.credit.value.style.display
|
||||
|
||||
this.hide.all()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@ -0,0 +1,117 @@
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class ModalPromotions {
|
||||
prefix
|
||||
ids = {
|
||||
button: '',
|
||||
elements: ''
|
||||
}
|
||||
data = {
|
||||
promotions: [],
|
||||
selected: []
|
||||
}
|
||||
components = {
|
||||
button: null,
|
||||
promotions: null,
|
||||
}
|
||||
display = {
|
||||
button: ''
|
||||
}
|
||||
|
||||
constructor(prefix) {
|
||||
this.prefix = prefix
|
||||
this.ids = {
|
||||
button: `${prefix}_promotion`,
|
||||
elements: `${prefix}_promotions`
|
||||
}
|
||||
this.setup()
|
||||
}
|
||||
add() {
|
||||
const idx = Math.max(this.data.selected.length, 0, Math.max(...this.data.selected) + 1)
|
||||
this.data.selected.push(idx)
|
||||
this.draw.promotions()
|
||||
return idx
|
||||
}
|
||||
reset() {
|
||||
this.data.selected = []
|
||||
this.draw.promotions()
|
||||
}
|
||||
remove(idx) {
|
||||
this.data.selected = this.data.selected.filter(promotion => promotion !== idx)
|
||||
this.draw.promotions()
|
||||
}
|
||||
draw = {
|
||||
promotion: idx => {
|
||||
const promotions = this.data.promotions.map(promotion => {
|
||||
return `<div class="item" data-value="${promotion.value}">${promotion.name}</div>`
|
||||
})
|
||||
return [
|
||||
`<div class="fields promotion" data-id="${idx}">`,
|
||||
'<div class="three wide field">',
|
||||
'<label>Promoción</label>',
|
||||
`<div class="ui search selection dropdown">`,
|
||||
`<input type="hidden" name="${this.prefix}_promotions[]" />`,
|
||||
'<i class="dropdown icon"></i>',
|
||||
'<div class="default text">Promoción</div>',
|
||||
`<div class="menu">${promotions.join('')}</div>`,
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="two wide field">',
|
||||
'<label></label>',
|
||||
`<button class="ui red tiny icon button remove_${this.prefix}_promotion" type="button" data-id="${idx}"><i class="trash icon"></i></button>`,
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
},
|
||||
promotions: () => {
|
||||
if (this.data.promotions.length === 0) {
|
||||
this.components.button.parentElement.style.display = 'none'
|
||||
this.components.promotions.innerHTML = ''
|
||||
return
|
||||
}
|
||||
this.components.button.parentElement.style.display = this.display.button
|
||||
if (this.data.selected.length === 0) {
|
||||
return
|
||||
}
|
||||
this.components.promotions.innerHTML = this.data.selected.map(idx => {
|
||||
return this.draw.promotion(idx)
|
||||
}).join('')
|
||||
this.components.promotions.querySelectorAll('.dropdown').forEach(dropdown => {
|
||||
$(dropdown).dropdown()
|
||||
})
|
||||
this.components.promotions.querySelectorAll(`.remove_${this.prefix}_promotion`).forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const idx = Number(button.dataset.id)
|
||||
this.remove(idx)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
async fill(promotions) {
|
||||
if (reservations.locks.promotions !== null) {
|
||||
await reservations.locks.promotions
|
||||
}
|
||||
const idxs = []
|
||||
promotions.forEach(promotion => {
|
||||
const idx = this.add()
|
||||
idxs.push(idx)
|
||||
})
|
||||
let i = 0
|
||||
promotions.forEach(promotion => {
|
||||
const idx = idxs[i ++]
|
||||
const dropdown = $(this.components.promotions.querySelector(`div.promotion[data-id="${idx}"]`).querySelector('div.dropdown'))
|
||||
dropdown.dropdown('set selected', promotion.id)
|
||||
})
|
||||
}
|
||||
setup() {
|
||||
this.components.button = document.getElementById(this.ids.button)
|
||||
this.components.promotions = document.getElementById(this.ids.elements)
|
||||
this.components.button.addEventListener('click', () => {
|
||||
this.add()
|
||||
})
|
||||
this.display.button = this.components.button.parentElement.style.display
|
||||
this.draw.promotions()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@ -0,0 +1,189 @@
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class ModalUnits {
|
||||
prefix
|
||||
parent = null
|
||||
ids = {
|
||||
buttons_holder: '',
|
||||
units: ''
|
||||
}
|
||||
data = {
|
||||
button_map: {},
|
||||
types: {},
|
||||
units: [],
|
||||
}
|
||||
components = {
|
||||
buttons_holder: null,
|
||||
units: null,
|
||||
}
|
||||
|
||||
constructor({parent, prefix}) {
|
||||
this.prefix = prefix
|
||||
this.parent = parent
|
||||
this.ids = {
|
||||
buttons_holder: `${this.prefix}_unit_buttons`,
|
||||
units: `${this.prefix}_units`
|
||||
}
|
||||
this.data.button_map = {
|
||||
'departamento': 'building',
|
||||
'estacionamiento': 'car',
|
||||
'bodega': 'warehouse',
|
||||
'terraza': 'vector square'
|
||||
}
|
||||
this.setup()
|
||||
}
|
||||
get promotions() {
|
||||
return this.parent.data.promotions[this.parent.data.current_project]
|
||||
}
|
||||
draw = {
|
||||
button: type => {
|
||||
return [
|
||||
'<div class="field">',
|
||||
`<button class="ui icon button" type="button" data-type="${type}" title="${type.charAt(0).toUpperCase() + type.slice(1)}">`,
|
||||
'<i class="plus icon"></i>',
|
||||
`<i class="${this.data.button_map[type]} icon"></i>`,
|
||||
'</button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
},
|
||||
buttons: () => {
|
||||
const keys = Object.keys(this.data.types)
|
||||
if (keys.length === 0) {
|
||||
return
|
||||
}
|
||||
this.components.buttons_holder.innerHTML = keys.map(type => {
|
||||
return this.draw.button(type)
|
||||
}).join('')
|
||||
this.components.buttons_holder.querySelectorAll('.button').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const type = button.dataset.type
|
||||
this.add(type)
|
||||
})
|
||||
})
|
||||
},
|
||||
units: () => {
|
||||
if (this.data.units.length === 0) {
|
||||
this.components.units.innerHTML = ''
|
||||
return
|
||||
}
|
||||
this.components.units.innerHTML = this.data.units.map(unit => {
|
||||
return this.draw.unit(unit)
|
||||
}).join('')
|
||||
this.components.units.querySelectorAll(`.dropdown.${this.prefix}_units`).forEach(dropdown => {
|
||||
$(dropdown).dropdown({
|
||||
onChange: (value, text, $selectedItem) => {
|
||||
const unitPromotions = this.promotions.filter(promotion => promotion.units.length > 0)
|
||||
const promotions = unitPromotions.filter(promotion => promotion.units.filter(unit => unit.id === parseInt(value)).length > 0)
|
||||
$selectedItem.parent().parent().parent().parent().find(`.${this.prefix}_promotions_unit`)
|
||||
.dropdown('change values', promotions.map(promotion => {
|
||||
return {
|
||||
value: promotion.id,
|
||||
name: promotion.description,
|
||||
text: promotion.description,
|
||||
}
|
||||
}))
|
||||
}
|
||||
})
|
||||
})
|
||||
this.components.units.querySelectorAll(`.dropdown.${this.prefix}_promotions_unit`).forEach(dropdown => {
|
||||
$(dropdown).dropdown()
|
||||
})
|
||||
this.components.units.querySelectorAll('.remove_unit').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const idx = Number(button.dataset.id)
|
||||
this.remove(idx)
|
||||
})
|
||||
})
|
||||
},
|
||||
unit: unit => {
|
||||
let promotions = ''
|
||||
if (unit.type === 'departamento') {
|
||||
if (this.promotions.filter(promotion => promotion.units.length > 0).length > 0) {
|
||||
promotions = [
|
||||
'<div class="three wide field">',
|
||||
'<label>Promociones</label>',
|
||||
`<div class="ui multiple search selection dropdown ${this.prefix}_promotions_unit">`,
|
||||
`<input type="hidden" name="${this.prefix}_units_promotions[]" />`,
|
||||
'<i class="dropdown icon"></i>',
|
||||
'<div class="default text">Promociones</div>',
|
||||
'<div class="menu">',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
}
|
||||
}
|
||||
const type = unit.type.charAt(0).toUpperCase() + unit.type.slice(1)
|
||||
|
||||
return [
|
||||
`<div class="fields unit" data-id="${unit.idx}">`,
|
||||
'<div class="four wide field">',
|
||||
`<label>${type}</label>`,
|
||||
`<div class="ui search selection dropdown ${this.prefix}_units">`,
|
||||
`<input type="hidden" name="${this.prefix}_units[]" />`,
|
||||
'<i class="dropdown icon"></i>',
|
||||
`<div class="default text">${type}</div>`,
|
||||
'<div class="menu">',
|
||||
this.data.types[unit.type].map(u => {
|
||||
return `<div class="item" data-value="${u.value}">${u.name}</div>`
|
||||
}).join(''),
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="three wide field">',
|
||||
'<label>Valor</label>',
|
||||
'<div class="ui right labeled input">',
|
||||
`<input type="text" name="${this.prefix}_units_value[]" placeholder="Valor" />`,
|
||||
'<div class="ui basic label">UF</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
promotions,
|
||||
'<div class="field">',
|
||||
'<label></label>',
|
||||
`<button class="ui red tiny icon button remove_unit" type="button" data-id="${unit.idx}"><i class="trash icon"></i></button>`,
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join('')
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.data.units = []
|
||||
this.components.units.innerHTML = ''
|
||||
this.draw.units()
|
||||
}
|
||||
add(type) {
|
||||
const idx = Math.max(this.data.units.length, 0, Math.max(...this.data.units.map(unit => unit.idx)) + 1)
|
||||
this.data.units.push({idx, type})
|
||||
this.draw.units()
|
||||
return idx
|
||||
}
|
||||
remove(idx) {
|
||||
this.data.units = this.data.units.filter(unit => unit.idx !== idx)
|
||||
this.draw.units()
|
||||
}
|
||||
async fill(unitValues) {
|
||||
if (reservations.locks.units !== null) {
|
||||
await reservations.locks.units
|
||||
}
|
||||
const idxs = []
|
||||
unitValues.forEach(unitValue => {
|
||||
const type = unitValue.unit.proyecto_tipo_unidad.tipo_unidad.descripcion
|
||||
const idx = this.add(type)
|
||||
idxs.push(idx)
|
||||
})
|
||||
let i = 0
|
||||
unitValues.forEach(unitValue => {
|
||||
const idx = idxs[i ++]
|
||||
const field = this.components.units.querySelector(`div.unit[data-id="${idx}"]`)
|
||||
$(field.querySelector('div.dropdown.edit_units')).dropdown('set selected', unitValue.unit.id)
|
||||
field.querySelector('div.input').querySelector('input').value = unitValue.value
|
||||
})
|
||||
}
|
||||
setup() {
|
||||
this.components.buttons_holder = document.getElementById(this.ids.buttons_holder)
|
||||
this.components.units = document.getElementById(this.ids.units)
|
||||
this.draw.buttons()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
182
app/resources/views/ventas/reservations/modal/edit.blade.php
Normal file
182
app/resources/views/ventas/reservations/modal/edit.blade.php
Normal file
@ -0,0 +1,182 @@
|
||||
@include('ventas.reservations.modal.common.modal', ['prefix' => 'edit', 'modalTitle' => 'Editar Reserva', 'okText' => 'Guardar Cambios'])
|
||||
|
||||
@push('page_scripts')
|
||||
<script>
|
||||
class EditModalPromotions extends ModalPromotions {
|
||||
constructor() {
|
||||
super('edit');
|
||||
}
|
||||
}
|
||||
class EditModalUnits extends ModalUnits {
|
||||
constructor(parent) {
|
||||
super({prefix: 'edit', parent})
|
||||
}
|
||||
}
|
||||
class EditModalPayments extends ModalPayments {
|
||||
constructor() {
|
||||
super('edit');
|
||||
}
|
||||
}
|
||||
class EditReservationModal extends ReservationModal {
|
||||
reservation = null
|
||||
|
||||
constructor(projects_id) {
|
||||
super({projects_id, prefix: 'edit', components: {
|
||||
promotions: new EditModalPromotions(),
|
||||
units: new EditModalUnits(),
|
||||
payments: new EditModalPayments()
|
||||
}});
|
||||
}
|
||||
|
||||
show({type, reservation_id}) {
|
||||
const reservation = reservations.components.reservations[type].reservations.find(r => r.id === parseInt(reservation_id))
|
||||
super.show()
|
||||
this.fillReservation(reservation)
|
||||
}
|
||||
reset() {
|
||||
super.reset()
|
||||
this.reservation = null
|
||||
}
|
||||
|
||||
async fillReservation(reservation) {
|
||||
this.reservation = reservation
|
||||
this.components.form.querySelector(`#${this.prefix}_reservation_id`).value = reservation.id
|
||||
const date = new Date(reservation.date)
|
||||
date.setDate(date.getDate() + 1)
|
||||
this.components.$date.calendar('set date', date)
|
||||
this.components.rut.querySelector(`input[name="${this.prefix}_buyer_rut"]`).value = Rut.format(reservation.buyer.rut) || ''
|
||||
this.components.digit.textContent = reservation.buyer.digito || ''
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_name"]`).value = reservation.buyer.nombres || ''
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_last_name"]`).value = reservation.buyer.apellidoPaterno || ''
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_last_name2"]`).value = reservation.buyer.apellidoMaterno || ''
|
||||
if ('datos' in reservation.buyer && reservation.buyer.datos !== null) {
|
||||
if ('direccion' in reservation.buyer.datos && reservation.buyer.datos.direccion !== null) {
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_address_street"]`).value = reservation.buyer.datos.direccion.calle
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_address_number"]`).value = reservation.buyer.datos.direccion.numero
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_address_extra"]`).value = reservation.buyer.datos.direccion.extra
|
||||
this.components.$region.dropdown('set selected', reservation.buyer.datos.direccion.comuna.provincia.region.id)
|
||||
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||
const waitComuna = async () => {
|
||||
await delay(500)
|
||||
this.components.$comuna.dropdown('set selected', reservation.buyer.datos.direccion.comuna.id)
|
||||
}
|
||||
await waitComuna()
|
||||
}
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_phone"]`).value = reservation.buyer.datos.telefono || ''
|
||||
if ('email' in reservation.buyer.datos && reservation.buyer.datos.email !== null && reservation.buyer.datos.email.includes('@') && reservation.buyer.datos.email.length > 1) {
|
||||
const parts = reservation.buyer.datos.email.split('@')
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_email_name"]`).value = parts[0]
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_email_domain"]`).value = parts[1]
|
||||
}
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_marital_status"]`).value = reservation.buyer.datos.estadoCivil || ''
|
||||
this.components.form.querySelector(`[name="${this.prefix}_buyer_profession"]`).value = reservation.buyer.datos.ocupacion || ''
|
||||
if ('fechaNacimiento' in reservation.buyer.datos) {
|
||||
const birthdate = new Date(reservation.buyer.datos.fechaNacimiento)
|
||||
birthdate.setDate(birthdate.getDate() + 1)
|
||||
this.components.$birthdate.calendar('set date', birthdate)
|
||||
}
|
||||
}
|
||||
|
||||
if (reservation.broker !== null) {
|
||||
if (reservations.locks.brokers === null) {
|
||||
await reservations.locks.brokers
|
||||
}
|
||||
this.components.$broker.dropdown('set selected', reservation.broker.rut)
|
||||
}
|
||||
if (reservation.promotions.length > 0) {
|
||||
this.components.promotions.fill(reservation.promotions)
|
||||
}
|
||||
if (reservation.units.length > 0) {
|
||||
this.components.units.fill(reservation.units)
|
||||
}
|
||||
if ('payment' in reservation && reservation.payment !== null) {
|
||||
this.components.payments.fill(reservation.payment)
|
||||
}
|
||||
}
|
||||
edit() {
|
||||
const id = this.components.form.querySelector(`#${this.prefix}_reservation_id`).value
|
||||
const url = `/api/ventas/reservation/${id}/edit`
|
||||
const form = document.getElementById(this.ids.form)
|
||||
let body = new FormData(form)
|
||||
body.delete(`${this.prefix}_reservation_id`)
|
||||
body.delete(`${this.prefix}_project_id`)
|
||||
|
||||
const date = this.components.$date.calendar('get date')
|
||||
body.set(`${this.prefix}_date`, [
|
||||
date.getFullYear(),
|
||||
(date.getMonth() + 1).toString().padStart(2, '0'),
|
||||
date.getDate().toString().padStart(2, '0')].join('-'))
|
||||
body.set(`${this.prefix}_buyer_rut`, Rut.clean(this.components.rut.querySelector('input').value))
|
||||
body.set(`${this.prefix}_buyer_digit`, this.components.digit.textContent)
|
||||
body.set(`${this.prefix}_buyer_address_comuna_id`, this.components.$comuna.dropdown('get value'))
|
||||
let email = form.querySelector(`input[name='${this.prefix}_buyer_email_name']`).value + '@' + form.querySelector(`input[name='${this.prefix}_buyer_email_domain']`).value
|
||||
if (email !== '@') {
|
||||
body.set(`${this.prefix}_buyer_email`, email)
|
||||
}
|
||||
const birthdate = this.components.$birthdate.calendar('get date')
|
||||
body.set(`${this.prefix}_buyer_birthdate`, [
|
||||
birthdate.getFullYear(),
|
||||
(birthdate.getMonth() + 1).toString().padStart(2, '0'),
|
||||
birthdate.getDate().toString().padStart(2, '0')].join('-'))
|
||||
body.set(`${this.prefix}_broker_rut`, Rut.clean(this.components.$broker.dropdown('get value')))
|
||||
|
||||
if (!this.components.payments.components.pie.$checkbox.is(':checked')) {
|
||||
body.delete(`${this.prefix}_payment_pie`)
|
||||
body.delete(`${this.prefix}_payment_cuotas`)
|
||||
}
|
||||
body.delete(`${this.prefix}_payment_has_pie`)
|
||||
if (!this.components.payments.components.credit.$checkbox.is(':checked')) {
|
||||
body.delete(`${this.prefix}_payment_credit`)
|
||||
}
|
||||
body.delete(`${this.prefix}_payment_has_credit`)
|
||||
|
||||
body.delete('comuna')
|
||||
body.delete('region')
|
||||
body.delete('broker')
|
||||
body.delete(`${this.prefix}_broker`)
|
||||
body.delete('birthdate')
|
||||
body.delete(`${this.prefix}_buyer_email_name`)
|
||||
body.delete(`${this.prefix}_buyer_email_domain`)
|
||||
|
||||
const keys = body.keys().toArray()
|
||||
keys.forEach(key => {
|
||||
if (body.get(key) === '' || body.get(key) === null) {
|
||||
body.delete(key)
|
||||
}
|
||||
})
|
||||
|
||||
body = this.validateBody(body)
|
||||
if (body.keys().toArray().length === 0) {
|
||||
alert('No hay cambios.')
|
||||
return
|
||||
}
|
||||
|
||||
const method = 'post'
|
||||
return APIClient.fetch(url, {method, body}).then(response => response.json()).then(json => {
|
||||
if (json.success) {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
setup() {
|
||||
super.setup()
|
||||
|
||||
this.components.$modal.modal({
|
||||
onApprove: () => {
|
||||
this.edit()
|
||||
}
|
||||
})
|
||||
this.components.form.addEventListener('submit', event => {
|
||||
event.preventDefault()
|
||||
this.edit()
|
||||
return false
|
||||
})
|
||||
const idInput = document.createElement('input')
|
||||
idInput.type = 'hidden'
|
||||
idInput.id = `${this.prefix}_reservation_id`
|
||||
idInput.name = `${this.prefix}_reservation_id`
|
||||
this.components.form.appendChild(idInput)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@ -18,6 +18,14 @@ return [
|
||||
$urls['assets'],
|
||||
'images'
|
||||
]);
|
||||
$urls['scripts'] = implode('/', [
|
||||
$urls['assets'],
|
||||
'js'
|
||||
]);
|
||||
$urls['styles'] = implode('/', [
|
||||
$urls['assets'],
|
||||
'css'
|
||||
]);
|
||||
return (object) $urls;
|
||||
},
|
||||
'apiUrls' => function(ContainerInterface $container) {
|
||||
|
||||
@ -15,21 +15,24 @@ return [
|
||||
);
|
||||
},
|
||||
Incoviba\Service\Money::class => function(ContainerInterface $container) {
|
||||
$mindicador = new Incoviba\Service\Money\MiIndicador(new GuzzleHttp\Client([
|
||||
$mindicador = new Incoviba\Service\Money\MiIndicador($container->get(Psr\Log\LoggerInterface::class), new GuzzleHttp\Client([
|
||||
'base_uri' => 'https://mindicador.cl/api/',
|
||||
'headers' => ['Accept' => 'application/json']
|
||||
]));
|
||||
$ine = new Incoviba\Service\Money\Ine(new GuzzleHttp\Client([
|
||||
$ine = new Incoviba\Service\Money\Ine($container->get(Psr\Log\LoggerInterface::class), new GuzzleHttp\Client([
|
||||
'base_uri' => 'https://api-calculadora.ine.cl/ServiciosCalculadoraVariacion'
|
||||
]));
|
||||
$sii = new Incoviba\Service\Money\SII(new GuzzleHttp\Client([
|
||||
$sii = new Incoviba\Service\Money\SII($container->get(Psr\Log\LoggerInterface::class), new GuzzleHttp\Client([
|
||||
'base_uri' => 'https://www.sii.cl/valores_y_fechas/'
|
||||
]), $container->get(Incoviba\Repository\UF::class));
|
||||
return (new Incoviba\Service\Money($container->get(Psr\Log\LoggerInterface::class)))
|
||||
->register('uf', $mindicador)
|
||||
->register('uf', $sii)
|
||||
->register('usd', $mindicador)
|
||||
->register('ipc', $ine);
|
||||
$findic = new Incoviba\Service\Money\Findic($container->get(Psr\Log\LoggerInterface::class), new GuzzleHttp\Client([
|
||||
'base_uri' => 'https://findic.cl/api/'
|
||||
]));
|
||||
return new Incoviba\Service\Money($container->get(Psr\Log\LoggerInterface::class))
|
||||
->register($findic)
|
||||
->register($sii)
|
||||
->register($ine)
|
||||
->register($mindicador);
|
||||
},
|
||||
Predis\ClientInterface::class => function(ContainerInterface $container) {
|
||||
$options = [
|
||||
@ -46,7 +49,7 @@ return [
|
||||
return new Predis\Client($options);
|
||||
},
|
||||
Incoviba\Service\Contabilidad\Cartola::class => function(ContainerInterface $container) {
|
||||
return (new Incoviba\Service\Contabilidad\Cartola(
|
||||
return new Incoviba\Service\Contabilidad\Cartola(
|
||||
$container->get(Psr\Log\LoggerInterface::class),
|
||||
$container->get(Psr\Http\Message\StreamFactoryInterface::class),
|
||||
$container->get(Incoviba\Common\Define\Contabilidad\Exporter::class),
|
||||
@ -55,7 +58,7 @@ return [
|
||||
$container->get(Incoviba\Repository\Contabilidad\Movimiento::class),
|
||||
$container->get(Incoviba\Service\Contabilidad\Movimiento::class),
|
||||
$container->get(Incoviba\Repository\Contabilidad\Cartola::class)
|
||||
))
|
||||
)
|
||||
->register('security', $container->get(Incoviba\Service\Contabilidad\Cartola\Security::class))
|
||||
->register('itau', $container->get(Incoviba\Service\Contabilidad\Cartola\Itau::class))
|
||||
->register('santander', $container->get(Incoviba\Service\Contabilidad\Cartola\Santander::class))
|
||||
@ -89,11 +92,10 @@ return [
|
||||
$container->get('nubox')->get('url'));
|
||||
},
|
||||
Incoviba\Service\Informe::class => function(ContainerInterface $container) {
|
||||
return (new Incoviba\Service\Informe(
|
||||
return new Incoviba\Service\Informe(
|
||||
$container->get(Psr\Log\LoggerInterface::class),
|
||||
$container->get('folders')->get('informes'))
|
||||
)
|
||||
->register('xlsx', Incoviba\Service\Informe\Excel::class);
|
||||
->register('xlsx', Incoviba\Service\Informe\Excel::class);
|
||||
},
|
||||
Incoviba\Service\Contabilidad\Informe\Tesoreria\Output\Excel::class => function(ContainerInterface $container) {
|
||||
return new Incoviba\Service\Contabilidad\Informe\Tesoreria\Output\Excel(
|
||||
@ -104,15 +106,15 @@ return [
|
||||
);
|
||||
},
|
||||
Incoviba\Service\Contabilidad\Cartola\Santander::class => function(ContainerInterface $container) {
|
||||
return (new Incoviba\Service\Contabilidad\Cartola\Santander(
|
||||
return new Incoviba\Service\Contabilidad\Cartola\Santander(
|
||||
$container->get(Psr\Log\LoggerInterface::class),
|
||||
))
|
||||
)
|
||||
->registerSub($container->get(Incoviba\Service\Contabilidad\Cartola\Santander\OfficeBanking::class));
|
||||
},
|
||||
Incoviba\Service\Contabilidad\Cartola\BCI::class => function(ContainerInterface $container) {
|
||||
return (new Incoviba\Service\Contabilidad\Cartola\BCI(
|
||||
return new Incoviba\Service\Contabilidad\Cartola\BCI(
|
||||
$container->get(Psr\Log\LoggerInterface::class),
|
||||
))
|
||||
)
|
||||
->registerSub($container->get(Incoviba\Service\Contabilidad\Cartola\BCI\Mes::class));
|
||||
},
|
||||
'TokuClient' => function(ContainerInterface $container) {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
namespace Incoviba\Controller\API\Contabilidad;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Incoviba\Common\Ideal\Controller;
|
||||
@ -117,24 +118,24 @@ class Cartolas extends Controller
|
||||
UPLOAD_ERR_CANT_WRITE => 'No se pudo escribir el archivo',
|
||||
UPLOAD_ERR_EXTENSION => 'Una extensión de PHP detuvo la subida del archivo'
|
||||
];
|
||||
if (is_array($files['file'])) {
|
||||
foreach ($files['file'] as $i => $file) {
|
||||
if ($file->getError() !== UPLOAD_ERR_OK) {
|
||||
$output['errors'] []= ['filename' => $file->getClientFilename(), 'error' => $errors[$file->getError()]];
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$output['movimientos'] = array_merge($output['movimientos'], $cartolaService->import($body['cuenta_id'][$i], $file));
|
||||
} catch (EmptyResult) {}
|
||||
}
|
||||
} else {
|
||||
$file = $files['file'];
|
||||
if (!is_array($files['file'])) {
|
||||
$files['file'] = [$files['file']];
|
||||
$body['cuenta_id'] = [$body['cuenta_id']];
|
||||
}
|
||||
foreach ($files['file'] as $i => $file) {
|
||||
if ($file->getError() !== UPLOAD_ERR_OK) {
|
||||
$output['errors'][] = ['filename' => $file->getClientFilename(), 'error' => $errors[$file->getError()]];
|
||||
} else {
|
||||
try {
|
||||
$output['movimientos'] = $cartolaService->import($body['cuenta_id'], $file);
|
||||
} catch (EmptyResult) {}
|
||||
$output['errors'] []= ['filename' => $file->getClientFilename(), 'error' => $errors[$file->getError()]];
|
||||
continue;
|
||||
}
|
||||
if (empty($body['cuenta_id'])) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$output['movimientos'] = array_merge($output['movimientos'], $cartolaService->import($body['cuenta_id'][$i], $file));
|
||||
} catch (Read $exception) {
|
||||
$output['errors'] []= ['filename' => $file->getClientFilename(),
|
||||
'error' => ['message' => $exception->getMessage(), 'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine()]];
|
||||
}
|
||||
}
|
||||
return $this->withJson($response, $output);
|
||||
|
||||
@ -88,8 +88,8 @@ class Movimientos extends Ideal\Controller
|
||||
$sociedades_ruts = $input['sociedades_ruts'];
|
||||
foreach ($sociedades_ruts as $sociedadRut) {
|
||||
try {
|
||||
$movimientos = $movimientoService->getAmountBySociedadAndMes($sociedadRut, new DateTimeImmutable($input['mes']), $input['amount'] ?? null);
|
||||
$output['movimientos'] = array_merge($output['movimientos'], $this->movimientosToArray($movimientos));
|
||||
$movimientos = $movimientoService->getAmountBySociedadAndMes($sociedadRut, new DateTimeImmutable($input['mes']), $input['amount'] ?? null);
|
||||
$output['movimientos'] = array_merge($output['movimientos'], $this->movimientosToArray($movimientos));
|
||||
} catch (EmptyResult) {}
|
||||
}
|
||||
return $this->withJson($response, $output);
|
||||
|
||||
@ -53,7 +53,7 @@ class Money
|
||||
}
|
||||
try {
|
||||
$this->data[$provider] = (array) $this->fetchRedis($redisService, $redisKey);
|
||||
if (!isset($this->data[$provider][$date->format('Y-m-d')])) {
|
||||
if (!isset($this->data[$provider][$date->format('Y-m-d')]) or $this->data[$provider][$date->format('Y-m-d')] === 0) {
|
||||
throw new EmptyRedis($redisKey);
|
||||
}
|
||||
} catch (EmptyRedis) {
|
||||
|
||||
@ -4,8 +4,11 @@ namespace Incoviba\Controller\API\Ventas;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResult;
|
||||
use Incoviba\Controller\API\withJson;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use Incoviba\Exception\ServiceAction\Update;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
|
||||
@ -42,7 +45,7 @@ class Escrituras
|
||||
try {
|
||||
$escrituraService->edit($venta_id, $body);
|
||||
$output['success'] = true;
|
||||
} catch (EmptyResult) {}
|
||||
} catch (Read|Update|EmptyResult) {}
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,14 +5,14 @@ use DateTime;
|
||||
use Exception;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Incoviba\Common\Ideal\Controller;
|
||||
use Incoviba\Common\Implement\Exception\{EmptyRedis,EmptyResult};
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Controller\API\{withJson,emptyBody};
|
||||
use Incoviba\Controller\withRedis;
|
||||
use Incoviba\Exception\ServiceAction\Create;
|
||||
use Incoviba\Service;
|
||||
|
||||
class Precios
|
||||
class Precios extends Controller
|
||||
{
|
||||
use withJson, emptyBody, withRedis;
|
||||
|
||||
@ -56,12 +56,16 @@ class Precios
|
||||
}
|
||||
|
||||
public function import(ServerRequestInterface $request, ResponseInterface $response,
|
||||
LoggerInterface $logger,
|
||||
Service\Venta\Precio $precioService): ResponseInterface
|
||||
{
|
||||
$body = $request->getParsedBody();
|
||||
$projectId = $body['project_id'];
|
||||
$date = $body['date'];
|
||||
if (array_key_exists('date', $body)) {
|
||||
$date = $body['date'];
|
||||
$date = DateTime::createFromFormat('Y-m-d', $date);
|
||||
} else {
|
||||
$date = new DateTime();
|
||||
}
|
||||
$file = $request->getUploadedFiles()['file'];
|
||||
$output = [
|
||||
'input' => $body,
|
||||
@ -69,13 +73,12 @@ class Precios
|
||||
'precios' => [],
|
||||
'status' => false
|
||||
];
|
||||
$date = DateTime::createFromFormat('Y-m-d', $date);
|
||||
try {
|
||||
$output['precios'] = $precioService->import($projectId, $date, $file);
|
||||
$output['total'] = count($output['precios']);
|
||||
$output['status'] = true;
|
||||
} catch (Create | Exception $exception) {
|
||||
$logger->warning($exception);
|
||||
$this->logger->warning($exception);
|
||||
}
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ class Reservations
|
||||
$output = [
|
||||
'input' => $input,
|
||||
'reservation_id' => $reservation_id,
|
||||
'reservations' => null,
|
||||
'reservation' => null,
|
||||
'success' => false,
|
||||
];
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
<?php
|
||||
namespace Incoviba\Controller;
|
||||
|
||||
use Exception;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Alias\View;
|
||||
use Incoviba\Common\Implement\Exception\EmptyRedis;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResult;
|
||||
@ -10,8 +14,6 @@ use Incoviba\Exception\ServiceAction\Update;
|
||||
use Incoviba\Model;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class Ventas
|
||||
{
|
||||
@ -91,14 +93,78 @@ class Ventas
|
||||
return $view->render($response, 'ventas.pies.edit', compact('venta'));
|
||||
}
|
||||
public function add(ServerRequestInterface $request, ResponseInterface $response, View $view,
|
||||
Repository\Region $regionRepository, Repository\Proyecto $proyectoRepository): ResponseInterface
|
||||
Repository\Region $regionRepository, Repository\Proyecto $proyectoRepository,
|
||||
LoggerInterface $logger,
|
||||
?Service\Venta\Reservation $reservaService = null): ResponseInterface
|
||||
{
|
||||
$regiones = $regionRepository->fetchAll();
|
||||
usort($regiones, function(Model\Region $a, Model\Region $b) {
|
||||
return $a->numeracion - $b->numeracion;
|
||||
});
|
||||
$proyectos = $proyectoRepository->fetchAllActive();
|
||||
return $view->render($response, 'ventas.add', compact('regiones', 'proyectos'));
|
||||
|
||||
$viewData = [
|
||||
'regiones' => $regiones,
|
||||
'proyectos' => $proyectos,
|
||||
'from_reservation' => false
|
||||
];
|
||||
|
||||
// Check if this is a conversion from a reservation
|
||||
if ($request->getMethod() === 'POST') {
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (isset($data['from_reservation']) && $data['from_reservation'] === 'true' && !empty($data['reservation_id'])) {
|
||||
try {
|
||||
$reservation = $reservaService->get((int) $data['reservation_id']);
|
||||
|
||||
$viewData['from_reservation'] = true;
|
||||
$viewData['reservation_id'] = $reservation->id;
|
||||
$viewData['date'] = $reservation->date;
|
||||
$viewData['comments'] = explode("\n", $reservation->comments);
|
||||
|
||||
$viewData['propietario'] = [
|
||||
'rut' => $reservation->buyer->rut,
|
||||
'digito' => $reservation->buyer->digito,
|
||||
'full' => $reservation->buyer->rutCompleto(),
|
||||
'email' => $reservation->buyer->datos()?->email,
|
||||
'telefono' => $reservation->buyer->datos()?->telefono,
|
||||
'comuna_id' => $reservation->buyer->datos()?->direccion?->comuna?->id
|
||||
];
|
||||
|
||||
// Add property data
|
||||
$viewData['proyecto_id'] = $reservation->project->id;
|
||||
|
||||
$viewData['unidades'] = [];
|
||||
foreach ($reservation->units as $unitValue) {
|
||||
$type = $unitValue->unit->proyectoTipoUnidad->tipoUnidad->descripcion;
|
||||
if (!array_key_exists($type, $viewData['unidades'])) {
|
||||
$viewData['unidades'][$type] = [];
|
||||
}
|
||||
$viewData['unidades'][$type] []= $unitValue->unit->id;
|
||||
}
|
||||
|
||||
$viewData['valor'] = $reservation->offer();
|
||||
|
||||
if ($reservation->payment !== null) {
|
||||
$viewData['forma_pago'] = [];
|
||||
if ($reservation->payment->pie !== null) {
|
||||
$viewData['forma_pago']['pie'] = [
|
||||
'valor' => $reservation->payment->pie->valor,
|
||||
'cuotas' => $reservation->payment->pie->cuotas,
|
||||
];
|
||||
}
|
||||
if ($reservation->payment->credito !== null) {
|
||||
$viewData['forma_pago']['credito'] = $reservation->payment->credito->valor;
|
||||
}
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
// Log error or handle as needed
|
||||
$logger->error('Error loading reservation data', ['exception' => $exception]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $view->render($response, 'ventas.add', $viewData);
|
||||
}
|
||||
public function cuotas(ServerRequestInterface $request, ResponseInterface $response, Service\Venta $ventaService,
|
||||
Service\Contabilidad\Banco $bancoService,
|
||||
|
||||
@ -14,6 +14,7 @@ class Reservation extends Common\Ideal\Model
|
||||
public array $units = [];
|
||||
public array $promotions = [];
|
||||
public ?Model\Proyecto\Broker $broker = null;
|
||||
public ?Model\Venta\Reservation\Payment $payment = null;
|
||||
|
||||
public function offer(): float
|
||||
{
|
||||
@ -22,10 +23,13 @@ class Reservation extends Common\Ideal\Model
|
||||
public function withCommission(): float
|
||||
{
|
||||
$base = 0;
|
||||
foreach ($this->units as $unit) {
|
||||
foreach ($this->units as &$unit) {
|
||||
$unitBase = $unit->value;
|
||||
foreach ($this->promotions as $promotion) {
|
||||
$base += $promotion->activate()->reverse($unit['unit'], $unit['value'], $this->broker);
|
||||
$unitBase = $promotion->activate()->reverse($unit->unit, $unitBase, $this->broker);
|
||||
}
|
||||
$unit->base = $unitBase;
|
||||
$base += $unitBase;
|
||||
}
|
||||
return $base;
|
||||
}
|
||||
@ -128,6 +132,7 @@ class Reservation extends Common\Ideal\Model
|
||||
'units' => $this->units,
|
||||
'promotions' => $this->promotions,
|
||||
'broker' => $this->broker,
|
||||
'payment' => $this->payment,
|
||||
'offer' => $this->offer(),
|
||||
'with_commission' => $this->withCommission(),
|
||||
'base' => $this->base(),
|
||||
|
||||
@ -6,6 +6,9 @@ enum Type: int
|
||||
case Unit = 1;
|
||||
case Promotion = 2;
|
||||
case Broker = 3;
|
||||
case Advance = 4; // Pie
|
||||
case Credit = 5; // Credito
|
||||
case Subsidy = 6; // Subsidio
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
|
||||
23
app/src/Model/Venta/Reservation/Payment.php
Normal file
23
app/src/Model/Venta/Reservation/Payment.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace Incoviba\Model\Venta\Reservation;
|
||||
|
||||
use JsonSerializable;
|
||||
use Incoviba\Model\Venta\Credito;
|
||||
use Incoviba\Model\Venta\Pie;
|
||||
use Incoviba\Model\Venta\Subsidio;
|
||||
|
||||
class Payment implements JsonSerializable
|
||||
{
|
||||
public ?Pie $pie = null;
|
||||
public ?Credito $credito = null;
|
||||
public ?Subsidio $subsidio = null;
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'pie' => $this->pie,
|
||||
'credito' => $this->credito,
|
||||
'subsidio' => $this->subsidio
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -54,6 +54,11 @@ class Movimiento extends Ideal\Repository
|
||||
return $this->update($model, ['cuenta_id', 'fecha', 'glosa', 'documento', 'cargo', 'abono', 'saldo'], $new_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cuenta_id
|
||||
* @return array
|
||||
* @throws Implement\Exception\EmptyResult
|
||||
*/
|
||||
public function fetchByCuenta(int $cuenta_id): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
@ -97,6 +102,13 @@ class Movimiento extends Ideal\Repository
|
||||
->where("cuenta_id = ? AND fecha = ? AND SUBSTRING(LOWER(LTRIM(glosa)), 0, {$len}) = SUBSTRING(LOWER(LTRIM(?)), 0, {$len}) AND cargo = ? AND abono = ? AND saldo = ?");
|
||||
return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d'), $glosa, $cargo, $abono, $saldo]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $start
|
||||
* @param int $amount
|
||||
* @return array
|
||||
* @throws Implement\Exception\EmptyResult
|
||||
*/
|
||||
public function fetchAmountStartingFrom(int $start, int $amount): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
@ -105,6 +117,14 @@ class Movimiento extends Ideal\Repository
|
||||
->limit($amount, $start);
|
||||
return $this->fetchMany($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sociedad_rut
|
||||
* @param DateTimeInterface $mes
|
||||
* @param int|null $amount
|
||||
* @return array
|
||||
* @throws Implement\Exception\EmptyResult
|
||||
*/
|
||||
public function fetchAmountBySociedadAndMes(int $sociedad_rut, DateTimeInterface $mes, ?int $amount): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
|
||||
@ -28,6 +28,9 @@ class Detalle extends Ideal\Repository
|
||||
->register('centro_costo_id', (new Implement\Repository\Mapper())
|
||||
->setProperty('centroCosto')
|
||||
->setFunction(function(array $data) {
|
||||
if (empty($data['centro_costo_id'])) {
|
||||
return null;
|
||||
}
|
||||
return $this->centroCostoRepository->fetchById($data['centro_costo_id']);
|
||||
})
|
||||
->setDefault(null));
|
||||
|
||||
@ -19,10 +19,13 @@ class Proveedor extends Ideal\Repository
|
||||
|
||||
public function create(?array $data = null): Model\Inmobiliaria\Proveedor
|
||||
{
|
||||
$map = (new Implement\Repository\MapperParser(['rut', 'digito', 'nombre', 'razon']))
|
||||
$map = new Implement\Repository\MapperParser(['rut', 'digito', 'nombre', 'razon'])
|
||||
->register('contacto_rut', (new Implement\Repository\Mapper())
|
||||
->setProperty('contacto')
|
||||
->setFunction(function($data) {
|
||||
if ($data['contacto_rut'] === null) {
|
||||
return null;
|
||||
}
|
||||
return $this->personaService->getById($data['contacto_rut']);
|
||||
})
|
||||
->setDefault(null));
|
||||
|
||||
@ -63,7 +63,7 @@ class Datos extends Ideal\Repository
|
||||
'persona_rut', 'direccion_id', 'telefono', 'email', 'fecha_nacimiento', 'sexo', 'estado_civil',
|
||||
'nacionalidad', 'ocupacion'
|
||||
], [
|
||||
$model->persona->rut, $model->direccion?->id, $model->telefono, $model->email, $model->fechaNacimiento,
|
||||
$model->persona->rut, $model->direccion?->id, $model->telefono, $model->email, $model->fechaNacimiento->format('Y-m-d'),
|
||||
$model->sexo, $model->estadoCivil, $model->nacionalidad, $model->ocupacion
|
||||
]);
|
||||
return $model;
|
||||
|
||||
@ -3,22 +3,28 @@ namespace Incoviba\Repository\Venta;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateInterval;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Define;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResult;
|
||||
use Incoviba\Exception\Model\InvalidState;
|
||||
use PDO;
|
||||
use Incoviba\Common;
|
||||
use Incoviba\Model;
|
||||
use Incoviba\Repository;
|
||||
use PDOException;
|
||||
use Incoviba\Service;
|
||||
|
||||
class Reservation extends Common\Ideal\Repository
|
||||
{
|
||||
public function __construct(Common\Define\Connection $connection,
|
||||
protected LoggerInterface $logger,
|
||||
protected Repository\Proyecto $proyectoRepository,
|
||||
protected Repository\Persona $personaRepository,
|
||||
protected Repository\Proyecto\Broker $brokerRepository,
|
||||
protected Unidad $unitRepository, protected Promotion $promotionRepository)
|
||||
protected Service\Proyecto\Broker $brokerService,
|
||||
protected Unidad $unitRepository,
|
||||
protected Promotion $promotionRepository, protected Pie $advanceRepository,
|
||||
protected Credito $creditRepository, protected Subsidio $subsidyRepository)
|
||||
{
|
||||
parent::__construct($connection);
|
||||
}
|
||||
@ -44,6 +50,12 @@ class Reservation extends Common\Ideal\Repository
|
||||
->register('date', new Common\Implement\Repository\Mapper\DateTime('date'));
|
||||
return $this->parseData(new Model\Venta\Reservation(), $data, $map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Define\Model $model
|
||||
* @return Model\Venta\Reservation
|
||||
* @throws PDOException
|
||||
*/
|
||||
public function save(Common\Define\Model $model): Model\Venta\Reservation
|
||||
{
|
||||
if (!isset($model->id)) {
|
||||
@ -85,8 +97,15 @@ class Reservation extends Common\Ideal\Repository
|
||||
$this->fetchUnits($model, $data_row);
|
||||
try {
|
||||
$this->fetchBroker($model, $data_row);
|
||||
} catch (Common\Implement\Exception\EmptyResult) {}
|
||||
} catch (EmptyResult $exception) {
|
||||
$this->logger->notice(implode("::", [__CLASS__, __METHOD__]), ['data' => $data_row, 'exception' => $exception]);
|
||||
}
|
||||
$this->fetchPromotions($model, $data_row);
|
||||
try {
|
||||
$this->fetchPayment($model, $data_row);
|
||||
} catch (EmptyResult $exception) {
|
||||
$this->logger->notice(implode("::", [__CLASS__, __METHOD__]), ['data' => $data_row, 'exception' => $exception]);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
@ -239,11 +258,20 @@ class Reservation extends Common\Ideal\Repository
|
||||
$exceptions[] = new Common\Implement\Exception\EmptyResult('No cancelled reservations', $exception);
|
||||
}
|
||||
if (count($reservations) === 0) {
|
||||
throw new Common\Implement\Exception\EmptyResult('No rejected nor cancelled reservations', last($exceptions));
|
||||
$exception = null;
|
||||
if (count($exceptions) > 0) {
|
||||
$exception = last($exceptions);
|
||||
}
|
||||
throw new Common\Implement\Exception\EmptyResult('No rejected nor cancelled reservations', $exception);
|
||||
}
|
||||
return $reservations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @return void
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function saveUnits(Model\Venta\Reservation $reservation): void
|
||||
{
|
||||
if (empty($reservation->units)) {
|
||||
@ -286,6 +314,12 @@ class Reservation extends Common\Ideal\Repository
|
||||
Model\Venta\Reservation\Detail\Type::Unit->value,
|
||||
array_map(fn($unit) => $unit->unit->id, $reservation->units));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @return void
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function saveBroker(Model\Venta\Reservation &$reservation): void
|
||||
{
|
||||
if ($reservation->broker === null) {
|
||||
@ -306,9 +340,9 @@ class Reservation extends Common\Ideal\Repository
|
||||
throw new PDOException();
|
||||
}
|
||||
$new_id = $reservation->broker->rut;
|
||||
$reservation->broker = $this->brokerRepository->fetchById($result['reference_id']);
|
||||
$reservation->broker = $this->brokerService->fetchById($result['reference_id']);
|
||||
$this->editBroker($reservation, ['broker_rut' => $new_id]);
|
||||
} catch (PDOException) {
|
||||
} catch (EmptyResult | PDOException) {
|
||||
$queryInsert = $this->connection->getQueryBuilder()
|
||||
->insert()
|
||||
->into('reservation_details')
|
||||
@ -321,6 +355,12 @@ class Reservation extends Common\Ideal\Repository
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @return void
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function savePromotions(Model\Venta\Reservation $reservation): void
|
||||
{
|
||||
if (empty($reservation->promotions)) {
|
||||
@ -362,6 +402,34 @@ class Reservation extends Common\Ideal\Repository
|
||||
Model\Venta\Reservation\Detail\Type::Promotion->value,
|
||||
array_map(fn($promotion) => $promotion->id, $reservation->promotions));
|
||||
}
|
||||
public function saveDetail(Model\Venta\Reservation $reservation, int $type, int $referenceId, ?float $value = null): void
|
||||
{
|
||||
$queryCheck = $this->connection->getQueryBuilder()
|
||||
->select('1')
|
||||
->from('reservation_details')
|
||||
->where('reservation_id = :reservation_id AND type = :type AND reference_id = :reference_id AND value = :value');
|
||||
$statementCheck = $this->connection->prepare($queryCheck);
|
||||
$statementCheck->execute([
|
||||
'reservation_id' => $reservation->id,
|
||||
'type' => $type,
|
||||
'reference_id' => $referenceId,
|
||||
'value' => $value
|
||||
]);
|
||||
$result = $statementCheck->fetch(PDO::FETCH_ASSOC);
|
||||
if ($result === false) {
|
||||
$queryInsert = $this->connection->getQueryBuilder()
|
||||
->insert()
|
||||
->into('reservation_details')
|
||||
->columns(['reservation_id', 'type', 'reference_id', 'value'])
|
||||
->values([':reservation_id', ':type', ':reference_id', ':value']);
|
||||
$this->connection->execute($queryInsert, [
|
||||
'reservation_id' => $reservation->id,
|
||||
'type' => $type,
|
||||
'reference_id' => $referenceId,
|
||||
'value' => $value
|
||||
]);
|
||||
}
|
||||
}
|
||||
protected function cleanUpDetails(Model\Venta\Reservation $reservation, int $type_id, array $currentIds): void
|
||||
{
|
||||
$queryCheck = $this->connection->getQueryBuilder()
|
||||
@ -394,6 +462,9 @@ class Reservation extends Common\Ideal\Repository
|
||||
|
||||
protected function editUnits(Model\Venta\Reservation &$reservation, array $new_data): void
|
||||
{
|
||||
if (!array_key_exists('units', $new_data)) {
|
||||
return;
|
||||
}
|
||||
$querySelect = $this->connection->getQueryBuilder()
|
||||
->select()
|
||||
->from('reservation_details')
|
||||
@ -410,6 +481,7 @@ class Reservation extends Common\Ideal\Repository
|
||||
->columns(['reservation_id', 'type', 'reference_id', 'value'])
|
||||
->values([':reservation_id', ':type', ':reference_id', ':value']);
|
||||
$statementInsert = $this->connection->prepare($queryInsert);
|
||||
|
||||
foreach ($new_data['units'] as $unit) {
|
||||
$idx = $reservation->findUnit($unit['unit_id']);
|
||||
if ($idx === null) {
|
||||
@ -447,6 +519,7 @@ class Reservation extends Common\Ideal\Repository
|
||||
$reservation->units[$idx]['value'] = $unit['value'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function editBroker(Model\Venta\Reservation &$reservation, array $new_data): void
|
||||
{
|
||||
if (!array_key_exists('broker_rut', $new_data) or $new_data['broker_rut'] === $reservation->broker->rut) {
|
||||
@ -462,11 +535,14 @@ class Reservation extends Common\Ideal\Repository
|
||||
'type' => Model\Venta\Reservation\Detail\Type::Broker->value,
|
||||
'broker_rut' => $new_data['broker_rut']
|
||||
]);
|
||||
$reservation->broker = $this->brokerRepository->fetchById($new_data['broker_rut']);
|
||||
} catch (PDOException) {}
|
||||
$reservation->broker = $this->brokerService->get($new_data['broker_rut']);
|
||||
} catch (PDOException|Read) {}
|
||||
}
|
||||
protected function editPromotions(Model\Venta\Reservation &$reservation, array $new_data): void
|
||||
{
|
||||
if (!array_key_exists('promotions', $new_data)) {
|
||||
return;
|
||||
}
|
||||
$querySelect = $this->connection->getQueryBuilder()
|
||||
->select()
|
||||
->from('reservation_details')
|
||||
@ -477,7 +553,7 @@ class Reservation extends Common\Ideal\Repository
|
||||
->set('value = :value')
|
||||
->where('reservation_id = :id AND type = "Promotion" AND reference_id = :promotion_id');
|
||||
$statementUpdate = $this->connection->prepare($queryUpdate);
|
||||
foreach ($new_data as $promotion_id => $value) {
|
||||
foreach ($new_data['promotions'] as $promotion_id => $value) {
|
||||
$idx = array_search($promotion_id, array_column($reservation->promotions, 'id'));
|
||||
if ($idx === false) {
|
||||
$reservation->promotions []= $this->promotionRepository->fetchById($promotion_id);
|
||||
@ -569,14 +645,18 @@ class Reservation extends Common\Ideal\Repository
|
||||
if ($result === false) {
|
||||
throw new Common\Implement\Exception\EmptyResult($query);
|
||||
}
|
||||
$reservation->broker = $this->brokerRepository->fetchById($result['reference_id']);
|
||||
} catch (PDOException) {}
|
||||
$reservation->broker = $this->brokerService->get($result['reference_id']);
|
||||
} catch (PDOException | Read $exception) {
|
||||
$this->logger->notice(implode("::", [__CLASS__, __METHOD__]), ['data' => $new_data, 'exception' => $exception]);
|
||||
}
|
||||
|
||||
return $reservation;
|
||||
}
|
||||
try {
|
||||
$reservation->broker = $this->brokerRepository->fetchById($new_data['broker_id']);
|
||||
} catch (Common\Implement\Exception\EmptyResult) {}
|
||||
$reservation->broker = $this->brokerService->get($new_data['broker_id']);
|
||||
} catch (Read $exception) {
|
||||
$this->logger->notice(implode("::", [__CLASS__, __METHOD__]), ['data' => $new_data, 'exception' => $exception]);
|
||||
}
|
||||
return $reservation;
|
||||
}
|
||||
protected function fetchPromotions(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
|
||||
@ -639,4 +719,49 @@ class Reservation extends Common\Ideal\Repository
|
||||
$reservation->broker = null;
|
||||
} catch (PDOException) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $new_data
|
||||
* @return Model\Venta\Reservation
|
||||
* @throws EmptyResult
|
||||
*/
|
||||
protected function fetchPayment(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
|
||||
{
|
||||
$types = [
|
||||
Model\Venta\Reservation\Detail\Type::Advance->value,
|
||||
Model\Venta\Reservation\Detail\Type::Credit->value,
|
||||
Model\Venta\Reservation\Detail\Type::Subsidy->value
|
||||
];
|
||||
$typesString = implode(', ', $types);
|
||||
$query = $this->connection->getQueryBuilder()
|
||||
->select()
|
||||
->from('reservation_details')
|
||||
->where("reservation_id = :id AND type IN ({$typesString})");
|
||||
try {
|
||||
$statement = $this->connection->execute($query, ['id' => $reservation->id]);
|
||||
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $exception) {
|
||||
throw new EmptyResult($query, $exception, ['id' => $reservation->id, 'data' => $new_data]);
|
||||
}
|
||||
if (count($results) === 0) {
|
||||
throw new EmptyResult($query);
|
||||
}
|
||||
$payment = new Model\Venta\Reservation\Payment();
|
||||
foreach ($results as $result) {
|
||||
switch ($result['type']) {
|
||||
case Model\Venta\Reservation\Detail\Type::Advance->value:
|
||||
$payment->pie = $this->advanceRepository->fetchById($result['reference_id']);
|
||||
break;
|
||||
case Model\Venta\Reservation\Detail\Type::Credit->value:
|
||||
$payment->credito = $this->creditRepository->fetchById($result['reference_id']);
|
||||
break;
|
||||
case Model\Venta\Reservation\Detail\Type::Subsidy->value:
|
||||
$payment->subsidio = $this->subsidyRepository->fetchById($result['reference_id']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$reservation->payment = $payment;
|
||||
return $reservation;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
<?php
|
||||
namespace Incoviba\Service\Contabilidad;
|
||||
|
||||
use DateMalformedStringException;
|
||||
use PDOException;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use PhpOffice\PhpSpreadsheet;
|
||||
use Incoviba\Common\Define\Cartola\Banco;
|
||||
use Incoviba\Common\Define\Contabilidad\Exporter;
|
||||
use Incoviba\Common\Ideal\Service;
|
||||
use Incoviba\Common\Implement\Exception;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use Incoviba\Model;
|
||||
use Incoviba\Repository;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Cartola extends Service
|
||||
{
|
||||
@ -32,6 +35,13 @@ class Cartola extends Service
|
||||
$this->bancos[$name] = $banco;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Contabilidad\Banco $banco
|
||||
* @param UploadedFileInterface $file
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
public function process(Model\Contabilidad\Banco $banco, UploadedFileInterface $file): array
|
||||
{
|
||||
return $this->bancos[strtolower($banco->nombre)]->process($file);
|
||||
@ -93,9 +103,19 @@ class Cartola extends Service
|
||||
return compact('cartola', 'movimientos');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cuenta_id
|
||||
* @param UploadedFileInterface $file
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
public function import(int $cuenta_id, UploadedFileInterface $file): array
|
||||
{
|
||||
$cuenta = $this->cuentaRepository->fetchById($cuenta_id);
|
||||
try {
|
||||
$cuenta = $this->cuentaRepository->fetchById($cuenta_id);
|
||||
} catch (Exception\EmptyResult $exception) {
|
||||
throw new Read(__CLASS__, $exception);
|
||||
}
|
||||
$movimientos = $this->process($cuenta->banco, $file);
|
||||
|
||||
$inmobiliaria = $cuenta->inmobiliaria;
|
||||
@ -106,15 +126,23 @@ class Cartola extends Service
|
||||
if (array_key_exists('centro_costo', $dataMovimiento) and $dataMovimiento['centro_costo'] !== 0) {
|
||||
$dataMovimiento['centro_costo_id'] = $dataMovimiento['centro_costo'];
|
||||
}
|
||||
$dataMovimiento['fecha'] = new DateTimeImmutable($dataMovimiento['fecha']);
|
||||
try {
|
||||
$dataMovimiento['fecha'] = new DateTimeImmutable($dataMovimiento['fecha']);
|
||||
} catch (DateMalformedStringException) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists('rut', $dataMovimiento)) {
|
||||
$ruts = $this->parseRut($dataMovimiento['rut']);
|
||||
if (key_exists('rut', $ruts)) {
|
||||
$dataMovimiento['rut'] = $ruts['rut'];
|
||||
$dataMovimiento['digito'] = $ruts['digito'];
|
||||
if ($dataMovimiento['rut'] === '') {
|
||||
unset($dataMovimiento['rut']);
|
||||
} else {
|
||||
$dataMovimiento['rut'] = $ruts[0]['rut'];
|
||||
$dataMovimiento['digito'] = $ruts[0]['digito'];
|
||||
$ruts = $this->parseRut($dataMovimiento['rut']);
|
||||
if (key_exists('rut', $ruts)) {
|
||||
$dataMovimiento['rut'] = $ruts['rut'];
|
||||
$dataMovimiento['digito'] = $ruts['digito'];
|
||||
} else {
|
||||
$dataMovimiento['rut'] = $ruts[0]['rut'];
|
||||
$dataMovimiento['digito'] = $ruts[0]['digito'];
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
@ -133,14 +161,28 @@ class Cartola extends Service
|
||||
$fechas = array_unique(array_map(function($movimiento) {
|
||||
return $movimiento['fecha']->format('Y-m-d');
|
||||
}, $movimientos));
|
||||
if (count($fechas) === 0) {
|
||||
throw new Read(__CLASS__);
|
||||
}
|
||||
|
||||
foreach ($fechas as $dia) {
|
||||
try {
|
||||
$this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, new DateTimeImmutable($dia));
|
||||
$dayDate = new DateTimeImmutable($dia);
|
||||
} catch (DateMalformedStringException) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $dayDate);
|
||||
continue;
|
||||
} catch (Exception\EmptyResult) {}
|
||||
|
||||
$movs = array_filter($movimientos, function($movimiento) use ($dia) {
|
||||
return $movimiento['fecha'] === $dia;
|
||||
return $movimiento['fecha']->format('Y-m-d') === $dia;
|
||||
});
|
||||
if (count($movs) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cargos = array_sum(array_map(function($movimiento) {
|
||||
return $movimiento['cargo'];
|
||||
}, $movs));
|
||||
@ -153,11 +195,19 @@ class Cartola extends Service
|
||||
'abonos' => $abonos,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
$this->buildCartola($cuenta, new DateTimeImmutable($dia), $cartolaData);
|
||||
$this->buildCartola($cuenta, $dayDate, $cartolaData);
|
||||
}
|
||||
|
||||
$startDate = new DateTimeImmutable(min($fechas));
|
||||
$endDate = new DateTimeImmutable(max($fechas));
|
||||
try {
|
||||
$startDate = new DateTimeImmutable(min($fechas));
|
||||
} catch (DateMalformedStringException $exception) {
|
||||
throw new Read(__CLASS__, $exception);
|
||||
}
|
||||
try {
|
||||
$endDate = new DateTimeImmutable(max($fechas));
|
||||
} catch (DateMalformedStringException $exception) {
|
||||
throw new Read(__CLASS__, $exception);
|
||||
}
|
||||
$movimientosFaltantes = $this->movimientoService->findMissing($cuenta, $addedMovimientos, $startDate, $endDate);
|
||||
$movimientosObsoletos = [];
|
||||
if (count($movimientosFaltantes) > 0) {
|
||||
|
||||
@ -3,6 +3,7 @@ namespace Incoviba\Service\Contabilidad\Cartola;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Incoviba\Common\Ideal\Cartola\Banco;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use PhpOffice\PhpSpreadsheet;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
@ -10,8 +11,8 @@ class Itau extends Banco
|
||||
{
|
||||
use isExcel;
|
||||
|
||||
const CUENTA_CORRIENTE = 0;
|
||||
const ULTIMOS_MOVIMIENTOS = 1;
|
||||
const int CUENTA_CORRIENTE = 0;
|
||||
const int ULTIMOS_MOVIMIENTOS = 1;
|
||||
|
||||
public function processMovimientosDiarios(array $movimientos): array
|
||||
{
|
||||
@ -44,6 +45,12 @@ class Itau extends Banco
|
||||
$ext = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION);
|
||||
return "/tmp/cartola.{$ext}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return array
|
||||
* @throws Read
|
||||
*/
|
||||
protected function parseFile(string $filename): array
|
||||
{
|
||||
$ext = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
@ -62,7 +69,7 @@ class Itau extends Banco
|
||||
break;
|
||||
}
|
||||
} catch (PhpSpreadsheet\Exception $exception) {
|
||||
$this->logger->critical($exception);
|
||||
throw new Read(__CLASS__, $exception);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
@ -166,6 +173,11 @@ class Itau extends Banco
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PhpSpreadsheet\Worksheet\Worksheet $sheet
|
||||
* @return int
|
||||
* @throws PhpSpreadsheet\Exception
|
||||
*/
|
||||
protected function identifySheet(PhpSpreadsheet\Worksheet\Worksheet $sheet): int
|
||||
{
|
||||
foreach ($sheet->getRowIterator(1, 10) as $row) {
|
||||
@ -177,7 +189,7 @@ class Itau extends Banco
|
||||
return self::ULTIMOS_MOVIMIENTOS;
|
||||
}
|
||||
}
|
||||
throw new PhpSpreadsheet\Exception();
|
||||
throw new PhpSpreadsheet\Exception('Incorrect type of Worksheet');
|
||||
}
|
||||
protected function getDateRange(PhpSpreadsheet\Worksheet\Row $row): array
|
||||
{
|
||||
|
||||
@ -5,11 +5,13 @@ use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
use DateInterval;
|
||||
use Incoviba\Common\Implement\Exception\EmptyRedis;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class IPC
|
||||
{
|
||||
protected string $redisKey = 'ipc';
|
||||
public function __construct(protected Redis $redisService, protected Money $moneyService) {}
|
||||
public function __construct(protected LoggerInterface $logger, protected Redis $redisService,
|
||||
protected Money $moneyService) {}
|
||||
|
||||
public function get(DateTimeInterface $from, DateTimeInterface $to = new DateTimeImmutable()): float
|
||||
{
|
||||
@ -21,7 +23,7 @@ class IPC
|
||||
$ipcs = [];
|
||||
try {
|
||||
$ipcs = json_decode($this->redisService->get($this->redisKey), JSON_OBJECT_AS_ARRAY);
|
||||
if (!isset($ipcs[$dateKey])) {
|
||||
if (!isset($ipcs[$dateKey]) or $ipcs[$dateKey] === 0) {
|
||||
throw new EmptyRedis($this->redisKey);
|
||||
}
|
||||
} catch (EmptyRedis) {
|
||||
@ -33,7 +35,7 @@ class IPC
|
||||
ksort($ipcs);
|
||||
$this->redisService->set($this->redisKey, json_encode($ipcs), 60 * 60 * 24 * 30);
|
||||
}
|
||||
return $ipcs[$dateKey];
|
||||
return $ipcs[$dateKey] * ($ipcs[$dateKey] > 1 ? 1/100 : 1);
|
||||
}
|
||||
public function readjust(float $base, DateTimeInterface $from, DateTimeInterface $to = new DateTimeImmutable()):float
|
||||
{
|
||||
|
||||
@ -8,32 +8,29 @@ use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
|
||||
class Money
|
||||
{
|
||||
const UF = 'uf';
|
||||
const USD = 'usd';
|
||||
const IPC = 'ipc';
|
||||
const string UF = 'uf';
|
||||
const string USD = 'usd';
|
||||
const string IPC = 'ipc';
|
||||
|
||||
public function __construct(protected LoggerInterface $logger) {}
|
||||
|
||||
protected array $providers;
|
||||
public function register(string $name, Provider $provider): Money
|
||||
protected array $providers = [];
|
||||
public function register(Provider $provider): Money
|
||||
{
|
||||
if (isset($this->providers) and isset($this->providers[$name]) and in_array($provider, $this->providers[$name])) {
|
||||
if (in_array($provider, $this->providers)) {
|
||||
return $this;
|
||||
}
|
||||
if (!isset($this->providers[$name])) {
|
||||
$this->providers[$name] = [];
|
||||
}
|
||||
$this->providers[$name] []= $provider;
|
||||
$this->providers []= $provider;
|
||||
return $this;
|
||||
}
|
||||
public function getProviders(string $name): array
|
||||
{
|
||||
return $this->providers[$name];
|
||||
return array_values(array_filter($this->providers, fn($provider) => $provider->supported($name)));
|
||||
}
|
||||
|
||||
public function get(string $provider, DateTimeInterface $dateTime): float
|
||||
public function get(string $name, DateTimeInterface $dateTime): float
|
||||
{
|
||||
$providers = $this->getProviders($provider);
|
||||
$providers = $this->getProviders($name);
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->get(self::getSymbol($provider), $dateTime);
|
||||
@ -45,7 +42,7 @@ class Money
|
||||
}
|
||||
public function getUF(?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
$providers = $this->getProviders('uf');
|
||||
$providers = $this->getProviders(self::UF);
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->get(self::UF, $dateTime);
|
||||
@ -60,10 +57,13 @@ class Money
|
||||
if ($start >= $end) {
|
||||
return 0;
|
||||
}
|
||||
$providers = $this->getProviders('ipc');
|
||||
$providers = $this->getProviders(self::IPC);
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->getVar($start, $end);
|
||||
if (method_exists($provider, 'getVar')) {
|
||||
return $provider->getVar($start, $end);
|
||||
}
|
||||
return $provider->get(self::IPC, $end);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->notice($exception);
|
||||
}
|
||||
@ -72,7 +72,7 @@ class Money
|
||||
}
|
||||
public function getUSD(DateTimeInterface $dateTime): float
|
||||
{
|
||||
$providers = $this->getProviders('usd');
|
||||
$providers = $this->getProviders(self::USD);
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->get(self::USD, $dateTime);
|
||||
|
||||
112
app/src/Service/Money/Findic.php
Normal file
112
app/src/Service/Money/Findic.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
namespace Incoviba\Service\Money;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
use JsonException;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Service\Money;
|
||||
|
||||
class Findic extends Ideal\Money\Provider
|
||||
{
|
||||
public function __construct(LoggerInterface $logger, protected ClientInterface $client)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
|
||||
protected string $url = 'https://findic.cl/api/';
|
||||
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
if (!$this->supported($money_symbol)) {
|
||||
throw new EmptyResponse($money_symbol);
|
||||
}
|
||||
if ($dateTime === null) {
|
||||
$dateTime = new DateTimeImmutable();
|
||||
}
|
||||
return match (strtolower($money_symbol)) {
|
||||
Money::UF, Money::USD => $this->getDate(strtolower($money_symbol), $dateTime),
|
||||
Money::IPC => $this->getSum(strtolower($money_symbol), $dateTime,
|
||||
DateTimeImmutable::createFromFormat('Y-m-d',
|
||||
$dateTime->modify('-1 year')->format('Y-11-01'))),
|
||||
default => throw new EmptyResponse($money_symbol),
|
||||
};
|
||||
}
|
||||
|
||||
protected array $supportedMap = [
|
||||
Money::UF => 'uf',
|
||||
Money::IPC => 'ipc',
|
||||
Money::USD => 'dolar'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $request_uri
|
||||
* @return float
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
protected function sendRequest(string $request_uri): array
|
||||
{
|
||||
try {
|
||||
$response = $this->client->get($request_uri);
|
||||
} catch (ClientExceptionInterface $exception) {
|
||||
throw new EmptyResponse($request_uri, $exception);
|
||||
}
|
||||
|
||||
if ((int) floor($response->getStatusCode() / 100) !== 2) {
|
||||
throw new EmptyResponse($request_uri);
|
||||
}
|
||||
$body = $response->getBody();
|
||||
try {
|
||||
$json = json_decode($body->getContents(), true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $exception) {
|
||||
throw new EmptyResponse($request_uri, $exception);
|
||||
}
|
||||
|
||||
if (!array_key_exists('serie', $json) or count($json['serie']) === 0) {
|
||||
throw new EmptyResponse($request_uri);
|
||||
}
|
||||
return $json['serie'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $symbol
|
||||
* @param DateTimeInterface $dateTime
|
||||
* @return float
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
protected function getDate(string $symbol, DateTimeInterface $dateTime): float
|
||||
{
|
||||
$request_uri = "{$this->supportedMap[strtolower($symbol)]}/{$dateTime->format('d-m-Y')}";
|
||||
return (float) $this->sendRequest($request_uri)[0]['valor'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $symbol
|
||||
* @param DateTimeInterface $dateTime
|
||||
* @param DateTimeInterface|null $fromDateTime
|
||||
* @return float
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
protected function getSum(string $symbol, DateTimeInterface $dateTime, ?DateTimeInterface $fromDateTime = null): float
|
||||
{
|
||||
$dateTime = $dateTime->modify('last day of this month');
|
||||
$requestUri = "{$this->supportedMap[strtolower($symbol)]}/{$dateTime->format('Y')}";
|
||||
$serie = $this->sendRequest($requestUri);
|
||||
$values = array_filter($serie, fn($month) => DateTimeImmutable::createFromFormat('Y-m-d', $month['fecha']) <= $dateTime);
|
||||
$value = array_reduce($values, fn($value, $month) => $value + ((float) $month['valor']), 0.0);
|
||||
|
||||
if ($fromDateTime === null) {
|
||||
$fromDateTime = $dateTime->modify('-1 year');
|
||||
} else {
|
||||
$fromDateTime = $fromDateTime->modify('last day of this month');
|
||||
}
|
||||
$requestUri = "{$this->supportedMap[strtolower($symbol)]}/{$fromDateTime->format('Y')}";
|
||||
$serie = $this->sendRequest($requestUri);
|
||||
$values = array_filter($serie, fn($month) => DateTimeImmutable::createFromFormat('Y-m-d', $month['fecha']) > $fromDateTime);
|
||||
return array_reduce($values, fn($value, $month) => $value + ((float) $month['valor']), $value);
|
||||
}
|
||||
}
|
||||
@ -6,14 +6,19 @@ use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
use DateInterval;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Common\Define\Money\Provider;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Service\Money;
|
||||
|
||||
class Ine implements Provider
|
||||
class Ine extends Ideal\Money\Provider
|
||||
{
|
||||
protected string $uri = 'https://api-calculadora.ine.cl/ServiciosCalculadoraVariacion';
|
||||
public function __construct(protected ClientInterface $client) {}
|
||||
public function __construct(LoggerInterface $logger, protected ClientInterface $client)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EmptyResponse
|
||||
@ -21,6 +26,9 @@ class Ine implements Provider
|
||||
*/
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
if (!$this->supported($money_symbol)) {
|
||||
throw new EmptyResponse($money_symbol);
|
||||
}
|
||||
if ($dateTime === null) {
|
||||
$dateTime = new DateTimeImmutable();
|
||||
}
|
||||
@ -29,6 +37,10 @@ class Ine implements Provider
|
||||
return $this->getVar($start, $end);
|
||||
}
|
||||
|
||||
protected array $supportedMap = [
|
||||
Money::IPC => 'ipc'
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
|
||||
@ -3,22 +3,33 @@ namespace Incoviba\Service\Money;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Incoviba\Common\Define\Money\Provider;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Service\Money;
|
||||
|
||||
class MiIndicador implements Provider
|
||||
class MiIndicador extends Ideal\Money\Provider
|
||||
{
|
||||
public function __construct(protected ClientInterface $client) {}
|
||||
public function __construct(LoggerInterface $logger, protected ClientInterface $client)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $money_symbol
|
||||
* @param DateTimeInterface|null $dateTime
|
||||
* @return float
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
$request_uri = "{$money_symbol}";
|
||||
if (!$this->supported($money_symbol)) {
|
||||
throw new EmptyResponse($money_symbol);
|
||||
}
|
||||
$request_uri = "{$this->supportedMap[strtolower($money_symbol)]}";
|
||||
if ($dateTime !== null) {
|
||||
$request_uri = "{$money_symbol}/{$dateTime->format('d-m-Y')}";
|
||||
$request_uri = "{$request_uri}/{$dateTime->format('d-m-Y')}";
|
||||
}
|
||||
try {
|
||||
$response = $this->client->get($request_uri);
|
||||
@ -33,9 +44,14 @@ class MiIndicador implements Provider
|
||||
$body = $response->getBody();
|
||||
$json = json_decode($body->getContents());
|
||||
|
||||
if (empty($json) or $json->codigo !== $money_symbol or count($json->serie) === 0) {
|
||||
if (empty($json) or !isset($json->codigo) or !isset($json->serie) or $json->codigo !== $money_symbol or count($json->serie) === 0) {
|
||||
throw new EmptyResponse($request_uri);
|
||||
}
|
||||
return $json->serie[0]->valor;
|
||||
}
|
||||
protected array $supportedMap = [
|
||||
Money::UF => 'uf',
|
||||
Money::IPC => 'ipc',
|
||||
Money::USD => 'dolar'
|
||||
];
|
||||
}
|
||||
|
||||
@ -6,27 +6,38 @@ use DateTimeImmutable;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Dom\HTMLDocument;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Common\Define;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
|
||||
class SII implements Define\Money\Provider
|
||||
class SII extends Ideal\Money\Provider
|
||||
{
|
||||
public function __construct(protected ClientInterface $client,
|
||||
protected Repository\UF $ufRepository) {}
|
||||
public function __construct(LoggerInterface $logger, protected ClientInterface $client,
|
||||
protected Repository\UF $ufRepository)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
if ($money_symbol === Service\Money::UF) {
|
||||
if (!$this->supported($money_symbol)) {
|
||||
throw new EmptyResponse("{$money_symbol} not found in " . __CLASS__);
|
||||
}
|
||||
if (strtolower($money_symbol) === Service\Money::UF) {
|
||||
return $this->getUF($dateTime);
|
||||
}
|
||||
$class = __CLASS__;
|
||||
throw new EmptyResponse("{$money_symbol} not found in {$class}");
|
||||
}
|
||||
|
||||
protected array $supportedMap = [
|
||||
Service\Money::UF => 'uf'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param DateTimeInterface|null $dateTime
|
||||
* @return float
|
||||
|
||||
@ -236,6 +236,9 @@ class Persona extends Ideal\Service
|
||||
} else {
|
||||
$newKey = substr($key, strlen('address_'));
|
||||
}
|
||||
if ($newKey === 'id') {
|
||||
continue;
|
||||
}
|
||||
$addressData[$newKey] = $value;
|
||||
}
|
||||
if (!empty($addressData)) {
|
||||
@ -258,7 +261,7 @@ class Persona extends Ideal\Service
|
||||
'birthdate' => 'fecha_nacimiento',
|
||||
];
|
||||
foreach ($data as $key => $value) {
|
||||
if (array_key_exists($key, $dataMap)) {
|
||||
if (array_key_exists($key, $dataMap) and trim($value) !== '') {
|
||||
$data[$dataMap[$key]] = $value;
|
||||
unset($data[$key]);
|
||||
}
|
||||
|
||||
@ -79,13 +79,7 @@ class Queue extends Ideal\Service
|
||||
try {
|
||||
$this->jobService->update($job);
|
||||
} catch (Update $exception) {
|
||||
$this->logger->error($exception->getMessage(), ['job' => $job, 'exception' => [
|
||||
'code' => $exception->getCode(),
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'trace' => $exception->getTraceAsString(),
|
||||
]]);
|
||||
$this->logger->error($exception->getMessage(), ['job' => $job, 'exception' => $exception]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ class UF
|
||||
if ($date === null) {
|
||||
$date = new DateTimeImmutable();
|
||||
}
|
||||
if ($date->diff($today)->days < 0) {
|
||||
if ($date->diff($today)->invert === 1) {
|
||||
return 0.0;
|
||||
}
|
||||
/**
|
||||
@ -32,7 +32,7 @@ class UF
|
||||
*/
|
||||
try {
|
||||
$ufs = $this->getRedisUFs();
|
||||
if (!isset($ufs[$date->format('Y-m-d')])) {
|
||||
if (!isset($ufs[$date->format('Y-m-d')]) or $ufs[$date->format('Y-m-d')] === 0) {
|
||||
throw new EmptyRedis($this->redisKey);
|
||||
}
|
||||
return $ufs[$date->format('Y-m-d')];
|
||||
@ -54,12 +54,16 @@ class UF
|
||||
}
|
||||
public function updateMany(array $dates): array
|
||||
{
|
||||
$today = new DateTimeImmutable();
|
||||
$ufs = [];
|
||||
try {
|
||||
$ufs = json_decode($this->redisService->get($this->redisKey), JSON_OBJECT_AS_ARRAY);
|
||||
} catch (EmptyRedis) {}
|
||||
$updated = [];
|
||||
foreach ($dates as $date) {
|
||||
if ($date->diff($today)->invert === 1) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($ufs[$date->format('Y-m-d')]) or $ufs[$date->format('Y-m-d')] === 0) {
|
||||
$uf = $this->moneyService->getUF($date);
|
||||
if ($uf === 0.0) {
|
||||
|
||||
@ -19,7 +19,7 @@ class USD
|
||||
$usds = [];
|
||||
try {
|
||||
$usds = json_decode($this->redisService->get($this->redisKey), JSON_OBJECT_AS_ARRAY);
|
||||
if (!isset($usds[$date->format('Y-m-d')])) {
|
||||
if (!isset($usds[$date->format('Y-m-d')]) or $usds[$date->format('Y-m-d')] === 0) {
|
||||
throw new EmptyRedis($this->redisKey);
|
||||
}
|
||||
$usd = $usds[$date->format('Y-m-d')];
|
||||
|
||||
@ -4,6 +4,7 @@ namespace Incoviba\Service\Venta;
|
||||
use DateMalformedStringException;
|
||||
use Exception;
|
||||
use DateTimeImmutable;
|
||||
use Incoviba\Exception\ServiceAction\Update;
|
||||
use PDOException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Ideal;
|
||||
@ -55,7 +56,11 @@ class Credito extends Ideal\Service
|
||||
} catch (DateMalformedStringException) {
|
||||
$fecha = new DateTimeImmutable();
|
||||
}
|
||||
$uf = $this->valorService->clean($data['uf']) ?? $this->moneyService->getUF($fecha);
|
||||
if (array_key_exists('uf', $data)) {
|
||||
$uf = $this->valorService->clean($data['uf']) ?? $this->moneyService->getUF($fecha);
|
||||
} else {
|
||||
$uf = $this->moneyService->getUF($fecha);
|
||||
}
|
||||
try {
|
||||
$tipoPago = $this->tipoPagoRepository->fetchByDescripcion('carta de resguardo');
|
||||
} catch (EmptyResult $exception) {
|
||||
@ -85,13 +90,20 @@ class Credito extends Ideal\Service
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @param Model\Venta\Credito $credito
|
||||
* @param array $data
|
||||
* @return Model\Venta\Credito
|
||||
* @throws Update
|
||||
*/
|
||||
public function edit(Model\Venta\Credito $credito, array $data): Model\Venta\Credito
|
||||
{
|
||||
$uf = $this->moneyService->getUF($credito->pago->fecha);
|
||||
if (array_key_exists('fecha', $data)) {
|
||||
$fecha = new DateTimeImmutable($data['fecha']);
|
||||
try {
|
||||
$fecha = new DateTimeImmutable($data['fecha']);
|
||||
} catch (DateMalformedStringException $exception) {
|
||||
throw new Update(__CLASS__, $exception);
|
||||
}
|
||||
$data['fecha'] = $fecha->format('Y-m-d');
|
||||
$uf = $this->moneyService->getUF($fecha);
|
||||
$data['uf'] = $uf;
|
||||
@ -112,6 +124,10 @@ class Credito extends Ideal\Service
|
||||
}
|
||||
$credito->pago = $this->pagoService->edit($credito->pago, $filteredDataPago);
|
||||
|
||||
return $this->creditoRepository->edit($credito, $filteredData);
|
||||
try {
|
||||
return $this->creditoRepository->edit($credito, $filteredData);
|
||||
} catch (EmptyResult $exception) {
|
||||
throw new Update(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ namespace Incoviba\Service\Venta;
|
||||
use Exception;
|
||||
use DateTimeImmutable;
|
||||
use DateMalformedStringException;
|
||||
use Incoviba\Exception\ServiceAction\Read;
|
||||
use Incoviba\Exception\ServiceAction\Update;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResult;
|
||||
@ -56,13 +58,15 @@ class Escritura extends Ideal\Service
|
||||
|
||||
/**
|
||||
* @throws EmptyResult
|
||||
* @throws Read
|
||||
* @throws Update
|
||||
*/
|
||||
public function edit(int $venta_id, array $data): Model\Venta\Escritura
|
||||
{
|
||||
$venta = $this->ventaService->getById($venta_id);
|
||||
$estado = $venta->currentEstado();
|
||||
if (!in_array($estado->tipoEstadoVenta->descripcion, ['escriturando', 'firmado por inmobiliaria'])) {
|
||||
throw new EmptyResult('');
|
||||
throw new Update(__CLASS__);
|
||||
}
|
||||
try {
|
||||
$data['fecha'] = (new DateTimeImmutable($data['fecha']))->format('Y-m-d');
|
||||
|
||||
@ -116,8 +116,8 @@ class Pago extends Ideal\Service\Repository
|
||||
try {
|
||||
$pago = $this->pagoRepository->fetchById($pago_id);
|
||||
return $this->process($pago);
|
||||
} catch (EmptyResult) {
|
||||
throw new Read(__CLASS__);
|
||||
} catch (EmptyResult $exception) {
|
||||
throw new Read(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -72,22 +72,22 @@ class Import extends Ideal\Service
|
||||
}
|
||||
$baseTitlesMap = [
|
||||
'proyecto' => [],
|
||||
'precio' => [],
|
||||
'precio' => ['valor'],
|
||||
'unidad' => ['departamento'],
|
||||
'tipo' => ['tipo unidad'],
|
||||
'fecha' => []
|
||||
'fecha' => [],
|
||||
];
|
||||
|
||||
$realTitlesMap = [];
|
||||
$titles = array_keys($data[0]);
|
||||
foreach ($baseTitlesMap as $base => $optionals) {
|
||||
foreach ($titles as $title) {
|
||||
if (str_contains($title, $base)) {
|
||||
if (str_contains(strtolower($title), $base)) {
|
||||
$realTitlesMap[$title] = $base;
|
||||
break;
|
||||
}
|
||||
foreach ($optionals as $optional) {
|
||||
if (str_contains($title, $optional)) {
|
||||
if (str_contains(strtolower($title), $optional)) {
|
||||
$realTitlesMap[$title] = $base;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@ class Reservation extends Ideal\Service\API
|
||||
protected Repository\Venta\Reservation\State $stateRepository,
|
||||
protected Service\Persona $personaService,
|
||||
protected Service\Proyecto\Broker $brokerService,
|
||||
protected Promotion $promotionService, protected Unidad $unitService)
|
||||
protected Promotion $promotionService, protected Unidad $unitService,
|
||||
protected Pie $pieService, protected Credito $creditService,
|
||||
protected Subsidio $subsidyService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
@ -116,81 +118,36 @@ class Reservation extends Ideal\Service\API
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return Model\Venta\Reservation
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
public function add(array $data): Model\Venta\Reservation
|
||||
{
|
||||
$date = new DateTimeImmutable();
|
||||
$date = $this->parseDate($data['date'] ?? 'now');
|
||||
|
||||
try {
|
||||
$date = new DateTimeImmutable($data['date']);
|
||||
} catch (DateMalformedStringException) {}
|
||||
try {
|
||||
$reservation = $this->reservationRepository->fetchByBuyerAndUnitAndDate($data['buyer_rut'], (int) $data['units'][0], $date);
|
||||
|
||||
if (array_key_exists('broker_rut', $data) and $data['broker_rut'] !== '') {
|
||||
try {
|
||||
$broker = $this->brokerService->get($data['broker_rut']);
|
||||
$reservation = $this->reservationRepository->edit($reservation, ['broker_rut' => $broker->rut]);
|
||||
} catch (ServiceAction\Read) {}
|
||||
}
|
||||
} catch (Implement\Exception\EmptyResult) {
|
||||
if (!$this->reservationRepository->getConnection()->getPDO()->inTransaction()) {
|
||||
$this->reservationRepository->getConnection()->getPDO()->beginTransaction();
|
||||
}
|
||||
$buyerData = [];
|
||||
foreach ($data as $key => $value) {
|
||||
if (!str_starts_with($key, 'buyer_')) {
|
||||
continue;
|
||||
}
|
||||
$buyerData[substr($key, strlen('buyer_'))] = $value;
|
||||
}
|
||||
$this->personaService->add($buyerData);
|
||||
|
||||
$data['date'] = $date->format('Y-m-d');
|
||||
try {
|
||||
$reservationData = $this->reservationRepository->filterData($data);
|
||||
$reservation = $this->reservationRepository->create($reservationData);
|
||||
$reservation = $this->reservationRepository->save($reservation);
|
||||
|
||||
$stateType = Model\Venta\Reservation\State\Type::INACTIVE;
|
||||
$stateData = [
|
||||
'reservation_id' => $reservation->id,
|
||||
'date' => $data['date'],
|
||||
'type' => $stateType->value,
|
||||
];
|
||||
$state = $this->stateRepository->create($stateData);
|
||||
$this->stateRepository->save($state);
|
||||
|
||||
$units = array_combine($data['units'], $data['units_value']);
|
||||
$this->addUnits($reservation, $units);
|
||||
|
||||
if (array_key_exists('broker_rut', $data) and !empty($data['broker_rut'])) {
|
||||
$this->addBroker($reservation, $data['broker_rut']);
|
||||
}
|
||||
|
||||
if (array_key_exists('promotions', $data)) {
|
||||
$this->addPromotions($reservation, $data['promotions']);
|
||||
}
|
||||
|
||||
if ($this->reservationRepository->getConnection()->getPDO()->inTransaction()) {
|
||||
$this->reservationRepository->getConnection()->getPDO()->commit();
|
||||
}
|
||||
} catch (PDOException $exception) {
|
||||
$this->logger->warning($exception->getMessage(), ['exception' => $exception->getTraceAsString()]);
|
||||
if ($this->reservationRepository->getConnection()->getPDO()->inTransaction()) {
|
||||
$this->reservationRepository->getConnection()->getPDO()->rollBack();
|
||||
}
|
||||
throw new ServiceAction\Create(__CLASS__, $exception);
|
||||
}
|
||||
return $this->updateExistingReservation($data, $date);
|
||||
} catch (Read | ServiceAction\Update) {
|
||||
return $this->createNewReservation($data, $date);
|
||||
}
|
||||
|
||||
return $this->process($reservation);
|
||||
}
|
||||
public function edit(Define\Model $model, array $new_data): Model\Venta\Reservation
|
||||
public function edit(Define\Model $model, array $newData): Model\Venta\Reservation
|
||||
{
|
||||
$editData = [];
|
||||
foreach ($newData as $key => $value) {
|
||||
$newKey = str_replace('edit_', '', $key);
|
||||
$editData[$newKey] = $value;
|
||||
}
|
||||
try {
|
||||
return $this->process($this->reservationRepository->edit($model, $new_data));
|
||||
$reservation = $this->reservationRepository->edit($model, $editData);
|
||||
} catch (PDOException | Implement\Exception\EmptyResult $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
$paymentData = array_filter($editData, fn($key) => str_starts_with($key, 'payment_'), ARRAY_FILTER_USE_KEY);
|
||||
$this->editPayment($reservation, $paymentData);
|
||||
return $this->process($reservation);
|
||||
}
|
||||
public function delete(int $id): Model\Venta\Reservation
|
||||
{
|
||||
@ -251,6 +208,12 @@ class Reservation extends Ideal\Service\API
|
||||
return $this->stateRepository->fetchByReservation($reservation_id);
|
||||
})
|
||||
);
|
||||
foreach ($model->units as &$unit) {
|
||||
$unit->unit = $this->unitService->getById($unit->unit->id);
|
||||
}
|
||||
foreach ($model->promotions as &$promotion) {
|
||||
$promotion = $this->promotionService->getById($promotion->id);
|
||||
}
|
||||
$model->buyer = $this->personaService->getById($model->buyer->rut);
|
||||
return $model;
|
||||
}
|
||||
@ -274,6 +237,13 @@ class Reservation extends Ideal\Service\API
|
||||
$this->reservationRepository->save($reservation);
|
||||
} catch (ServiceAction\Read) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $promotions
|
||||
* @return void
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function addPromotions(Model\Venta\Reservation $reservation, array $promotions): void
|
||||
{
|
||||
foreach ($promotions as $promotion_id) {
|
||||
@ -286,4 +256,434 @@ class Reservation extends Ideal\Service\API
|
||||
}
|
||||
$this->reservationRepository->save($reservation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $payment
|
||||
* @return void
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
protected function addPayment(Model\Venta\Reservation $reservation, array $payment): void
|
||||
{
|
||||
$fields = [
|
||||
'pie',
|
||||
'advance',
|
||||
'cuotas',
|
||||
'investments',
|
||||
'credito',
|
||||
'credit',
|
||||
'subsidio',
|
||||
'subsidy',
|
||||
'ahorro',
|
||||
'savings',
|
||||
];
|
||||
$filteredData = array_intersect_key($payment, array_flip($fields));
|
||||
$map = [
|
||||
'pie' => 'advance',
|
||||
'cuotas' => 'investments',
|
||||
'credito' => 'credit',
|
||||
'subsidio' => 'subsidy',
|
||||
'ahorro' => 'savings'
|
||||
];
|
||||
$mappedData = [];
|
||||
foreach ($filteredData as $key => $value) {
|
||||
$mappedData[$map[$key]] = $value;
|
||||
}
|
||||
if (array_key_exists('advance', $mappedData)) {
|
||||
$this->addAdvance($reservation, $mappedData);
|
||||
}
|
||||
if (array_key_exists('credit', $mappedData)) {
|
||||
$this->addCredit($reservation, $mappedData);
|
||||
}
|
||||
if (array_key_exists('subsidy', $mappedData)) {
|
||||
$this->addSubsidy($reservation, $mappedData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
protected function editPayment(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$fields = [
|
||||
'pie',
|
||||
'advance',
|
||||
'cuotas',
|
||||
'investments',
|
||||
'credito',
|
||||
'credit',
|
||||
'subsidio',
|
||||
'subsidy',
|
||||
'ahorro',
|
||||
'savings',
|
||||
];
|
||||
$filteredData = [];
|
||||
foreach ($data as $key => $value) {
|
||||
$newKey = str_replace('payment_', '', $key);
|
||||
if (in_array($newKey, $fields)) {
|
||||
$filteredData[$newKey] = $value;
|
||||
}
|
||||
}
|
||||
$map = [
|
||||
'pie' => 'advance',
|
||||
'cuotas' => 'investments',
|
||||
'credito' => 'credit',
|
||||
'subsidio' => 'subsidy',
|
||||
'ahorro' => 'savings'
|
||||
];
|
||||
$mappedData = [];
|
||||
foreach ($filteredData as $key => $value) {
|
||||
if (array_key_exists($key, $map)) {
|
||||
$mappedData[$map[$key]] = $value;
|
||||
continue;
|
||||
}
|
||||
$mappedData[$key] = $value;
|
||||
}
|
||||
if (array_key_exists('advance', $mappedData)) {
|
||||
$this->editAdvance($reservation, $mappedData);
|
||||
} else {
|
||||
$this->removeAdvance($reservation);
|
||||
}
|
||||
if (array_key_exists('credit', $mappedData)) {
|
||||
$this->editCredit($reservation, $mappedData);
|
||||
} else {
|
||||
$this->removeCredit($reservation);
|
||||
}
|
||||
/*if (array_key_exists('subsidy', $mappedData)) {
|
||||
$this->editSubsidy($reservation, $mappedData);
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
protected function addAdvance(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$fields = [
|
||||
'advance',
|
||||
'investments'
|
||||
];
|
||||
$filteredData = array_intersect_key($data, array_flip($fields));
|
||||
$inputData = [
|
||||
'fecha' => $reservation->date->format('Y-m-d'),
|
||||
'valor' => $filteredData['advance'],
|
||||
'cuotas' => $filteredData['investments']
|
||||
];
|
||||
$pie = $this->pieService->add($inputData);
|
||||
try {
|
||||
$this->reservationRepository->saveDetail($reservation, Model\Venta\Reservation\Detail\Type::Advance->value, $pie->id);
|
||||
} catch (PDOException $exception) {
|
||||
throw new ServiceAction\Create(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
protected function editAdvance(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$fields = [
|
||||
'advance',
|
||||
'investments'
|
||||
];
|
||||
$filteredData = array_intersect_key($data, array_flip($fields));
|
||||
$inputData = [
|
||||
'fecha' => $reservation->date->format('Y-m-d'),
|
||||
'valor' => $filteredData['advance'],
|
||||
'cuotas' => $filteredData['investments']
|
||||
];
|
||||
if (isset($reservation->payment) and isset($reservation->payment->pie)) {
|
||||
$this->pieService->edit($reservation->payment->pie, $inputData);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$pie = $this->pieService->add($inputData);
|
||||
} catch (ServiceAction\Create $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
try {
|
||||
$this->reservationRepository->saveDetail($reservation, Model\Venta\Reservation\Detail\Type::Advance->value, $pie->id);
|
||||
} catch (PDOException $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
protected function removeAdvance(Model\Venta\Reservation $reservation): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
protected function addCredit(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$creditValue = $data['credit'];
|
||||
$inputData = [
|
||||
'fecha' => $reservation->date->format('Y-m-d'),
|
||||
'valor' => $creditValue
|
||||
];
|
||||
$credit = $this->creditService->add($inputData);
|
||||
try {
|
||||
$this->reservationRepository->saveDetail($reservation, Model\Venta\Reservation\Detail\Type::Credit->value, $credit->id);
|
||||
} catch (PDOException $exception) {
|
||||
throw new ServiceAction\Create(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
protected function editCredit(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$creditValue = $data['credit'];
|
||||
$inputData = [
|
||||
'fecha' => $reservation->date->format('Y-m-d'),
|
||||
'valor' => $creditValue
|
||||
];
|
||||
if (isset($reservation->payment) and isset($reservation->payment->credito)) {
|
||||
$this->creditService->edit($reservation->payment->credito, $inputData);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$credit = $this->creditService->add($inputData);
|
||||
} catch (ServiceAction\Create $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
try {
|
||||
$this->reservationRepository->saveDetail($reservation, Model\Venta\Reservation\Detail\Type::Credit->value, $credit->id);
|
||||
} catch (PDOException $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
protected function removeCredit(Model\Venta\Reservation $reservation): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
protected function addSubsidy(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$fields = [
|
||||
'subsidy',
|
||||
'savings'
|
||||
];
|
||||
$filteredData = array_intersect_key($data, array_flip($fields));
|
||||
$inputData = [
|
||||
'fecha' => $reservation->date->format('Y-m-d'),
|
||||
'subsidio' => $filteredData['subsidy'],
|
||||
'ahorro' => $filteredData['savings']
|
||||
];
|
||||
$subsidy = $this->subsidyService->add($inputData);
|
||||
try {
|
||||
$this->reservationRepository->saveDetail($reservation, Model\Venta\Reservation\Detail\Type::Subsidy->value, $subsidy->id);
|
||||
} catch (PDOException $exception) {
|
||||
throw new ServiceAction\Create(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
/*protected function editSubsidy(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
$fields = [
|
||||
'subsidy',
|
||||
'savings'
|
||||
];
|
||||
$filteredData = array_intersect_key($data, array_flip($fields));
|
||||
$inputData = [
|
||||
'fecha' => $reservation->date->format('Y-m-d'),
|
||||
'subsidio' => $filteredData['subsidy'],
|
||||
'ahorro' => $filteredData['savings']
|
||||
];
|
||||
if (isset($reservation->payment) and isset($reservation->payment->subsidy)) {
|
||||
$this->subsidyService->edit($reservation->payment->subsidy, $inputData);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$subsidy = $this->subsidyService->add($inputData);
|
||||
} catch (ServiceAction\Create $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
try {
|
||||
$this->reservationRepository->saveDetail($reservation, Model\Venta\Reservation\Detail\Type::Subsidy->value, $subsidy->id);
|
||||
} catch (PDOException $exception) {
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param DateTimeImmutable $date
|
||||
* @return Model\Venta\Reservation
|
||||
* @throws Read
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
protected function updateExistingReservation(array $data, DateTimeImmutable $date): Model\Venta\Reservation
|
||||
{
|
||||
try {
|
||||
$reservation = $this->reservationRepository->fetchByBuyerAndUnitAndDate(
|
||||
$data['buyer_rut'],
|
||||
(int) ($data['units'][0] ?? 0),
|
||||
$date
|
||||
);
|
||||
} catch (Implement\Exception\EmptyResult $exception) {
|
||||
throw new Read(__CLASS__, $exception);
|
||||
}
|
||||
|
||||
if (!empty($data['broker_rut'] ?? null)) {
|
||||
$this->updateBrokerForReservation($reservation, $data['broker_rut']);
|
||||
}
|
||||
|
||||
return $this->process($reservation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param DateTimeImmutable $date
|
||||
* @return Model\Venta\Reservation
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
protected function createNewReservation(array $data, DateTimeImmutable $date): Model\Venta\Reservation
|
||||
{
|
||||
$pdo = $this->reservationRepository->getConnection()->getPDO();
|
||||
$inTransaction = $pdo->inTransaction();
|
||||
|
||||
try {
|
||||
if (!$inTransaction) {
|
||||
$pdo->beginTransaction();
|
||||
}
|
||||
|
||||
$this->createOrUpdateBuyer($data);
|
||||
$reservation = $this->persistReservation($data, $date->format('Y-m-d'));
|
||||
$this->createInitialState($reservation, $date);
|
||||
|
||||
$this->processReservationDetails($reservation, $data);
|
||||
|
||||
if (!$inTransaction) {
|
||||
$pdo->commit();
|
||||
}
|
||||
|
||||
return $this->process($reservation);
|
||||
} catch (PDOException $exception) {
|
||||
if (!$inTransaction && $pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$this->logger->error('Failed to create reservation', [
|
||||
'error' => $exception->getMessage(),
|
||||
'data' => $this->sanitizeData($data)
|
||||
]);
|
||||
throw new ServiceAction\Create(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
protected function parseDate(?string $dateString): DateTimeImmutable
|
||||
{
|
||||
try {
|
||||
return new DateTimeImmutable($dateString);
|
||||
} catch (\Exception) {
|
||||
return new DateTimeImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws ServiceAction\Create
|
||||
*/
|
||||
protected function createOrUpdateBuyer(array $data): void
|
||||
{
|
||||
$buyerData = array_filter($data, fn($key) => str_starts_with($key, 'buyer_'), ARRAY_FILTER_USE_KEY);
|
||||
$buyerData = array_combine(
|
||||
array_map(fn($key) => substr($key, strlen('buyer_')), array_keys($buyerData)),
|
||||
$buyerData
|
||||
);
|
||||
|
||||
$this->personaService->add($buyerData);
|
||||
}
|
||||
protected function persistReservation(array $data, string $date): Model\Venta\Reservation
|
||||
{
|
||||
$data['date'] = $date;
|
||||
$reservationData = $this->reservationRepository->filterData($data);
|
||||
$reservation = $this->reservationRepository->create($reservationData);
|
||||
return $this->reservationRepository->save($reservation);
|
||||
}
|
||||
protected function createInitialState(Model\Venta\Reservation $reservation, DateTimeInterface $date): void
|
||||
{
|
||||
$stateData = [
|
||||
'reservation_id' => $reservation->id,
|
||||
'date' => $date->format('Y-m-d'),
|
||||
'type' => Model\Venta\Reservation\State\Type::INACTIVE->value,
|
||||
];
|
||||
$state = $this->stateRepository->create($stateData);
|
||||
$this->stateRepository->save($state);
|
||||
}
|
||||
protected function processReservationDetails(Model\Venta\Reservation $reservation, array $data): void
|
||||
{
|
||||
if (!empty($data['units']) && !empty($data['units_value'])) {
|
||||
$units = array_combine($data['units'], $data['units_value']);
|
||||
$this->addUnits($reservation, $units);
|
||||
}
|
||||
|
||||
if (!empty($data['broker_rut'])) {
|
||||
$this->addBroker($reservation, $data['broker_rut']);
|
||||
}
|
||||
|
||||
if (!empty($data['promotions'])) {
|
||||
$this->addPromotions($reservation, $data['promotions']);
|
||||
}
|
||||
|
||||
$paymentData = array_filter($data, fn($key) => str_starts_with($key, 'payment_'), ARRAY_FILTER_USE_KEY);
|
||||
if (!empty($paymentData)) {
|
||||
$this->addPayment($reservation, $paymentData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model\Venta\Reservation $reservation
|
||||
* @param string $brokerRut
|
||||
* @return void
|
||||
* @throws ServiceAction\Update
|
||||
*/
|
||||
protected function updateBrokerForReservation(Model\Venta\Reservation $reservation, string $brokerRut): void
|
||||
{
|
||||
try {
|
||||
$broker = $this->brokerService->get($brokerRut);
|
||||
$this->reservationRepository->edit($reservation, ['broker_rut' => $broker->rut]);
|
||||
} catch (Implement\Exception\EmptyResult | ServiceAction\Read $exception) {
|
||||
$this->logger->warning('Broker not found', ['broker_rut' => $brokerRut]);
|
||||
throw new ServiceAction\Update(__CLASS__, $exception);
|
||||
}
|
||||
}
|
||||
protected function sanitizeData(array $data): array
|
||||
{
|
||||
$sensitiveFields = ['password', 'token', 'api_key', 'rut'];
|
||||
return array_map(
|
||||
fn($value, $key) => in_array($key, $sensitiveFields, true) ? '***' : $value,
|
||||
$data,
|
||||
array_keys($data)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user