<repo_name>pf2019g4/utn-simulador-ui<file_sep>/src/app/premios-detail/premios-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { EscenariosService } from '../escenarios.service';
import { MessagesService } from '../messages.service';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'app-premios-detail',
  templateUrl: './premios-detail.component.html',
  styleUrls: ['./premios-detail.component.css']
})
export class PremiosDetailComponent implements OnInit {

  escenario;
  curso;

  constructor(private route: ActivatedRoute,
    private escenariosService: EscenariosService,
    private usuarioService: UsuarioService,
    private messageService: MessagesService) { }

  ngOnInit() {
    const escenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    this.curso = this.usuarioService.usuario.curso;
    this.getEscenario(escenarioId);
  }

  getEscenario(escenarioId) {
    this.escenariosService.getEscenario(escenarioId).subscribe(escenario => {
      this.escenario = escenario
    })
  }
}
<file_sep>/src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PremiosComponent } from './premios/premios.component';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { DecisionesComponent } from './decisiones/decisiones.component';
import { DecisionDetalleComponent } from './decision-detalle/decision-detalle.component';
import { FlujoFondosComponent } from './flujo-fondos/flujo-fondos.component';
import { TomaDecisionesComponent } from './toma-decisiones/toma-decisiones.component';
import { ResultadosComponent } from './resultados/resultados.component';
import { ResultadosAdminComponent } from './resultados-admin/resultados-admin.component';
import { EscenariosComponent } from './escenarios/escenarios.component';
import { EscenarioDetalleComponent } from './escenario-detalle/escenario-detalle.component';
import { SimulacionesComponent } from './simulaciones/simulaciones.component';
import { ProveedorDetailComponent } from './proveedor-detail/proveedor-detail.component';
import { EstadoJuegosComponent } from './estado-juegos/estado-juegos.component';
import { CursosComponent } from './cursos/cursos.component';
import { EstadoJuegoDetailComponent } from './estado-juego-detail/estado-juego-detail.component';
import { SimulacionEntregadaComponent } from './simulacion-entregada/simulacion-entregada.component';
import { PremiosDetailComponent } from './premios-detail/premios-detail.component';


const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: 'login', component: LoginComponent },
  { path: 'home', redirectTo: 'simulaciones' },
  {
    path: 'simulaciones', component: HomeComponent,
    children: [{ path: '', component: SimulacionesComponent, pathMatch: 'full' }]
  },
  {
    path: 'simulacion-entregada', component: HomeComponent,
    children: [{ path: '', component: SimulacionEntregadaComponent, pathMatch: 'full' }]
  },
  {
    path: 'simulaciones/escenario/:escenarioId/resultados', component: HomeComponent,
    children: [{ path: '', component: ResultadosComponent, pathMatch: 'full' }]
  },
  {
    path: 'simulaciones/escenario/:escenarioId/resultados/curso/:cursoId/usuario/:usuarioId', component: HomeComponent,
    children: [{ path: '', component: ResultadosAdminComponent, pathMatch: 'full' }]
  },
  {
    path: 'premios', component: HomeComponent,
    children: [{ path: '', component: PremiosComponent, pathMatch: 'full' }]
  },
  {
    path: 'premios/escenario/:escenarioId', component: HomeComponent,
    children: [{ path: '', component: PremiosDetailComponent, pathMatch: 'full' }]
  },
  {
    path: 'escenarios', component: HomeComponent,
    children: [{ path: '', component: EscenariosComponent, pathMatch: 'full' }]
  }, 
  {
    path: 'cursos', component: HomeComponent,
    children: [{ path: '', component: CursosComponent, pathMatch: 'full' }]
  },
  {
    path: 'estado-juegos', component: HomeComponent,
    children: [{ path: '', component: EstadoJuegosComponent, pathMatch: 'full' }]
  },
  {
    path: 'simulaciones/escenario/:escenarioId', component: HomeComponent,
    children: [{ path: '', component: TomaDecisionesComponent, pathMatch: 'full' }]
  },
  {
    path: 'estado-juego/curso/:cursoId/escenario/:escenarioId', component: HomeComponent,
    children: [{ path: '', component: EstadoJuegoDetailComponent, pathMatch: 'full' }]
  },
  { path: 'escenarios/:escenarioId/decisiones/new', component: DecisionDetalleComponent, pathMatch: 'full' },
  { path: 'escenarios/:escenarioId/decisiones/:id', component: DecisionDetalleComponent, pathMatch: 'full' },
  { path: 'escenarios/:escenarioId/proveedores/new', component: ProveedorDetailComponent, pathMatch: 'full' },
  { path: 'escenarios/:escenarioId/proveedores/:id', component: ProveedorDetailComponent, pathMatch: 'full' },
  { path: 'escenarios/new', component: EscenarioDetalleComponent, pathMatch: 'full' },
  { path: 'escenarios/:id', component: EscenarioDetalleComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
})
export class AppRoutingModule { }
<file_sep>/src/app/proveedor-detail/proveedor-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { Validators, FormControl, FormGroup } from '@angular/forms';
import { ProveedoresService } from '../proveedores.service';
import { ActivatedRoute, Router } from '@angular/router';
import { MessagesService } from '../messages.service';
import { EscenariosService } from '../escenarios.service';

@Component({
  selector: 'app-proveedor-detail',
  templateUrl: './proveedor-detail.component.html',
  styleUrls: ['./proveedor-detail.component.css']
})
export class ProveedorDetailComponent implements OnInit {

  proveedor: any = {
    escenarioId: null,
    nombre: "",
    variacionCostoVariable: 0,
    variacionCalidad: 0,
    modalidadPago: [{
      porcentaje: 0,
      offsetPeriodo: 0
    }, {
      porcentaje: 0,
      offsetPeriodo: 1
    }, {
      porcentaje: 0,
      offsetPeriodo: 2
    }, {
      porcentaje: 0,
      offsetPeriodo: 3
    }]
  };

  nombre = new FormControl('', [Validators.required]);
  variacionCostoVariable = new FormControl('', [Validators.required]);
  variacionCalidad = new FormControl('', [Validators.required]);
  proveedorForm: FormGroup = new FormGroup({
    nombre: this.nombre,
    variacionCostoVariable: this.variacionCostoVariable,
    variacionCalidad: this.variacionCalidad
  });

  constructor(private proveedoresService: ProveedoresService,
    private route: ActivatedRoute,
    private router: Router,
    private messageService: MessagesService,
    private escenariosService: EscenariosService) { }

  ngOnInit() {
    var esenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    var id = Number(this.route.snapshot.paramMap.get('id'));
    if (id) {
      this.escenariosService.getProveedor(esenarioId, id).subscribe(p => {
        p.modalidadPago = this.proveedor.modalidadPago.map(mpt => {
          return { ...mpt, ...p.modalidadPago.find(mp => mp.offsetPeriodo === mpt.offsetPeriodo) };
        });
        this.proveedor = p;
        this.proveedor.escenarioId = esenarioId;
      });
    } else {
      this.proveedor.escenarioId = esenarioId;
    }
  }

  save() {
    if (this.proveedorForm.valid) {
      if (this.inputsValidos()) {
        this.proveedoresService[this.proveedor.id ? 'modifyProveedor' : 'createProveedor'](this.proveedor)
          .subscribe(_ => {
            this.messageService.openSnackBar("Proveedor modificado");
            this.router.navigate([`/escenarios/${this.proveedor.escenarioId}`])
          })
      }
    } else {
      this.messageService.openSnackBarDatosIngresados()
    }
  }

  inputsValidos() {
    //Validar totales de modalidad de pago
    var modalidadPagoPorcentajeTotal = this.proveedor.modalidadPago.filter(elem => elem.porcentaje > 0)
      .reduce((acum, elem) => acum + elem.porcentaje, 0)
    if (modalidadPagoPorcentajeTotal !== 100) {
      this.messageService.openSnackBar("Los porcentajes en la modalidad de pago deben sumar 100%!");
      return false;
    }
    return true;
  }

}
<file_sep>/src/app/decision-detalle/decision-detalle.component.ts
import { Component, OnInit } from '@angular/core';
import { Validators, FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { MatDialog } from '@angular/material';
import { OpcionDialogComponent } from './opciones/opcion-dialog.component';
import { DecisionesService } from '../decisiones.service';
import { ConsecuenciaDialogComponent } from './consecuencias/consecuencia-dialog.component';
import { MessagesService } from '../messages.service';
import { EscenariosService } from '../escenarios.service';
import { ProyectoService } from '../proyecto.service';

@Component({
  selector: 'app-decision-detalle',
  templateUrl: './decision-detalle.component.html',
  styleUrls: ['./decision-detalle.component.css'],
  animations: [
    trigger('detailExpand', [
      state('collapsed', style({ height: '0px', minHeight: '0' })),
      state('expanded', style({ height: '*' })),
      transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
    ]),
  ]
})
export class DecisionDetalleComponent implements OnInit {
  tipoCuentas;
  decision: any = {
    escenarioId: null,
    descripcion: "",
    opciones: []
  };

  descripcion = new FormControl('', [Validators.required]);
  decisionForm: FormGroup = new FormGroup({
    descripcion: this.descripcion
  });

  displayedColumns: string[] = ['descripcion', 'edit'];

  constructor(private decisionesService: DecisionesService,
    private route: ActivatedRoute,
    private dialog: MatDialog,
    private router: Router,
    private messageService: MessagesService,
    private escenariosService: EscenariosService,
    private proyectoService: ProyectoService) { }

  ngOnInit() {
    var esenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    var id = Number(this.route.snapshot.paramMap.get('id'));
    if (id) {
      this.escenariosService.getDecision(esenarioId, id).subscribe(d => {
        this.decision = d;
        this.decision.escenarioId = esenarioId;
      });
    } else {
      this.decision.escenarioId = esenarioId;
    }
    this.getTipoCuentas();
  }

  getTipoCuentas() {
    this.proyectoService.getTipoCuentas().subscribe(tipoCuentas => this.tipoCuentas = tipoCuentas)
  }

  tipoCuentaDescripcion(tipoCuenta) {
    if (this.tipoCuentas) {
      const tipoCuentaObject = this.tipoCuentas.find(tc => tc.key === tipoCuenta)
      return tipoCuentaObject && tipoCuentaObject.descripcion;
    } 
  }

  addOpcion() {
    const dialogRef = this.dialog.open(OpcionDialogComponent, {
      width: '400px',
      data: {
        decisionId: this.decision.id,
        descripcion: "",
        variacionCostoFijo: 0,
        variacionCostoVariable: 0,
        variacionProduccion: 0,
        variacionCalidad: 0,
        variacionPublicidad: 0,
        variacionCantidadVendedores: 0,
        consecuencias: []
      }
    });
    var that = this;
    dialogRef.afterClosed().subscribe(updatedOpcion => {
      if (updatedOpcion) {
        that.decision.opciones.push(updatedOpcion);
        that.refreshDecisiones();
      }
    });
  }

  editOpcion(opcion) {
    const dialogRef = this.dialog.open(OpcionDialogComponent, {
      width: '400px',
      data: opcion
    });
    var that = this;
    dialogRef.afterClosed().subscribe(updatedOpcion => {
      if (updatedOpcion) {
        var index = that.decision.opciones.indexOf(opcion);
        if (index !== -1) {
          that.decision.opciones[index] = updatedOpcion;
        }
        that.refreshDecisiones();
      }
    });
  }

  removeOpcion(opcion) {
    var index = this.decision.opciones.indexOf(opcion);
    this.decision.opciones.splice(index, 1)
    this.refreshDecisiones();
  }

  addConsecuencia(opcion) {
    const dialogRef = this.dialog.open(ConsecuenciaDialogComponent, {
      width: '400px',
      data: {
        opcionId: opcion.id,
        descripcion: "",
        monto: null,
        periodoInicio: 0,
        cantidadPeriodos: 1,
        tipoCuenta: "ECONOMICO"
      }
    });
    dialogRef.afterClosed().subscribe(updatedConsecuencia => {
      if (updatedConsecuencia) {
        opcion.consecuencias.push(updatedConsecuencia);
        this.refreshConsecuencias(opcion);
      }
    });
  }

  editConsecuencia(opcion, consecuencia) {
    const dialogRef = this.dialog.open(ConsecuenciaDialogComponent, {
      width: '400px',
      data: consecuencia
    });

    dialogRef.afterClosed().subscribe(updatedConsecuencia => {
      if (updatedConsecuencia) {
        var index = opcion.consecuencias.indexOf(consecuencia);
        if (index !== -1) {
          opcion.consecuencias[index] = updatedConsecuencia;
        }
        this.refreshConsecuencias(opcion);
      }
    });
  }

  removeConsecuencia(opcion, consecuencia) {
    var index = opcion.consecuencias.indexOf(consecuencia);
    opcion.consecuencias.splice(index, 1);
    this.refreshConsecuencias(opcion);
  }

  refreshDecisiones() {
    this.decision.opciones = this.decision.opciones.slice(0);
  }

  refreshConsecuencias(opcion) {
    opcion.consecuencias = opcion.consecuencias.slice(0);
  }

  save() {
    if (this.decisionForm.valid) {
      this.decisionesService[this.decision.id ? 'modifyDecision' : 'createDecision'](this.decision)
        .subscribe(_ => {
          this.messageService.openSnackBar("Decision modificada");
          this.router.navigate([`/escenarios/${this.decision.escenarioId}`])
        })
    } else {
      this.messageService.openSnackBarDatosIngresados()
    }
  }

}
<file_sep>/src/app/escenarios.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { environment } from '../environments/environment';
import { catchError, map } from 'rxjs/operators';
import { MessagesService } from './messages.service';

@Injectable({
  providedIn: 'root'
})
export class EscenariosService {
  getCursosPorEscenario(id: any) {
    throw new Error("Method not implemented.");
  }

  constructor(private http: HttpClient, private messageService: MessagesService) { }

  getEscenariosParaUsuario(idUsuario) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenarios/usuario/${idUsuario}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getEscenarios(): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenarios`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getEscenario(id): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenarios/${id}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  modifyEscenario(escenario) {
    return this.http.put(`${environment.proyectoServiceHost}/api/escenarios/${escenario.id}`, escenario)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  duplicateEscenario(escenario) {
    return this.http.post(`${environment.proyectoServiceHost}/api/escenarios/${escenario.id}`, escenario)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  createEscenario(escenario) {
    delete escenario.id;
    return this.http.post(`${environment.proyectoServiceHost}/api/escenarios`, escenario)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  deleteEscenario(id): Observable<any> {
    return this.http.delete(`${environment.proyectoServiceHost}/api/escenarios/${id}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getDecisiones(idEscenario): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${idEscenario}/decisiones`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getProveedores(idEscenario): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${idEscenario}/proveedores`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getDecision(idEscenario, idDecision) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${idEscenario}/decisiones`)
      .pipe(map((decisiones: Array<any>) => {
        return decisiones.find(d => d.id === idDecision);
      }));
  }

  getProveedor(idEscenario, idProveedor) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${idEscenario}/proveedores`)
      .pipe(map((proveedores: Array<any>) => {
        return proveedores.find(p => p.id === idProveedor);
      }));
  }

  getFinanciaciones(idEscenario): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${idEscenario}/financiacion`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getCursosEscenario(idEscenario) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenarios/${idEscenario}/cursos`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  updateCursosEscenario(idEscenario, cursos) {
    return this.http.put(`${environment.proyectoServiceHost}/api/escenarios/${idEscenario}/cursos`, cursos)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getDetalleEscenarioUsuariosPorCurso(idEscenario, idCurso) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenarios/${idEscenario}/cursos/${idCurso}`)
      .pipe(map( (escenarioCurso:any) => {
        escenarioCurso.jugadores.sort((a,b) => Number(a.puntaje) < Number(b.puntaje) ? 1 : -1 )
        return escenarioCurso;
      }),catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getConfiguracionMercado(escenarioId) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${escenarioId}/configuracionMercado`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)),
        map(configuracionesMercado => {
          var ponderacionesMercado = {
            precioDesde: [],
            modalidadCobro: [],
            publicidadDesde: [],
            calidadDesde: [],
            vendedoresDesde: []
          }
          configuracionesMercado.ponderacionesMercado.forEach(pm => {
            ponderacionesMercado[{
              "PRECIO_DESDE": "precioDesde",
              "MODALIDAD_DE_COBRO": "modalidadCobro",
              "PUBLICIDAD_DESDE": "publicidadDesde",
              "CALIDAD_DESDE": "calidadDesde",
              "VENDEDORES_DESDE": "vendedoresDesde",
            }[pm.concepto]].push(pm)
          })
          configuracionesMercado.ponderacionesMercado = ponderacionesMercado;
          return configuracionesMercado;
        }));
  }

  postConfiguracionesMercado(configuracionesMercado) {
    const escenarioId = configuracionesMercado.restriccionPrecio.escenarioId,
      configuracionesMercadoForPost = JSON.parse(JSON.stringify(configuracionesMercado));
    configuracionesMercadoForPost.ponderacionesMercado = [
      ...configuracionesMercadoForPost.ponderacionesMercado.precioDesde,
      ...configuracionesMercadoForPost.ponderacionesMercado.modalidadCobro,
      ...configuracionesMercadoForPost.ponderacionesMercado.publicidadDesde,
      ...configuracionesMercadoForPost.ponderacionesMercado.calidadDesde,
      ...configuracionesMercadoForPost.ponderacionesMercado.vendedoresDesde
    ]
    return this.http.post(`${environment.proyectoServiceHost}/api/escenario/${escenarioId}/configuracionMercado`, configuracionesMercadoForPost)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  simularMercado(escenarioId, cursoId) {
    return this.http.post(`${environment.proyectoServiceHost}/api/escenario/${escenarioId}/curso/${cursoId}/simular-mercado`, {})
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getPuntajes(escenarioId) {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${escenarioId}/puntajeEscenario`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  postPuntajes(escenarioId, puntajes) {
    puntajes.escenarioId = escenarioId;
    return this.http.post(`${environment.proyectoServiceHost}/api/escenario/${escenarioId}/puntajeEscenario`, puntajes)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getProyectosByEscenario(escenarioId) {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyectosPorEscenario/${escenarioId}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

}
<file_sep>/src/app/messages.service.ts
import { Injectable, Component } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { throwError } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
 
export class MessagesService {


  constructor(private _snackBar: MatSnackBar) { }

  openSnackBar(message) {
    this._snackBar.open(message, "OK", {
      duration: 2000, 
    });
  }

  catchError(err) {
    this.openSnackBar(err.error? err.error.message : "Error desconocido");
    return throwError(err);
  }

  openSnackBarDatosIngresados(){
    this._snackBar.open("Revise los datos ingresados", "OK", {
      duration: 2000, 
    });
  }




}
<file_sep>/src/app/cursos/curso-dialog/curso-dialog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormControl, Validators, FormGroup } from '@angular/forms';

import { MessagesService } from '../../messages.service';
import { Router } from '@angular/router';
import { CursosService } from '../../cursos.service';

@Component({
  selector: 'curso-dialog',
  templateUrl: './curso-dialog.component.html',
  styleUrls: ['./curso-dialog.component.css']
})
export class CursoDialogComponent implements OnInit {
  curso;
  nombre = new FormControl('', [Validators.required])
  clave = new FormControl('', [Validators.required])
  cursoForm: FormGroup = new FormGroup({
    nombre: this.nombre,
    clave: this.clave
  });

  constructor( public dialogRef: MatDialogRef<CursoDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data, private cursosService: CursosService, 
    private messageService: MessagesService) { }

  ngOnInit() {
      this.curso = JSON.parse(JSON.stringify(this.data));
  }

  cancel(){
      this.dialogRef.close();
  }

  save() {
    if (this.cursoForm.valid) {
      let cursoSend = {id: this.curso.id, nombre: this.curso.nombre, clave: btoa(this.curso.clave)};
      this.cursosService[this.curso.id ? 'modifyCurso' : 'createCurso'](cursoSend)
        .subscribe(_ => {
          this.messageService.openSnackBar("Curso modificado");
          this.dialogRef.close();
        })
    }else {
      this.messageService.openSnackBarDatosIngresados()
    }
  }

}
<file_sep>/src/app/usuario.service.ts
import { Injectable, Component } from '@angular/core';
import { environment } from '../environments/environment';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map, catchError, tap } from 'rxjs/operators';
import { MessagesService } from './messages.service';

@Injectable()
export class UsuarioService {

  private _usuario;
 
  constructor(private http: HttpClient,
    private messageService: MessagesService) { }

  get usuario() {
    return this._usuario;
  }

  getUsuario(usuario): Observable<any> {
    return this.http.post(`${environment.proyectoServiceHost}/api/usuario`,usuario)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)),
            tap( usuario => this._usuario = usuario ));
  }

  getProyecto(idEscenario, idUsuario): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/escenario/${idEscenario}/usuario/${idUsuario}/proyecto`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  matricular(usuario,curso){
    return this.http.post(`${environment.proyectoServiceHost}/api/usuario/${usuario.id}/matricular`, curso)
    .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

}

<file_sep>/src/app/presupuesto-economico/presupuesto-economico.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ProyectoService } from '../proyecto.service';
import { ActivatedRoute } from '@angular/router';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'app-presupuesto-economico',
  templateUrl: './presupuesto-economico.component.html',
  styleUrls: ['./presupuesto-economico.component.css']
})
export class PresupuestoEconomicoComponent implements OnInit {
  @Input() proyecto: any;
  @Input() forecast: boolean;
  presupuestoEconomico;
  periodos;
  escenario;

  constructor(private proyectoService: ProyectoService) { }

  ngOnInit() {
    this.getPeriodos(this.proyecto.id);
    this.getPresupuestoEconomico(this.proyecto.id);
    this.escenario = this.proyecto.escenario;
  }

  getPresupuestoEconomico(proyectoId) {
    this.proyectoService.getPresupuestoEconomico(proyectoId,this.forecast).subscribe(pe => {
      this.presupuestoEconomico = pe;
    })
  }

  getPeriodos(proyectoId) {
    this.proyectoService.getPeriodoActual(proyectoId, this.forecast).subscribe(periodoActual => {
      this.periodos = [...Array(periodoActual).keys(), periodoActual];
    })
  }

  getCuentaPeriodo(cuentasPeriodo, periodo, defaultCero = true, signoInvertido = false) {
    var periodo = cuentasPeriodo && cuentasPeriodo.find(cuentaPeriodo => cuentaPeriodo.periodo === periodo)
    var montoCrudo = periodo ? periodo.monto : (defaultCero ? 0 : "");
    montoCrudo = signoInvertido ? -montoCrudo : montoCrudo;
    if (montoCrudo < 0) {
      return "(" + Math.abs(montoCrudo).toLocaleString() + ")";
    } else if (montoCrudo === 0) {
      return montoCrudo;
    } else {
      return montoCrudo.toLocaleString();
    }
  }

  getTotalCuentaPeriodo(cuentasPeriodo, signoInvertido = false) {
    var acumulador = 0;
    if (cuentasPeriodo) {
      cuentasPeriodo.map(cp => acumulador = acumulador + cp.monto);
      acumulador = signoInvertido ? -acumulador : acumulador;
      if (acumulador < 0) {
        return "(" + Math.abs(acumulador).toLocaleString() + ")";
      } else if (acumulador === 0) {
        return acumulador;
      } else {
        return acumulador.toLocaleString();
      }
    }
  }

}
<file_sep>/src/app/financiacion.service.ts
import { Injectable } from '@angular/core';
import { map, switchMap } from 'rxjs/operators';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { environment } from '../environments/environment';
import { catchError } from 'rxjs/operators';
import { MessagesService } from './messages.service';
@Injectable({
  providedIn: 'root'
})
export class FinanciacionService {

  constructor(private http: HttpClient, private messageService: MessagesService) { }

  removeFinanciacion(financiacion) {
    return this.http.delete(`${environment.proyectoServiceHost}/api/financiacion/${financiacion.id}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  createFinanciacion(financiacion) {
    return this.http.post(`${environment.proyectoServiceHost}/api/financiacion`, financiacion)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  modifyFinanciacion(financiacion) {
    return this.http.put(`${environment.proyectoServiceHost}/api/financiacion/${financiacion.id}`, financiacion)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

}
<file_sep>/src/app/resultados/resultados.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { ActivatedRoute, Router } from '@angular/router';
import { UsuarioService } from '../usuario.service';
import { ProyectoService } from '../proyecto.service';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material';

@Component({
  selector: 'app-resultados',
  templateUrl: './resultados.component.html',
  styleUrls: ['./resultados.component.css']
})
export class ResultadosComponent implements OnInit {
  proyecto;
  escenario;
  decisiones;
  escenarioCurso;
  forecast;
  seccion = "RESULTADOS_FORECAST";

  constructor(private escenarioService: EscenariosService, private route: ActivatedRoute,
    private usuarioService: UsuarioService, private proyectoService: ProyectoService, public dialog: MatDialog, private router: Router) { }

  ngOnInit() {
    var escenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    this.getEscenario(escenarioId);
    this.getProyecto(escenarioId, this.usuarioService.usuario.id);
    this.getDetalleEscenarioUsuariosPorCurso(escenarioId, this.usuarioService.usuario.curso.id);
  }

  getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId) {
    this.escenarioService.getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId).subscribe(escenarioCurso => {
      this.escenarioCurso = escenarioCurso;
    })
  }

  getProyecto(escenarioId, usuarioId) {
    this.usuarioService.getProyecto(escenarioId, usuarioId).subscribe(estado => {
      this.proyecto = estado.proyecto;
      this.getDecisiones(this.proyecto.id);
      this.getForecast(this.proyecto.id);
    })
  }

  getDecisiones(proyectoId) {
    this.proyectoService.getDecisiones(proyectoId)
      .subscribe(decisiones => this.decisiones = decisiones);
  }

  getEscenario(escenarioId) {
    this.escenarioService.getEscenario(escenarioId).subscribe(escenario => this.escenario = escenario);
  }

  getForecast(proyectoId) {
    this.proyectoService.getForecast(proyectoId).subscribe(forecast => {this.forecast = forecast;
    if(forecast.length <= 0) this.seccion = "RESULTADOS_REALES"});
  }

  entregarSimulacion() {
    const dialogRef = this.dialog.open(EntregarConfirmationPopup, {
      width: window.screen.width > 768 ? '50%' : '95%'
    });

    dialogRef.afterClosed().subscribe(respuesta => {
      if (respuesta === "ENTREGAR") {
        this.proyectoService.entregarProyecto(this.proyecto.id)
          .subscribe( _ =>  this.router.navigate(['/simulacion-entregada']) ); 
      }
    });
  }
}

@Component({
  selector: 'entregar-confirmation-popup',
  templateUrl: 'entregar-confirmation-popup.html',
})
export class EntregarConfirmationPopup {

  constructor(
    public dialogRef: MatDialogRef<EntregarConfirmationPopup>) { }

  onEntregarClick(): void {
    this.dialogRef.close('ENTREGAR');
  }

  onCancelClick(): void {
    this.dialogRef.close();
  }

}
<file_sep>/src/app/presupuesto-financiero/presupuesto-financiero.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ProyectoService } from '../proyecto.service';
import { ActivatedRoute } from '@angular/router';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'app-presupuesto-financiero',
  templateUrl: './presupuesto-financiero.component.html',
  styleUrls: ['./presupuesto-financiero.component.css']
})
export class PresupuestoFinancieroComponent implements OnInit {
  @Input() proyecto: any;
  @Input() forecast: boolean;
  presupuestoFinanciero;
  periodos;
  escenario;

  constructor(private proyectoService: ProyectoService) { }

  ngOnInit() {
    this.getPeriodos(this.proyecto.id);
    this.getPresupuestoFinanciero(this.proyecto.id);
    this.escenario = this.proyecto.escenario;
  }

  getPresupuestoFinanciero(proyectoId) {
    this.proyectoService.getPresupuestoFinanciero(proyectoId,this.forecast).subscribe(pf => {
      this.presupuestoFinanciero = pf;
    })
  }

  getPeriodos(proyectoId) {
    this.proyectoService.getPeriodoActual(proyectoId, this.forecast).subscribe(periodoActual => {
      this.periodos = [...Array(periodoActual).keys(), periodoActual];
    })
  }

  getCuentaPeriodo(cuentasPeriodo, periodo, defaultCero = true, signoInvertido = false) {
    var periodo = cuentasPeriodo && cuentasPeriodo.find(cuentaPeriodo => cuentaPeriodo.periodo === periodo)
    var montoCrudo = periodo ? periodo.monto : (defaultCero ? 0 : "");
    montoCrudo = signoInvertido ? -montoCrudo : montoCrudo;
    if (montoCrudo < 0) {
      return "(" + Math.abs(montoCrudo).toLocaleString() + ")";
    } else if (montoCrudo === 0) {
      return montoCrudo;
    } else {
      return montoCrudo.toLocaleString();
    }
  }

  getTotalAcumulado(cuentasPeriodo){
    var totalAcumulado = cuentasPeriodo.reduce((total,cp) => total + cp.monto,0 );
    if (totalAcumulado < 0) {
      return "(" + Math.abs(totalAcumulado).toLocaleString() + ")";
    } else if (totalAcumulado === 0) {
      return totalAcumulado;
    } else {
      return totalAcumulado.toLocaleString();
    }
  }

}
<file_sep>/src/app/estado/estado.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ProyectoService } from "../proyecto.service";
import { Observable } from 'rxjs/Observable';
import * as Chart from 'chart.js';
import { UsuarioService } from '../usuario.service';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-estado',
  templateUrl: './estado.component.html',
  styleUrls: ['./estado.component.css']
})
export class EstadoComponent implements OnInit {
  @Input() proyecto: any;
  @Input() forecast: boolean;
  estadoActual: any;
  ultimoPrecioProducto;
  estados: any;
  cajaChartProps;
  ventasChartProps;
  ventasVsStockChartProps;
  escenario;

  constructor(private proyectoService: ProyectoService,
    private route: ActivatedRoute,
    private usuarioService: UsuarioService) {
  }

  ngOnInit() {
    this.getEstadoActual(this.proyecto.id);
    this.getUltimoPrecioProducto(this.proyecto.id);
  }

  getUltimoPrecioProducto(proyectoId) {
    if (this.forecast) {
      this.proyectoService.getForecast(proyectoId).subscribe(forecasts => {
        this.ultimoPrecioProducto = forecasts.pop().precio;
      })
    } else {
      this.ultimoPrecioProducto = 0;
    }
  }

  getPeriodoQuiebreCaja(estados) {
    let estadoQuiebreCaja = estados.find(estado => estado.caja < 0);
    return estados.indexOf(estadoQuiebreCaja);
  }

  getEstadoActual(proyectoId) {
    this.estadoActual = this.proyectoService.getEstado(proyectoId, this.forecast).subscribe(estadoActual => {
      this.estadoActual = estadoActual;
      this.escenario = estadoActual.proyecto.escenario;
      this.getEstados(proyectoId);
    })
  }

  getEstados(proyectoId) {
    this.proyectoService.getEstados(proyectoId, this.forecast).subscribe(estados => {
      this.estados = estados;
      this.setCajaChartProps(this.estados);
      this.setVentasChartProps(this.estados);
      this.proyectoService.getForecast(proyectoId).subscribe(forecasts => {
        this.setStockVsVentasChartProps(this.estados, forecasts);
      })

    })
  }

  setCajaChartProps(estados) {
    this.cajaChartProps = {
      options: {
        scaleShowVerticalLines: false,
        responsive: true
      },
      labels: estados.map(estado => `${this.escenario.nombrePeriodos} ${estado.periodo}`),
      type: 'line',
      legend: false,
      data: [
        { data: estados.map(estado => estado.caja), label: 'Caja', fill: false }
      ]
    }
  }

  setVentasChartProps(estados) {
    this.ventasChartProps = {
      options: {
        scaleShowVerticalLines: false,
        responsive: true
      },
      labels: estados.filter((_, index) => index !== 0).map(estado => `${this.escenario.nombrePeriodos} ${estado.periodo}`),
      type: 'line',
      legend: true,
      data: [
        {
          data: estados.filter((_, index) => index !== 0).map(estado => estado.ventas),
          fill: false,
          label: 'Ventas'
        },
        {
          data: estados.filter((_, index) => index !== 0).map(estado => estado.demandaPotencial),
          label: 'Demanda obtenida',
          fill: false,
          backgroundColor: 'rgb(75, 192, 192)',
          borderColor: 'rgb(75, 192, 192)',
          borderDash: [5, 5]
        }
      ]
    }
  }

  setStockVsVentasChartProps(estados, forecasts) {
    var forecastForVentas = forecasts.filter((_, index) => index !== 0);
    this.ventasVsStockChartProps = {
      options: {
        scaleShowVerticalLines: false,
        responsive: true,
        aspectRatio: 1.5
      },
      labels: estados.filter((_, index) => index !== 0).map(e => e.proyecto.escenario.nombrePeriodos + " " + e.periodo),
      type: 'bar',
      legend: true,
      data: [
        {
          data: estados.filter((_, index) => index !== 0).map((e, index) => {
            var precio = forecastForVentas[index].precio;
            if (precio === 0) {
              return 0;
            } else {
              return e.ventas / precio;
            }
          }),
          type: 'line',
          fill: false,
          label: 'Cantidad Vendida'
        },
        {
          data: estados.filter((_, index) => index !== 0).map(e => e.stock),
          type: 'bar',
          label: 'Stock',
          fill: false
        }
      ]
    }

  }

  getEvenNumber = function (num) {
    return Math.floor(Number(num) / 2) > 0 ? new Array(Math.floor(Number(num) / 2)) : new Array(0);
  }

  getOddNumber = function (num) {
    return Number(num) % 2 != 0;
  }

}
<file_sep>/src/app/estado-juegos/estado-juegos.component.ts
import { Component, OnInit } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { CursosService } from '../cursos.service';
import { zip, of } from "rxjs";
import { switchMap, map, flatMap, combineAll } from 'rxjs/operators';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'app-estado-juegos',
  templateUrl: './estado-juegos.component.html',
  styleUrls: ['./estado-juegos.component.css']
})
export class EstadoJuegosComponent implements OnInit {
  cursosTree;
  escenarios: Array<any>;
  usuario;

  constructor(private escenariosService: EscenariosService,
    private cursosService: CursosService,
    private usuarioService: UsuarioService) { }

  ngOnInit() {
    this.getCursosPorEscenario();
    this.usuario = this.usuarioService.usuario;
  }

  getCursosPorEscenario() {
    const $cursos = this.cursosService.getCursos();
    const $escenarios = this.escenariosService.getEscenarios();

    const $escenariosConCursos = $escenarios.pipe(
      switchMap(escenarios => {
        const $escenarios = escenarios.map(escenario => {
          return this.escenariosService.getCursosEscenario(escenario.id).pipe(
            map(cursosPorEscenario => {
              escenario.cursos = cursosPorEscenario
              return escenario;
            })
          )
        })
        return zip(...$escenarios);
      })
    );

    zip($cursos, $escenariosConCursos).subscribe(([cursos, escenariosConCursos]) => {
      var cursosTree = cursos.map(curso => {
        curso.escenarios = escenariosConCursos.filter((escenario: any) => escenario.cursos.find(c => c.nombre === curso.nombre));
        curso.escenarios = JSON.parse(JSON.stringify(curso.escenarios));//Debo copiar la lista como un nuevo objeto para que existan varias referencias
        return curso;
      });
      cursosTree.forEach(curso => {
        curso.escenarios.forEach(escenario => {
          this.escenariosService.getDetalleEscenarioUsuariosPorCurso(escenario.id, curso.id)
            .subscribe(escenarioCurso => {
                escenario.escenarioCurso = escenarioCurso
            })
        })
      })
      this.cursosTree = cursosTree;
    })

  }


}
<file_sep>/src/app/proveedores.service.ts
import { Injectable } from '@angular/core';
import { EscenariosService } from './escenarios.service';
import { map, switchMap } from 'rxjs/operators';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { environment } from '../environments/environment';
import { catchError } from 'rxjs/operators';
import { MessagesService } from './messages.service';

@Injectable({
  providedIn: 'root'
})
export class ProveedoresService {

  constructor(private http: HttpClient, private messageService: MessagesService, private escenarioService: EscenariosService) { }

  _getProveedores(idEscenario) {
    return this.escenarioService.getProveedores(idEscenario);
  }

  removeProveedor(proveedor) {
    return this.http.delete(`${environment.proyectoServiceHost}/api/proveedores/${proveedor.id}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  createProveedor(proveedor) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proveedor`, proveedor)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  modifyProveedor(proveedor) {
    return this.http.put(`${environment.proyectoServiceHost}/api/proveedores/${proveedor.id}`, proveedor)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }


}
<file_sep>/src/app/estado-juegos/estado-juegos.component.spec.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { EstadoJuegosComponent } from './estado-juegos.component';

describe('EstadoJuegosComponent', () => {
  let component: EstadoJuegosComponent;
  let fixture: ComponentFixture<EstadoJuegosComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ EstadoJuegosComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(EstadoJuegosComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
<file_sep>/src/app/escenarios/escenarios.component.ts
import { Component, OnInit } from '@angular/core';
import { ProyectoService } from '../proyecto.service';
import { EscenariosService } from '../escenarios.service';
import { UsuarioService } from '../usuario.service';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { MatDialog } from '@angular/material';
import { switchMap, map } from 'rxjs/operators';
import { zip } from 'rxjs';

@Component({
  selector: 'app-escenarios',
  templateUrl: './escenarios.component.html',
  styleUrls: ['./escenarios.component.css']
})
export class EscenariosComponent implements OnInit {
  usuario;
  escenarios: Array<any>;

  constructor(private escenariosService: EscenariosService, private usuarioService: UsuarioService,
    private dialog: MatDialog) { }

  ngOnInit() {
    this.usuario = this.usuarioService.usuario;
    this.getEscenarios();
  }

  getEscenarios() {
    this.escenariosService.getEscenarios().pipe(
      switchMap(escenarios => {
        const $escenarios = escenarios.map(e => {
          return this.escenariosService.getProyectosByEscenario(e.id).pipe(
            map(proyectos => {
              e.hasProyectos = (proyectos.length > 0)
              return e;
            })
          )
        })
        return zip(...$escenarios)
      })
    ).subscribe(escenarios => this.escenarios = escenarios)
  }


  deleteEscenario(id) {
    const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
      width: '500px',
      data: {
        message: "¿Seguro que desea eliminar el escenario?"
      }
    });
    dialogRef.afterClosed().subscribe(response => {
      if (response === "OK") {
        this.escenariosService.deleteEscenario(id).subscribe(_ => this.getEscenarios())
      }
    });
  }

  duplicateEscenario(escenario) {
    const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
      width: '500px',
      data: {
        message: "¿Seguro que desea clonar el escenario?"
      }
    });
    dialogRef.afterClosed().subscribe(response => {
      if (response === "OK") {
        this.escenariosService.duplicateEscenario(escenario).subscribe(_ => this.getEscenarios())
      }
    });
  }

}
<file_sep>/src/app/premios/premios.component.ts
import { Component, OnInit } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { UsuarioService } from '../usuario.service';
import { switchMap, map, filter } from 'rxjs/operators';
import { zip } from 'rxjs';

@Component({
  selector: 'app-premios',
  templateUrl: './premios.component.html',
  styleUrls: ['./premios.component.css']
})
export class PremiosComponent implements OnInit {

  usuario;
  escenarios;

  constructor(private escenariosService: EscenariosService, private usuarioService: UsuarioService) { }

  ngOnInit() {
    this.usuario = this.usuarioService.usuario;
    this.getEscenarios(this.usuario.id, this.usuario.curso.id);
  }

  getEscenarios(idUsuario, cursoId) {
    const $escenarios = this.escenariosService.getEscenariosParaUsuario(idUsuario);

    const $escenariosConProyectos = $escenarios.pipe(
      switchMap(escenarios => {
        const $escenarios = escenarios.map(escenario => {
          var $proyecto = this.usuarioService.getProyecto(escenario.id, idUsuario);
          var $escenarioCurso = this.escenariosService.getDetalleEscenarioUsuariosPorCurso(escenario.id, cursoId);
          return zip($proyecto, $escenarioCurso).pipe(
            map(([proyecto, escenarioCurso]) => {
              escenario.proyecto = proyecto;
              escenario.escenarioCurso = escenarioCurso;
              escenario.podio = this._jugadoresToPodio(escenarioCurso.jugadores);
              return escenario;
            })
          );
        })
        return zip(...$escenarios);
      }));

    $escenariosConProyectos.subscribe(escenarios => this.escenarios = escenarios);
  }

  _jugadoresToPodio(jugadores) {
    return {
      primerPuesto: jugadores[0],
      segundoPuesto: jugadores[1],
      tercerPuesto: jugadores[2]
    }
  }

}
<file_sep>/src/app/decision-detalle/opciones/opcion-dialog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormControl, Validators, FormGroup } from '@angular/forms';
import { MessagesService } from '../../messages.service';

@Component({
  selector: 'opcion-dialog',
  templateUrl: './opcion-dialog.component.html',
  styleUrls: ['./opcion-dialog.component.css']
})
export class OpcionDialogComponent implements OnInit {
  opcion;
  descripcion = new FormControl('', [Validators.required])
  variacionCostoFijo = new FormControl('', [Validators.required])
  variacionCostoVariable = new FormControl('', [Validators.required])
  variacionProduccion = new FormControl('', [Validators.required])
  variacionCalidad = new FormControl('', [Validators.required])
  variacionPublicidad = new FormControl('', [Validators.required])
  variacionCantidadVendedores = new FormControl('', [Validators.required])
  opcionForm: FormGroup = new FormGroup({
    descripcion: this.descripcion,
    variacionCostoFijo: this.variacionCostoFijo,
    variacionCostoVariable: this.variacionCostoVariable,
    variacionProduccion: this.variacionProduccion,
    variacionCalidad: this.variacionCalidad,
    variacionPublicidad: this.variacionPublicidad,
    variacionCantidadVendedores: this.variacionCantidadVendedores
  });

  constructor( public dialogRef: MatDialogRef<OpcionDialogComponent>,
    private messageService: MessagesService,
    @Inject(MAT_DIALOG_DATA) public data) { }

  ngOnInit() {
      this.opcion = JSON.parse(JSON.stringify(this.data));
  }

  cancel(){
      this.dialogRef.close();
  }

  save(){
    if(this.opcionForm.valid){
      this.dialogRef.close(this.opcion);
    }else {
      this.messageService.openSnackBarDatosIngresados()
    }
  }

}
<file_sep>/src/app/estado-juego-detail/estado-juego-detail.component.spec.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { EstadoJuegoDetailComponent } from './estado-juego-detail.component';

describe('EstadoJuegoDetailComponent', () => {
  let component: EstadoJuegoDetailComponent;
  let fixture: ComponentFixture<EstadoJuegoDetailComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ EstadoJuegoDetailComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(EstadoJuegoDetailComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
<file_sep>/src/app/login/login.component.ts
import { Component, OnInit, HostBinding } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import * as firebase from 'firebase/app';
import { Router } from '@angular/router';
import { DomSanitizer } from '@angular/platform-browser';
import { MatIconRegistry, MatSnackBar } from '@angular/material';
import { MatDialog, DialogPosition } from '@angular/material';
import { RegisterComponent } from './components/register.component';
import { FormControl, Validators, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  hide = true;
  rememberMeChecked = true;
  persistance = firebase.auth.Auth.Persistence.LOCAL;
  loading = false;

  email = new FormControl('', [Validators.required, Validators.email])
  password = new FormControl('', [Validators.required])
  registerForm: FormGroup = new FormGroup({
    email: this.email,
    password: this.password,
  });


  constructor(private afAuth: AngularFireAuth,
    private router: Router,
    iconRegistry: MatIconRegistry,
    sanitizer: DomSanitizer,
    private dialog: MatDialog,
    public snackBar: MatSnackBar) {
    this.loading = true;
    this.afAuth.authState.subscribe(user => {
      this.loading = false;
      if (user) {
        this.router.navigateByUrl("/home");
      }
    });
    iconRegistry.addSvgIcon('facebook_icon', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/facebook-logo.svg'));
    iconRegistry.addSvgIcon('google_icon', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/google-plus.svg'));
  }

  ngOnInit() {
  }

  withGoogle() {
    this.afAuth.auth.setPersistence(this.persistance)
      .then(_ => this.afAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider())
        .catch(e => this.openSnackBar(e.message)))
      .catch(e => this.openSnackBar(e.message));
  }

  withFacebook() {
    this.afAuth.auth.setPersistence(this.persistance)
      .then(_ => this.afAuth.auth.signInWithRedirect(new firebase.auth.FacebookAuthProvider())
        .catch(e => this.openSnackBar(e.message)))
      .catch(e => this.openSnackBar(e.message));
  }

  withEmail() {
    if (this.registerForm.valid) {
      this.afAuth.auth.setPersistence(this.persistance)
        .then(_ => this.afAuth.auth.signInWithEmailAndPassword(this.email.value, this.password.value)
          .catch(e => this.openSnackBar(e.message)))
        .catch(e => this.openSnackBar(e.message));
    }
  }

  rememberMe() {
    if (this.rememberMeChecked) {
      this.persistance = firebase.auth.Auth.Persistence.LOCAL;
    } else {
      this.persistance = firebase.auth.Auth.Persistence.SESSION;
    }
  }

  openRegisterDialog() {
    const dialogRef = this.dialog.open(RegisterComponent, {
      height: '350px'
    });
    dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog result: ${result}`);
    });
  }

  openSnackBar(message: string, action: string = "OK") {
    this.snackBar.open(message, action, {
      duration: 2000,
      //extraClasses: ['error-snack-bar']
    });
  }

}
<file_sep>/src/app/financiaciones/financiacion-dialog/financiacion-dialog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormControl, Validators, FormGroup } from '@angular/forms';

import { MessagesService } from '../../messages.service';
import { Router } from '@angular/router';
import { FinanciacionService } from '../../financiacion.service';

@Component({
  selector: 'financiacion-dialog',
  templateUrl: './financiacion-dialog.component.html',
  styleUrls: ['./financiacion-dialog.component.css']
})
export class FinanciacionDialogComponent implements OnInit {
  financiacion;
  descripcion = new FormControl('', [Validators.required])
  tea = new FormControl('', [Validators.required, Validators.min(1), Validators.max(100)])
  cantidadCuotas = new FormControl('', [Validators.required])
  financiacionForm: FormGroup = new FormGroup({
    descripcion: this.descripcion,
    cantidadCuotas: this.cantidadCuotas,
    tea: this.tea
  });

  constructor( public dialogRef: MatDialogRef<FinanciacionDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data, private financiacionService: FinanciacionService, 
    private messageService: MessagesService, private router: Router) { }

  ngOnInit() {
      this.financiacion = JSON.parse(JSON.stringify(this.data));
  }

  cancel(){
      this.dialogRef.close();
  }

  save() {
    if (this.financiacionForm.valid) {
      this.financiacionService[this.financiacion.id ? 'modifyFinanciacion' : 'createFinanciacion'](this.financiacion)
        .subscribe(_ => {
          this.messageService.openSnackBar("Financiacion modificada");
          this.dialogRef.close();
        })
    }
  }

}
<file_sep>/src/app/valores-dominio.ts
export const descripcionesTipoCuentas = {
  "ECONOMICO": "Economico",
  "FINANCIERO": "Financiero"
};

export const descripcionesTipoFlujoFondos = {
  "INGRESOS_AFECTOS_A_IMPUESTOS": "Ingresos afectos a impuestos",
  "INGRESOS_NO_AFECTOS_A_IMPUESTOS": "Ingresos no afectos a impuestos",
  "GASTOS_NO_DESEMBOLSABLES": "Gastos no desembolsables",
  "EGRESOS_AFECTOS_A_IMPUESTOS": "Egresos afectos a impuestos",
  "EGRESOS_NO_AFECTOS_A_IMPUESTOS": "Egresos no afectos a impuestos",
  "UTILIDAD_ANTES_DE_IMPUESTOS": "Utilidad antes de impuestos",
  "IMPUESTOS": "Impuestos",
  "INVERSIONES": "Inversiones",
  "UTILIDAD_DESPUES_DE_IMPUESTOS": "Utilidad despues de impuestos",
  "AJUSTE_DE_GASTOS_NO_DESEMBOLSABLES": "Ajuste de gastos no desembolsables",
  "FLUJO_DE_FONDOS": "Flujo de fondos"
};




<file_sep>/src/app/balance-final/balance-final.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ProyectoService } from '../proyecto.service';

@Component({
  selector: 'app-balance-final',
  templateUrl: './balance-final.component.html',
  styleUrls: ['./balance-final.component.css']
})
export class BalanceFinalComponent implements OnInit {
  @Input() proyecto: any;
  @Input() forecast: boolean;
  balanceFinal;
  totalActivo = 0;
  totalPasivoPatrimonioNeto = 0;

  constructor(private proyectoService: ProyectoService) { }

  ngOnInit() {
    this.getBalanceFinal(this.proyecto.id);
  }

  getBalanceFinal(idProyecto){
    this.proyectoService.getBalanceFinal(idProyecto,this.forecast).subscribe(balanceFinal => {this.balanceFinal = balanceFinal; this.getTotalActivo(); this.getTotalPasivoPatrimonioNeto()});
  }

  getTotalActivo() {
    this.totalActivo = this.balanceFinal.activo.caja + this.balanceFinal.activo.cuentasPorCobrar + this.balanceFinal.activo.inventario + this.balanceFinal.activo.maquinaria + this.balanceFinal.activo.amortizacionAcumulada + this.balanceFinal.activo.otros;
  }

  getTotalPasivoPatrimonioNeto() {
    this.totalPasivoPatrimonioNeto = this.balanceFinal.pasivo.proveedores + this.balanceFinal.pasivo.deudasBancarias + this.balanceFinal.pasivo.otros + this.balanceFinal.patrimonioNeto.capitalSocial + this.balanceFinal.patrimonioNeto.resultadoDelEjercicio;
  }

}
<file_sep>/src/app/decisiones/decisiones.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { DecisionesService } from '../decisiones.service';
import { EscenariosService } from '../escenarios.service';


@Component({
  selector: 'app-decisiones',
  templateUrl: './decisiones.component.html',
  styleUrls: ['./decisiones.component.css']
})
export class DecisionesComponent implements OnInit {
  @Input() escenario: any;
  @Input() hasProyectos: boolean;
  decisiones: Array<any>;

  constructor(private decisionesService: DecisionesService, private escenariosService: EscenariosService) { }

  ngOnInit() {
    this.getDecisiones();
  }

  getDecisiones() {
   this.escenariosService.getDecisiones(this.escenario.id).subscribe( decisiones => {
      this.decisiones = decisiones;
   });
  }

  removeDecision(id){
      this.decisionesService.removeDecision(id).subscribe(_ => this.getDecisiones());
  }



}
<file_sep>/src/app/rankings/rankings.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { CursosService } from '../cursos.service';
import { EscenariosService } from '../escenarios.service';
import { MessagesService } from '../messages.service';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'app-rankings',
  templateUrl: './rankings.component.html',
  styleUrls: ['./rankings.component.css']
})
export class RankingsComponent implements OnInit {
  @Input() escenario: any;
  @Input() curso: any;

  ventasChartProps;
  cajaChartProps;
  rentaChartProps;
  escenarioCurso;
  puntajes;

  constructor(private route: ActivatedRoute, private cursosService: CursosService,
    private escenariosService: EscenariosService,
    private usuarioService: UsuarioService) { }

  ngOnInit() {
    this.getCurso(this.usuarioService.usuario.cursoId);
    this.getDetalleEscenarioUsuariosPorCurso(this.escenario.id, this.curso.id);
    this.getPuntajes(this.escenario.id)
  }

  getCurso(cursoId) {
    this.cursosService.getCurso(cursoId).subscribe(curso => this.curso = curso)
  }

  getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId) {
    this.escenariosService.getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId).subscribe(escenarioCurso => {
      this.escenarioCurso = escenarioCurso;
      this.setVentasChartProps(escenarioCurso.jugadores);
      this.setCajaChartProps(escenarioCurso.jugadores);
      this.setRentaChartProps(escenarioCurso.jugadores);
    })
  }

  getPuntajes(escenarioId) {
    this.escenariosService.getPuntajes(escenarioId).subscribe(puntajes => {
      if (puntajes) {
        this.puntajes = puntajes;
      }
    })
  }

  setVentasChartProps(jugadores) {
    var jugadoresPorVentas = jugadores.concat([]);
    jugadoresPorVentas.sort((j1, j2) => j1.ventas < j2.ventas);
    this.ventasChartProps = {
      options: {
        scaleShowVerticalLines: false,
        responsive: true,
        aspectRatio: 3
      },
      labels: jugadoresPorVentas.map(j => j.usuarioNombre),
      type: 'horizontalBar',
      legend: false,
      data: [
        {
          data: jugadoresPorVentas.map(j => j.ventasTotales),
          label: 'Ventas'
        }
      ]
    }
  }


  setCajaChartProps(jugadores) {
    var jugadoresPorCaja = jugadores.concat([]);
    jugadoresPorCaja.sort((j1, j2) => j1.cajaFinal < j2.cajaFinal);
    this.cajaChartProps = {
      options: {
        scaleShowVerticalLines: false,
        responsive: true,
        aspectRatio: 3
      },
      labels: jugadoresPorCaja.map(j => j.usuarioNombre),
      type: 'horizontalBar',
      legend: false,
      data: [
        {
          data: jugadoresPorCaja.map(j => j.cajaFinal),
          label: 'Caja Final'
        }
      ]
    }
  }


  setRentaChartProps(jugadores) {
    var jugadoresPorRenta = jugadores.concat([]);
    jugadoresPorRenta.sort((j1, j2) => j1.renta < j2.renta);
    this.rentaChartProps = {
      options: {
        scaleShowVerticalLines: false,
        responsive: true,
        aspectRatio: 3
      },
      labels: jugadoresPorRenta.map(j => j.usuarioNombre),
      type: 'horizontalBar',
      legend: false,
      data: [
        {
          data: jugadoresPorRenta.map(j => j.renta),
          label: 'Renta'
        }
      ]
    }
  }

  getMedalUrlByIndex(index) {
    switch (index) {
      case 0: return "../../assets/img/gold-medal.png";
      case 1: return "../../assets/img/silver-medal.png";
      case 2: return "../../assets/img/bronze-medal.png";
      default: return "../../assets/img/medal.png";
    }
  }

}
<file_sep>/src/app/premios-detail/premios-detail.component.spec.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { PremiosDetailComponent } from './premios-detail.component';

describe('PremiosDetailComponent', () => {
  let component: PremiosDetailComponent;
  let fixture: ComponentFixture<PremiosDetailComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ PremiosDetailComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(PremiosDetailComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
<file_sep>/src/environments/environment.prod.ts
export const environment = {
  production: true,
  firebase: {
    apiKey: "<KEY>",
    authDomain: "augure-8db58.firebaseapp.com",
    databaseURL: "https://augure-8db58.firebaseio.com",
    projectId: "augure-8db58",
    storageBucket: "",
    messagingSenderId: "30865807746"
  },
  proyectoServiceHost: ""
};
   
<file_sep>/src/app/decision-detalle/consecuencias/consecuencia-dialog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormControl, Validators, FormGroup } from '@angular/forms';
import { ProyectoService } from '../../proyecto.service';
import { MessagesService } from '../../messages.service';


@Component({
  selector: 'consecuencia-dialog',
  templateUrl: './consecuencia-dialog.component.html',
  styleUrls: ['./consecuencia-dialog.component.css']
})
export class ConsecuenciaDialogComponent implements OnInit {
  tipoCuentas;
  tipoFlujoFondos;
  consecuencia;
  descripcion = new FormControl('', [Validators.required])
  monto = new FormControl('', [Validators.required])
  periodoInicio = new FormControl('', [Validators.required])
  cantidadPeriodos = new FormControl('', [Validators.required])
  tipoCuenta = new FormControl('', [Validators.required])
  tipoFlujoFondo = new FormControl('', [Validators.required])
  tipoBalance = new FormControl(null);
  consecuenciaForm: FormGroup = new FormGroup({
    descripcion: this.descripcion,
    monto: this.monto,
    periodoInicio: this.periodoInicio,
    cantidadPeriodos: this.cantidadPeriodos,
    tipoCuenta: this.tipoCuenta,
    tipoFlujoFondo: this.tipoFlujoFondo,
    tipoBalance: this.tipoBalance
  });

  constructor(public dialogRef: MatDialogRef<ConsecuenciaDialogComponent>,
    private proyectoService: ProyectoService,
    private messageService: MessagesService,
    @Inject(MAT_DIALOG_DATA) public data) { }

  ngOnInit() {
    this.getTipoCuentas();
    this.getTipoFlujoFondos();
    this.consecuencia = JSON.parse(JSON.stringify(this.data));
  }

  getTipoCuentas(){
      this.proyectoService.getTipoCuentas().subscribe(tipoCuentas => this.tipoCuentas = tipoCuentas)
  }

  getTipoFlujoFondos(){
    this.proyectoService.getTipoFlujoFondos().subscribe(tipoFlujoFondos => this.tipoFlujoFondos = tipoFlujoFondos)
  }

  cancel(){
      this.dialogRef.close();
  }

  save(){
    if(this.consecuenciaForm.valid){
      this.dialogRef.close(this.consecuencia);
    }else {
      this.messageService.openSnackBarDatosIngresados()
    }
  }

}
<file_sep>/src/app/cursos/cursos.component.ts
import { Component, OnInit } from '@angular/core';
import { CursosService } from '../cursos.service';
import { CursoDialogComponent } from './curso-dialog/curso-dialog.component';
import { MatDialog } from '@angular/material';
import { UsuarioService } from '../usuario.service';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';

@Component({
  selector: 'app-cursos',
  templateUrl: './cursos.component.html',
  styleUrls: ['./cursos.component.css']
})
export class CursosComponent implements OnInit {

  cursos;
  usuario;

  constructor(private cursosService: CursosService, private dialog: MatDialog, private usuarioService: UsuarioService) { }

  ngOnInit() {
    this.getCursos();
    this.usuario = this.usuarioService.usuario;
  }

  getCursos() {
    this.cursosService.getCursos().subscribe(cursos => this.cursos = cursos.map(
      c => {
        const curso={
          id: c.id,
          nombre: c.nombre,
          clave: atob(c.clave)
        };
        return curso;
      }
    ));
  }

  decodeBase64() {

  }

  editCurso(curso) {
    const dialogRef = this.dialog.open(CursoDialogComponent, {
      width: '400px',
      data: curso
    });
    dialogRef.afterClosed().subscribe(_ => this.getCursos());
  }

  removeCurso(curso) {
    const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
      width: '500px',
      data: {
        message: "Seguro que desea eliminar el curso?"
      }
    });
    dialogRef.afterClosed().subscribe(response => {
      if (response === "OK") {
        this.cursosService.removeCurso(curso).subscribe(_ => this.getCursos())
      }
    });
  }

  createCurso() {
    const dialogRef = this.dialog.open(CursoDialogComponent, {
      width: '400px',
      data: {
        nombre: "",
        clave: ""
      }
    });
    dialogRef.afterClosed().subscribe(_ => this.getCursos());
  }

}
<file_sep>/src/app/decisiones.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { environment } from '../environments/environment';
import { catchError } from 'rxjs/operators';
import { MessagesService } from './messages.service';

@Injectable({
  providedIn: 'root'
})
export class DecisionesService {

  constructor(private http: HttpClient, private messageService: MessagesService) { }

  createDecision(decision) {
    return this.http.post(`${environment.proyectoServiceHost}/api/decisiones`, decision)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  modifyDecision(decision) {
    return this.http.put(`${environment.proyectoServiceHost}/api/decisiones/${decision.id}`, decision)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  removeDecision(idDecision) {
    return this.http.delete(`${environment.proyectoServiceHost}/api/decisiones/${idDecision}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

}
<file_sep>/src/app/confirmation-dialog/confirmation-dialog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { MessagesService } from '../messages.service';

@Component({
  selector: 'app-confirmation-dialog',
  templateUrl: './confirmation-dialog.component.html',
  styleUrls: ['./confirmation-dialog.component.css']
})
export class ConfirmationDialogComponent implements OnInit {

  message;

  constructor(public dialogRef: MatDialogRef<ConfirmationDialogComponent>,
    private messageService: MessagesService,
    @Inject(MAT_DIALOG_DATA) public data) { }

  ngOnInit() {
    const data = JSON.parse(JSON.stringify(this.data));
    this.message = data.message;
  }

  cancel() {
    this.dialogRef.close();
  }

  accept() {
    this.dialogRef.close("OK");
  }


}
<file_sep>/src/app/estado-juego-detail/estado-juego-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { CursosService } from '../cursos.service';
import { EscenariosService } from '../escenarios.service';
import { Message, MessageSpan } from '@angular/compiler/src/i18n/i18n_ast';
import { MessagesService } from '../messages.service';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';

@Component({
  selector: 'app-estado-juego-detail',
  templateUrl: './estado-juego-detail.component.html',
  styleUrls: ['./estado-juego-detail.component.css']
})
export class EstadoJuegoDetailComponent implements OnInit {

  escenario;
  curso;
  ventasChartProps;
  escenarioCurso;
  puntajes

  configuracionMercado;

  constructor(private route: ActivatedRoute, private cursosService: CursosService,
    private escenariosService: EscenariosService,
    private dialog: MatDialog,
    private messageService: MessagesService) { }

  ngOnInit() {
    this._loadData()
  }

  _loadData() {
    const escenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    const cursoId = Number(this.route.snapshot.paramMap.get('cursoId'));
    this.getCurso(cursoId)
    this.getEscenario(escenarioId);
    this.getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId);
    this.getPuntajes(escenarioId)
  }

  getPuntajes(escenarioId) {
    this.escenariosService.getPuntajes(escenarioId).subscribe(puntajes => {
      if (puntajes) {
        this.puntajes = puntajes;
      }
    })
  }

  getConfiguracionMercado(escenario) {
    this.escenariosService.getConfiguracionMercado(escenario.id).subscribe(configuracionMercado => {
      if (configuracionMercado.restriccionPrecio) {
        this.configuracionMercado = configuracionMercado;
      } else {
        this.setConfiguracionMercado(escenario)
      }
    })
  }

  setConfiguracionMercado(escenario) {
    const arrayPeriodos = [...Array(escenario.maximosPeriodos).keys()],
      array3 = [...Array(3).keys()],
      array4 = [...Array(4).keys()];
    this.configuracionMercado = {
      empresasCompetidoras: array3.map((_, index) => {
        return {
          escenarioId: escenario.id,
          nombre: null,
          bajo: 0,
          medio: 0,
          alto: 0
        }
      }),
      mercadosPeriodo: arrayPeriodos.map((_, index) => {
        return {
          escenarioId: escenario.id,
          periodo: index + 1,
          bajo: 0,
          medio: 0,
          alto: 0
        }
      }),
      ponderacionesMercado: {
        precioDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "PRECIO_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        vendedoresDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "VENDEDORES_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        publicidadDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "PUBLICIDAD_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        modalidadCobro: array4.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "MODALIDAD_DE_COBRO",
            valor: index,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        calidadDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "CALIDAD_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        })
      },
      restriccionPrecio: {
        escenarioId: escenario.id,
        precioMin: 0,
        precioMax: 0
      }
    }
  }

  getModalidadCobroDesc(modCobroIndex) {
    return ["Contado", "a 30 dias", "a 60 dias", "a 90 dias"][modCobroIndex]
  }

  getCurso(cursoId) {
    this.cursosService.getCurso(cursoId).subscribe(curso => this.curso = curso)
  }

  getEscenario(escenarioId) {
    this.escenariosService.getEscenario(escenarioId).subscribe(escenario => {
      this.escenario = escenario
      this.getConfiguracionMercado(escenario)
    })
  }

  getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId) {
    this.escenariosService.getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId).subscribe(escenarioCurso => this.escenarioCurso = escenarioCurso)
  }

  cerrarEscenario() {
    const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
      width: '500px',
      data: {
        message: "¿Seguro que desea cerrar el escenario?"
      }
    });

    dialogRef.afterClosed().subscribe(response => {
      if (response === "OK") {
        this.escenariosService.getConfiguracionMercado(this.escenario.id).subscribe(configuracionMercado => {
          if (configuracionMercado.restriccionPrecio) {
            this.escenariosService.simularMercado(this.escenario.id, this.curso.id).subscribe(_ => {
              this.messageService.openSnackBar("Simulación de Mercado ejecutada correctamente")
              this._loadData()
            })
          } else {
            this.messageService.openSnackBar("Antes de cerrar el escenario, debe guardar las configuraciones de mercado")
          }
        })
      }
    });

  }



}
<file_sep>/src/app/simulaciones/simulaciones.component.ts
import { Component, OnInit } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { UsuarioService } from '../usuario.service';
import { zip, of } from "rxjs";
import { switchMap, map } from 'rxjs/operators';
import { Router } from '@angular/router';

@Component({
  selector: 'app-simulaciones',
  templateUrl: './simulaciones.component.html',
  styleUrls: ['./simulaciones.component.css']
})
export class SimulacionesComponent implements OnInit {
  usuario;
  escenarios;

  constructor(private escenariosService: EscenariosService, private usuarioService: UsuarioService,private router: Router) { }

  ngOnInit() {
    this.usuario = this.usuarioService.usuario;
    this.getEscenarios(this.usuario.id);
    if(this.usuario.rol === "ADMIN"){
      this.router.navigateByUrl("/estado-juegos");
    }
  }

  getEscenarios(idUsuario) {
    const $escenarios = this.escenariosService.getEscenariosParaUsuario(idUsuario);

    const $escenariosConProyectos = $escenarios.pipe(
      switchMap(escenarios => {
        const $escenarios = escenarios.map(escenario => {
          var $proyecto = this.usuarioService.getProyecto(escenario.id, idUsuario);
          var $escenarioCurso = this.escenariosService.getDetalleEscenarioUsuariosPorCurso(escenario.id, this.usuario.curso.id);
          return zip($proyecto, $escenarioCurso).pipe(
            map(([proyecto, escenarioCurso]) => {
              escenario.proyecto = proyecto;
              escenario.escenarioCurso = escenarioCurso;
              return escenario;
            })
          );
        })
        return zip(...$escenarios);
      }));

    $escenariosConProyectos.subscribe(escenarios => this.escenarios = escenarios);
  }

}
<file_sep>/src/app/app.module.ts
import '../polyfills';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { environment } from '../environments/environment';
import { HttpClientModule } from '@angular/common/http';
import { CdkTableModule } from '@angular/cdk/table';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './/app-routing.module';
import { EstadoComponent } from './estado/estado.component';
import { PremiosComponent } from './premios/premios.component';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './login/components/register.component';
import { DecisionesComponent } from './decisiones/decisiones.component';
import { DecisionDetalleComponent } from './decision-detalle/decision-detalle.component';
import { FlujoFondosComponent } from './flujo-fondos/flujo-fondos.component';
import { ProyectoService } from './proyecto.service';
import { UsuarioService } from './usuario.service';
import { TomaDecisionesComponent } from './toma-decisiones/toma-decisiones.component';
import { ResultadosComponent, EntregarConfirmationPopup } from './resultados/resultados.component';
import { ResultadosAdminComponent } from './resultados-admin/resultados-admin.component';
import { EscenariosComponent } from './escenarios/escenarios.component';
import { EscenarioDetalleComponent } from './escenario-detalle/escenario-detalle.component';
import { ChartsModule } from 'ng2-charts';
import { EscenariosService } from './escenarios.service';
import { DecisionesService } from './decisiones.service';
import { OpcionDialogComponent } from './decision-detalle/opciones/opcion-dialog.component';
import { ConsecuenciaDialogComponent } from './decision-detalle/consecuencias/consecuencia-dialog.component';
import { MessagesService } from './messages.service';
import { SimulacionesComponent } from './simulaciones/simulaciones.component';
import { PresupuestoFinancieroComponent } from './presupuesto-financiero/presupuesto-financiero.component';
import { PresupuestoEconomicoComponent } from './presupuesto-economico/presupuesto-economico.component';
import { BalanceFinalComponent } from './balance-final/balance-final.component';
import { ProveedoresComponent } from './proveedores/proveedores.component';
import { ProveedorDetailComponent } from './proveedor-detail/proveedor-detail.component';
import { ProveedoresService } from './proveedores.service';
import { EstadoJuegosComponent } from './estado-juegos/estado-juegos.component';
import { FinanciacionesComponent } from './financiaciones/financiaciones.component';
import { FinanciacionService } from './financiacion.service';
import { FinanciacionDialogComponent } from './financiaciones/financiacion-dialog/financiacion-dialog.component';
import { CursosComponent } from './cursos/cursos.component';
import { CursoDialogComponent } from './cursos/curso-dialog/curso-dialog.component';

import {
  MatAutocompleteModule,
  MatButtonModule,
  MatButtonToggleModule,
  MatCardModule,
  MatCheckboxModule,
  MatChipsModule,
  MatDatepickerModule,
  MatDialogModule,
  MatExpansionModule,
  MatGridListModule,
  MatIconModule,
  MatInputModule,
  MatListModule,
  MatMenuModule,
  MatNativeDateModule,
  MatPaginatorModule,
  MatProgressBarModule,
  MatProgressSpinnerModule,
  MatRadioModule,
  MatRippleModule,
  MatSelectModule,
  MatSidenavModule,
  MatSliderModule,
  MatSlideToggleModule,
  MatSnackBarModule,
  MatSortModule,
  MatTableModule,
  MatTabsModule,
  MatToolbarModule,
  MatTooltipModule,
  MatStepperModule,
  MatTabGroup
} from '@angular/material';
import { MatriculacionDialogComponent } from './matriculacion-dialog/matriculacion-dialog.component';
import { EstadoJuegoDetailComponent } from './estado-juego-detail/estado-juego-detail.component';
import { SimulacionEntregadaComponent } from './simulacion-entregada/simulacion-entregada.component';
import { PremiosDetailComponent } from './premios-detail/premios-detail.component';
import { RankingsComponent } from './rankings/rankings.component';
import { ConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component';


@NgModule({
  exports: [
    CdkTableModule,
    MatAutocompleteModule,
    MatButtonModule,
    MatButtonToggleModule,
    MatCardModule,
    MatCheckboxModule,
    MatChipsModule,
    MatStepperModule,
    MatDatepickerModule,
    MatDialogModule,
    MatExpansionModule,
    MatGridListModule,
    MatIconModule,
    MatInputModule,
    MatListModule,
    MatMenuModule,
    MatNativeDateModule,
    MatPaginatorModule,
    MatProgressBarModule,
    MatProgressSpinnerModule,
    MatRadioModule,
    MatRippleModule,
    MatSelectModule,
    MatSidenavModule,
    MatSliderModule,
    MatSlideToggleModule,
    MatSnackBarModule,
    MatSortModule,
    MatTableModule,
    MatTabsModule,
    MatToolbarModule,
    MatTooltipModule
  ],
  imports: [AppRoutingModule]
})
export class MaterialModule { }


@NgModule({
  declarations: [
    AppComponent,
    EstadoComponent,
    PremiosComponent,
    HomeComponent,
    LoginComponent,
    RegisterComponent,
    DecisionesComponent,
    DecisionDetalleComponent,
    SimulacionEntregadaComponent,
    FlujoFondosComponent,
    TomaDecisionesComponent,
    ResultadosComponent,
    ResultadosAdminComponent,
    EscenariosComponent,
    EscenarioDetalleComponent,
    OpcionDialogComponent,
    ConsecuenciaDialogComponent,
    FinanciacionDialogComponent,
    MatriculacionDialogComponent,
    EntregarConfirmationPopup,
    CursoDialogComponent, 
    ConfirmationDialogComponent,
    SimulacionesComponent,
    PresupuestoFinancieroComponent,
    PresupuestoEconomicoComponent,
    BalanceFinalComponent,
    ProveedoresComponent,
    ProveedorDetailComponent,
    EstadoJuegosComponent,
    FinanciacionesComponent,
    CursosComponent,
    EstadoJuegoDetailComponent,
    PremiosDetailComponent,
    RankingsComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    HttpClientModule,
    MaterialModule,
    ReactiveFormsModule,
    AppRoutingModule,
    HttpClientModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireAuthModule,
    ChartsModule
  ],
  providers: [
    ProyectoService,
    EscenariosService,
    UsuarioService,
    DecisionesService,
    MessagesService,
    ProveedoresService,
    FinanciacionService
  ],
  entryComponents: [RegisterComponent, OpcionDialogComponent, ConsecuenciaDialogComponent,
    FinanciacionDialogComponent, CursoDialogComponent, MatriculacionDialogComponent, EntregarConfirmationPopup, ConfirmationDialogComponent],
  bootstrap: [AppComponent, HomeComponent]
})
export class AppModule { }

//platformBrowserDynamic().bootstrapModule(AppModule);<file_sep>/src/app/decisiones.service.spec.ts
import { TestBed } from '@angular/core/testing';

import { DecisionesService } from './decisiones.service';

describe('DecisionesService', () => {
  beforeEach(() => TestBed.configureTestingModule({}));

  it('should be created', () => {
    const service: DecisionesService = TestBed.get(DecisionesService);
    expect(service).toBeTruthy();
  });
});
<file_sep>/src/app/login/components/register.component.ts
import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import * as firebase from 'firebase/app';
import { MatDialogRef, MatSnackBar } from '@angular/material';
import { FormControl, Validators, FormGroup, AbstractControl, ValidationErrors } from '@angular/forms';
import { Router } from '@angular/router';

@Component({
  selector: 'register-dialog',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {

  email = new FormControl('', [Validators.required, Validators.email])
  password = new FormControl('', [Validators.required])
  repeatPassword = new FormControl('', [Validators.required])
  registerForm: FormGroup = new FormGroup({
    email: this.email,
    password: <PASSWORD>,
    repeatPassword: this.<PASSWORD>
  }, this.validatePassword );

  constructor(public dialogRef: MatDialogRef<RegisterComponent>,
    private afAuth: AngularFireAuth,
    private router: Router,
    public snackBar: MatSnackBar) { }

  ngOnInit() {

  }

  onRegister() {
    if (this.registerForm.valid) {
      this.afAuth.auth.createUserWithEmailAndPassword(this.email.value, this.password.value)
        .then(_ => this.afAuth.auth.signInWithEmailAndPassword(this.email.value, this.password.value)
          .then(_ => {
            this.dialogRef.close();
            this.router.navigate(['/home']);
          })
          .catch(e => this.openSnackBar(e.message)))
        .catch(e => this.openSnackBar(e.message));
    }
  }

  validatePassword(c: AbstractControl): ValidationErrors {
    var error = { passwordNoMatch: true };
    var password = c.get('password');
    var repeat = c.get('repeatPassword');
    if (password.value != repeat.value){
     repeat.setErrors(error)
     return { passwordNoMatch: true };
    }else{
      repeat.setErrors(null);
      return null;
    }
  }

  onClose() {
    this.dialogRef.close();
  }

  openSnackBar(message: string, action: string = "OK") {
    this.snackBar.open(message, action, {
      duration: 2000,
      //extraClasses: ['error-snack-bar']
    });
  }

}
<file_sep>/src/app/escenario-detalle/escenario-detalle.component.ts
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { Validators, FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { EscenariosService } from '../escenarios.service';
import { MessagesService } from '../messages.service';
import { CursosService } from '../cursos.service';
import { MatAutocompleteSelectedEvent, MatAutocomplete } from '@angular/material';
import { startWith, map } from 'rxjs/operators';
import { of, Observable } from 'rxjs';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'app-escenario-detalle',
  templateUrl: './escenario-detalle.component.html',
  styleUrls: ['./escenario-detalle.component.css']
})
export class EscenarioDetalleComponent implements OnInit {
  separatorKeysCodes: number[] = [ENTER, COMMA];
  filtredCursos: Observable<string[]>;
  usuario;
  allCursos;
  configuracionMercado;
  escenario = {
    id: null,
    titulo: '',
    maximosPeriodos: null,
    nombrePeriodos: 'Mes',
    descripcion: '',
    impuestoPorcentaje: null,
    balanceInicial: {
      activo: {
        caja: 0,
        cuentasPorCobrar: 0,
        cuentasPorCobrarPeriodos: 0,
        inventario: 0,
        maquinaria: 0,
        amortizacionAcumulada: 0
      },
      pasivo: {
        proveedores: 0,
        proveedoresPeriodos: 0,
        deudasBancarias: 0,
        deudasBancariasPeriodos: 0
      },
      patrimonioNeto: {
        capitalSocial: 0,
        resultadoDelEjercicio: 0
      }
    },
    estadoInicial: {
      costoFijo: 0,
      costoVariable: 0,
      stock: 0,
      produccionMensual: 0,
      calidad: 0,
      publicidad: 0,
      cantidadVendedores: 0
    },
    cursos: [],
    invalid: null
  };

  descripcion = new FormControl('', [Validators.required]);
  titulo = new FormControl('', [Validators.required]);
  maximosPeriodos = new FormControl(new Number(), [Validators.required, Validators.min(1)]);
  nombrePeriodos = new FormControl({ value: '', disabled: true }, [Validators.required]);
  impuestoPorcentaje = new FormControl(new Number(), [Validators.required, Validators.min(0), Validators.max(99.9)]);
  cursos = new FormControl();
  escenarioForm: FormGroup = new FormGroup({
    descripcion: this.descripcion,
    titulo: this.titulo,
    maximosPeriodos: this.maximosPeriodos,
    nombrePeriodos: this.nombrePeriodos,
    cursos: this.cursos
  });

  caja = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  cuentasPorCobrar = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  cuentasPorCobrarPeriodos = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  inventario = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  maquinaria = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  amortizacionAcumulada = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  activoForm: FormGroup = new FormGroup({
    caja: this.caja,
    cuentasPorCobrar: this.cuentasPorCobrar,
    inventario: this.inventario,
    maquinaria: this.maquinaria,
    amortizacionAcumulada: this.amortizacionAcumulada
  });

  proveedores = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  proveedoresPeriodos = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  deudasBancarias = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  deudasBancariasPeriodos = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  pasivoForm: FormGroup = new FormGroup({
    proveedores: this.proveedores,
    proveedoresPeriodos: this.proveedoresPeriodos,
    deudasBancarias: this.deudasBancarias,
    deudasBancariasPeriodos: this.deudasBancariasPeriodos
  });

  capitalSocial = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  resultadoDelEjercicio = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  patrimonioNetoForm: FormGroup = new FormGroup({
    capitalSocial: this.capitalSocial,
    resultadoDelEjercicio: this.resultadoDelEjercicio
  });

  calidad = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  costoFijo = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  costoVariable = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  produccionMensual = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  stock = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  publicidad = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  cantidadVendedores = new FormControl(new Number(), [Validators.required, Validators.min(0)]);
  estadoInicialForm: FormGroup = new FormGroup({
    calidad: this.calidad,
    costoFijo: this.costoFijo,
    costoVariable: this.costoVariable,
    produccionMensual: this.produccionMensual,
    stock: this.stock,
    publicidad: this.publicidad,
    cantidadVendedores: this.cantidadVendedores
  });

  forecasts = [{
    proyectoId: 1,
    periodo: 0,
    cantidadUnidades: 0,
    precio: 0
  }, {
    proyectoId: 1,
    periodo: 1,
    cantidadUnidades: 0,
    precio: 0
  }, {
    proyectoId: 1,
    periodo: 2,
    cantidadUnidades: 0,
    precio: 0
  }, {
    proyectoId: 1,
    periodo: 3,
    cantidadUnidades: 0,
    precio: 0
  }]

  porcentajeCaja = new FormControl('', [Validators.required]);
  porcentajeVentas = new FormControl('', [Validators.required]);
  porcentajeRenta = new FormControl('', [Validators.required]);
  porcentajeEscenario = new FormControl('', [Validators.required, Validators.min(0), Validators.max(100)]);

  puntajeForm: FormGroup = new FormGroup({
    porcentajeCaja: this.porcentajeCaja,
    porcentajeVentas: this.porcentajeVentas,
    porcentajeRenta: this.porcentajeRenta,
    porcentajeEscenario: this.porcentajeEscenario
  });

  hasProyectos = false;

  constructor(private escenariosService: EscenariosService,
    private usuariosService: UsuarioService,
    private route: ActivatedRoute,
    private router: Router,
    private cursosService: CursosService,
    private messageService: MessagesService) {

  }


  puntajes = {
    escenarioId: null,
    porcentajeCaja: 0,
    porcentajeVentas: 0,
    porcentajeRenta: 0,
    porcentajeEscenario: 0
  }

  @ViewChild('cursosInput', { static: false }) cursosInput: ElementRef<HTMLInputElement>;
  @ViewChild('auto', { static: false }) matAutocomplete: MatAutocomplete;

  ngOnInit() {
    var id = Number(this.route.snapshot.paramMap.get('id'));
    if (id) {
      this.getEscenario(id).subscribe(escenario => {
        this.escenario = escenario
        this.getConfiguracionMercado(escenario)
        this.getHasProyectos(escenario.id);
        this.escenariosService.getCursosEscenario(id).subscribe(cursosEscenario => {
          this.escenario.cursos = cursosEscenario;
        })
      });
      this.getPuntajes(id);
    }
    this.getCursos();
  }

  getHasProyectos(escenarioId) {
    return this.escenariosService.getProyectosByEscenario(escenarioId).subscribe(proyectos => {
      this.hasProyectos = (proyectos.length > 0)
    })
  }

  getPuntajes(escenarioId) {
    this.escenariosService.getPuntajes(escenarioId).subscribe(puntajes => {
      if (puntajes) {
        this.puntajes = puntajes;
      } else {
        this.puntajes.escenarioId = escenarioId;
      }
    })
  }

  getCursos() {
    this.cursosService.getCursos().subscribe(cursos => {
      this.allCursos = cursos;
      this.filtredCursos = of(cursos);

      this.filtredCursos = this.cursos.valueChanges.pipe(
        startWith(null),
        map((curso: string | null) => {
          return curso ? this._filterCurso(curso) : this.allCursos.slice()
        }));

    })
  }

  selectedCurso(event: MatAutocompleteSelectedEvent) {
    this.escenario.cursos.push(
      event.option.value
    );
    this.cursosInput.nativeElement.value = '';
    this.cursos.setValue(null);
  }

  removeCurso(curso): void {
    const index = this.escenario.cursos.indexOf(curso);
    if (index >= 0) {
      this.escenario.cursos.splice(index, 1);
    }
  }

  private _filterCurso(value: string): string[] {
    if (value.toLowerCase) {
      const filterValue = value.toLowerCase();
      return this.allCursos.filter(curso => curso.nombre.toLowerCase().indexOf(filterValue) === 0);
    } else {
      return this.allCursos;
    }

  }

  getEscenario(id) {
    return this.escenariosService.getEscenario(id);
  }

  getPeriodosArray() {
    var maximosPeriodos = Number(this.escenario.maximosPeriodos);
    return [...Array(maximosPeriodos).keys(), maximosPeriodos];
  }

  save() {
    if (this.escenarioForm.valid && this.activoForm.valid && this.pasivoForm.valid && this.patrimonioNetoForm.valid
      && this.puntajeForm.valid) {
      if (this._porcentajesValidos()) {
        this.escenariosService[this.escenario.id ? 'modifyEscenario' : 'createEscenario'](this.escenario)
          .subscribe(escenario => {
            this.escenariosService.updateCursosEscenario(escenario.id, this.escenario.cursos).subscribe(_ => {
              this.escenariosService.postPuntajes(escenario.id, this.puntajes).subscribe(_ => {
                if (this.escenario.id) {
                  const error = this._getErroresConfiguracionesMercado();
                  if (error) {
                    this.messageService.openSnackBar(error)
                  } else {
                    this.escenariosService.postConfiguracionesMercado(this.configuracionMercado).subscribe(_ => {
                      this.messageService.openSnackBar("Escenario modificado");
                      this.router.navigate(['/escenarios'])
                    })
                  }
                } else {
                  this.messageService.openSnackBar("Escenario modificado");
                  this.router.navigate(['/escenarios/' + escenario.id])
                }
              })
            })
          });
      }
    } else {
      this.messageService.openSnackBar("ERROR: Revise los datos ingresados.");
    }
  }

  getConfiguracionMercado(escenario) {
    this.escenariosService.getConfiguracionMercado(escenario.id).subscribe(configuracionMercado => {
      if (configuracionMercado.restriccionPrecio) {
        this.configuracionMercado = configuracionMercado;
      } else {
        this.setConfiguracionMercado(escenario)
      }
    })
  }

  getMercadosPeriodo() {
    if (this.escenario.maximosPeriodos !== this.configuracionMercado.mercadosPeriodo.length && this.escenario.maximosPeriodos !== null) {
      this.setConfiguracionMercado(this.escenario);
    }
    return this.configuracionMercado.mercadosPeriodo;
  }

  setConfiguracionMercado(escenario) {
    const arrayPeriodos = [...Array(escenario.maximosPeriodos).keys()],
      array3 = [...Array(3).keys()],
      array4 = [...Array(4).keys()];
    this.configuracionMercado = {
      empresasCompetidoras: array3.map((_, index) => {
        return {
          escenarioId: escenario.id,
          nombre: 'Empresa ' + index,
          bajo: 0,
          medio: 0,
          alto: 0
        }
      }),
      mercadosPeriodo: arrayPeriodos.map((_, index) => {
        return {
          escenarioId: escenario.id,
          periodo: index + 1,
          bajo: 0,
          medio: 0,
          alto: 0
        }
      }),
      ponderacionesMercado: {
        precioDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "PRECIO_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        vendedoresDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "VENDEDORES_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        publicidadDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "PUBLICIDAD_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        modalidadCobro: array4.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "MODALIDAD_DE_COBRO",
            valor: index,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        }),
        calidadDesde: array3.map((_, index) => {
          return {
            escenarioId: escenario.id,
            concepto: "CALIDAD_DESDE",
            valor: 0,
            bajo: 0,
            medio: 0,
            alto: 0
          }
        })
      },
      restriccionPrecio: {
        escenarioId: escenario.id,
        precioMin: 0,
        precioMax: 0
      }
    }
  }

  getModalidadCobroDesc(modCobroIndex) {
    return ["Contado", "a 30 dias", "a 60 dias", "a 90 dias"][modCobroIndex]
  }

  _getErroresConfiguracionesMercado() {
    return [this._validateObligatory(this.configuracionMercado.empresasCompetidoras, "nombre"),
    this._validateObligatory(this.configuracionMercado.mercadosPeriodo, null),
    this._validateObligatory(this.configuracionMercado.ponderacionesMercado.precioDesde, "valor"),
    this._validateSecuencia(this.configuracionMercado.ponderacionesMercado.precioDesde, "valor"),
    this._validatePrecioContraIntervalo(),
    this._validateObligatory(this.configuracionMercado.ponderacionesMercado.modalidadCobro, "valor"),
    this._validateSecuencia(this.configuracionMercado.ponderacionesMercado.modalidadCobro, "valor"),
    this._validateObligatory(this.configuracionMercado.ponderacionesMercado.publicidadDesde, "valor"),
    this._validateSecuencia(this.configuracionMercado.ponderacionesMercado.publicidadDesde, "valor"),
    this._validateObligatory(this.configuracionMercado.ponderacionesMercado.calidadDesde, "valor"),
    this._validateSecuencia(this.configuracionMercado.ponderacionesMercado.calidadDesde, "valor"),
    this._validateObligatory(this.configuracionMercado.ponderacionesMercado.vendedoresDesde, "valor"),
    this._validateSecuencia(this.configuracionMercado.ponderacionesMercado.vendedoresDesde, "valor"),
    this._validateIntervaloPrecio()].find(elem => elem !== true)
  }

  _validateSecuencia(array, prop) {
    var arrayValor = array.map(elem => elem[prop]),
      arrayOrdernado = arrayValor.concat([]).sort((a, b) => a > b ? 1 : -1);
    if (JSON.stringify(arrayOrdernado) !== JSON.stringify(arrayValor)) {
      return "Revise las secuencias de valores en las configuraciones de mercado"
    }
    return true;
  }

  _validateObligatory(array, additionalProp) {
    return array.find(elem => {
      return (!(elem.bajo >= 0 && elem.medio >= 0 && elem.alto >= 0) ||
        (additionalProp ? !(elem[additionalProp] || elem[additionalProp] === 0) : false))
    }) ? "Falta completar datos en la configuracion del mercado" : true;
  }

  _validatePrecioContraIntervalo() {
    return this.configuracionMercado.ponderacionesMercado.precioDesde.find(elem => {
      return (elem.valor > this.configuracionMercado.restriccionPrecio.precioMax ||
        elem.valor < this.configuracionMercado.restriccionPrecio.precioMin)
    }) ? "El rango de precios y los precios ingresados no coinciden" : true;
  }

  _validateIntervaloPrecio() {
    return (this.configuracionMercado.restriccionPrecio.precioMin > 0 &&
      this.configuracionMercado.restriccionPrecio.precioMax > 0 &&
      this.configuracionMercado.restriccionPrecio.precioMin < this.configuracionMercado.restriccionPrecio.precioMax)
      ? true : "Ingrese un Intervalo de Precio valido"
  }

  _porcentajesValidos() {
    if (this.puntajes.porcentajeCaja + this.puntajes.porcentajeVentas + this.puntajes.porcentajeRenta !== 100) {
      this.messageService.openSnackBar("Puntajes: Los porcentajes ingresados para caja, renta y ventas deben sumar 100%")
      return false;
    }
    return true;
  }

}
<file_sep>/src/app/financiaciones/financiaciones.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { FinanciacionService } from '../financiacion.service';
import { MatDialog } from '@angular/material';
import { FinanciacionDialogComponent } from './financiacion-dialog/financiacion-dialog.component';

@Component({
  selector: 'app-financiaciones',
  templateUrl: './financiaciones.component.html',
  styleUrls: ['./financiaciones.component.css']
})
export class FinanciacionesComponent implements OnInit {
  @Input() escenario: any;
  @Input() hasProyectos: boolean;
  financiaciones;
  constructor(private dialog: MatDialog,
    private escenarioService: EscenariosService,
    private financiacionService: FinanciacionService) { }

  ngOnInit() {
    this.getFinanciaciones();
  }

  getFinanciaciones() {
    this.escenarioService.getFinanciaciones(this.escenario.id).subscribe(financiaciones => this.financiaciones = financiaciones);
  }

  editFinanciacion(financiacion) {
    const dialogRef = this.dialog.open(FinanciacionDialogComponent, {
      width: '400px',
      data: financiacion
    });
    dialogRef.afterClosed().subscribe(_ => this.getFinanciaciones());
  }

  removeFinanciacion(financiacion) {
    this.financiacionService.removeFinanciacion(financiacion).subscribe(_ => this.getFinanciaciones())
  }

  createFinanciacion(){
    const dialogRef = this.dialog.open(FinanciacionDialogComponent, {
      width: '400px',
      data: {
        escenarioId: this.escenario.id,
        descripcion:"",
        tea: 0,
        cantidadCuotas: 0
      }
    });
    dialogRef.afterClosed().subscribe(_ => this.getFinanciaciones());
  }

}
<file_sep>/src/app/matriculacion-dialog/matriculacion-dialog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormControl, Validators, FormGroup } from '@angular/forms';

import { MessagesService } from '../messages.service';
import { UsuarioService } from '../usuario.service';

@Component({
  selector: 'matriculacion-dialog',
  templateUrl: './matriculacion-dialog.component.html',
  styleUrls: ['./matriculacion-dialog.component.css']
})
export class MatriculacionDialogComponent implements OnInit {
  curso;
  usuario;
  nombre = new FormControl('', [Validators.required])
  clave = new FormControl('', [Validators.required])
  cursoForm: FormGroup = new FormGroup({
    nombre: this.nombre,
    clave: this.clave
  });

  constructor(public dialogRef: MatDialogRef<MatriculacionDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data, private usuarioService: UsuarioService,
    private messageService: MessagesService) { }

  ngOnInit() {
    this.usuario = JSON.parse(JSON.stringify(this.data));
    this.curso = {
      nombre: "",
      clave: ""
    };
  }

  cancel() {
    this.dialogRef.close();
  }

  matricular() {
    if (this.cursoForm.valid) {
      let cursoSend = {nombre: this.curso.nombre, clave: btoa(this.curso.clave)};
      this.usuarioService.matricular(this.usuario, cursoSend).subscribe(_ => {
        
        this.dialogRef.close();
        window.location.reload();
      });
    }
  }

}
<file_sep>/src/app/proveedores/proveedores.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { ProveedoresService } from '../proveedores.service';

@Component({
  selector: 'app-proveedores',
  templateUrl: './proveedores.component.html',
  styleUrls: ['./proveedores.component.css']
})
export class ProveedoresComponent implements OnInit {
  @Input() escenario: any;
  @Input() hasProyectos: boolean;
  proveedores;

  constructor(private escenariosService: EscenariosService, 
              private proveedoresService: ProveedoresService) { }

  ngOnInit() {
    this.getProveedores();
  }

  getProveedores() {
    this.escenariosService.getProveedores(this.escenario.id).subscribe(proveedores => {
      this.proveedores = proveedores;
    });
  }

  removeProveedor(proveedor) {
    this.proveedoresService.removeProveedor(proveedor).subscribe(_ => this.getProveedores());
  }

}
<file_sep>/README.md
# utn-simulador-ui

1. Run npm install


2. Run ng serve

3. Go to localhost:4200
<file_sep>/src/app/flujo-fondos/flujo-fondos.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ProyectoService } from '../proyecto.service';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { ActivatedRoute } from '@angular/router';
import { UsuarioService } from '../usuario.service';



@Component({
  selector: 'app-flujo-fondos',
  templateUrl: './flujo-fondos.component.html',
  styleUrls: ['./flujo-fondos.component.css'],
  animations: [
    trigger('detailExpand', [
      state('collapsed', style({ height: '0px', minHeight: '0' })),
      state('expanded', style({ height: '*' })),
      transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
    ]),
  ]
})
export class FlujoFondosComponent implements OnInit {
  @Input() proyecto: any;
  @Input() forecast: boolean;
  flujoFondos;
  periodos;
  escenario;

  constructor(private proyectoService: ProyectoService) { }

  ngOnInit() {
    this.getPeriodos(this.proyecto.id);
    this.getFlujoFondos(this.proyecto.id);
    this.escenario = this.proyecto.escenario;
  }

  getFlujoFondos(proyectoId) {
    this.proyectoService.getFlujoFondos(proyectoId, this.forecast).subscribe(ff => {
      this.flujoFondos = ff;
    })
  }

  getPeriodos(proyectoId) {
    this.proyectoService.getPeriodoActual(proyectoId, this.forecast).subscribe(periodoActual => {
      this.periodos = [...Array(periodoActual).keys(), periodoActual];
    })
  }

  getCuentaPeriodo(cuentasPeriodo, periodo, defaultCero = true, signoInvertido = false) {
    var periodo = cuentasPeriodo && cuentasPeriodo.find(cuentaPeriodo => cuentaPeriodo.periodo === periodo)
    var montoCrudo = periodo ? periodo.monto : (defaultCero ? 0 : "");
    montoCrudo = signoInvertido ? -montoCrudo : montoCrudo;
    if (montoCrudo < 0) {
      return "(" + Math.abs(montoCrudo).toLocaleString() + ")";
    } else if (montoCrudo === 0) {
      return montoCrudo;
    } else {
      return montoCrudo.toLocaleString();
    }
  }
}
<file_sep>/src/app/toma-decisiones/toma-decisiones.component.ts
import { Component, OnInit, EventEmitter } from '@angular/core';
import { ProyectoService } from '../proyecto.service';
import { Router, ActivatedRoute } from '@angular/router';
import { MessagesService } from '../messages.service';
import { EscenariosService } from '../escenarios.service';
import { UsuarioService } from '../usuario.service';
import { MatRadioChange } from '@angular/material';


@Component({
  selector: 'app-toma-decisiones',
  templateUrl: './toma-decisiones.component.html',
  styleUrls: ['./toma-decisiones.component.css']
})
export class TomaDecisionesComponent implements OnInit {

  decisiones: Array<any> = [];
  escenario;
  estado;
  proyecto;
  modalidadCobro: Array<any> = [];
  forecasts: Array<any> = [];
  proveedores: Array<any> = [];
  proveedorSeleccionado;
  usuario;
  financiaciones;
  credito;
  totalActivo = 0;
  totalPasivoPatrimonioNeto = 0;

  constructor(
    private proyectoService: ProyectoService,
    private escenarioService: EscenariosService,
    private usuarioService: UsuarioService,
    private router: Router,
    private route: ActivatedRoute,
    private messageService: MessagesService) { }

  ngOnInit() {
    var escenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    this.getProyecto(escenarioId, this.usuarioService.usuario.id);
    this.getFinanciaciones(escenarioId);
  }

  getEscenario(escenarioId) {
    this.escenarioService.getEscenario(escenarioId).subscribe(escenario => this.escenario = escenario);
  }

  getFinanciaciones(escenarioId) {
    this.escenarioService.getFinanciaciones(escenarioId).subscribe(financiaciones => this.financiaciones = financiaciones);
  }

  getProyecto(escenarioId, usuarioId) {
    this.usuarioService.getProyecto(escenarioId, usuarioId).subscribe(estado => {
      this.estado = estado;
      this.proyecto = estado.proyecto;
      this.escenario = estado.proyecto.escenario;
      this.getTotalActivo();
      this.getTotalPasivoPatrimonioNeto();
      this.getProveedores(this.proyecto.id)
      this.getCredito(this.proyecto.id)
      this.getDecisiones(this.proyecto.id)
      this.buildModalidadDeCobro(this.proyecto.id, estado.proyecto.modalidadCobro);
      this.buildForecast(this.proyecto.id);
    });
  }

  getCredito(proyectoId) {
    this.proyectoService.getCredito(proyectoId)
      .subscribe(credito => this.credito = credito || {
        monto: 0,
        periodoInicial: 0,
        proyectoId: this.proyecto.id
      });
  }

  getDecisiones(proyectoId) {
    this.proyectoService.getDecisiones(proyectoId)
      .subscribe(decisiones => this.decisiones = decisiones);
  }


  getProveedores(proyectoId) {
    this.proyectoService.getProveedores(proyectoId).subscribe(proveedores => {
      this.proveedores = proveedores;
      var proveedorSeleccionado = proveedores.find(p => p.seleccionado)
      if (proveedorSeleccionado) {
        this.proveedorSeleccionado = proveedorSeleccionado.id;
      }
    })
  }

  buildForecast(proyectoId) {
    this.proyectoService.getForecast(proyectoId).subscribe(forecasts => {
      var periodos = this.estado.proyecto.escenario.maximosPeriodos;
      if(forecasts.length > 0 && forecasts.length >= periodos + 1) { 
        this.forecasts = forecasts.slice(0, periodos + 1)
      } else {
        var periodosFaltantes = [...Array(periodos - forecasts.length).keys(), periodos - forecasts.length].map(periodo => {
          return {
            proyectoId: proyectoId,
            periodo: forecasts.length + periodo,
            cantidadUnidades: 0,
            precio: 0
          }
        });
        this.forecasts = this.forecasts.concat(forecasts);
        this.forecasts = this.forecasts.concat(periodosFaltantes);
      }
    })

  }

  buildModalidadDeCobro(proyectoId, modalidadCobro) {
    var modalidadCobroTemplate = [{
      proyectoId: this.proyecto.id,
      offsetPeriodo: 0,
      porcentaje: 0
    }, {
      proyectoId: this.proyecto.id,
      offsetPeriodo: 1,
      porcentaje: 0
    }, {
      proyectoId: this.proyecto.id,
      offsetPeriodo: 2,
      porcentaje: 0
    }, {
      proyectoId: this.proyecto.id,
      offsetPeriodo: 3,
      porcentaje: 0
    }];
    if (modalidadCobro) {
      this.modalidadCobro = modalidadCobroTemplate.map(mct => {
        return { ...mct, ...modalidadCobro.find(mc => mc.offsetPeriodo === mct.offsetPeriodo) };
      });
    } else {
      this.modalidadCobro = modalidadCobroTemplate;
    }
  }

  getModalidadDePagoParaPeriodo(modalidadPago, periodo) {
    if (modalidadPago) {
      var modalidadDePagoParaPeriodo = modalidadPago.find(mp => mp.offsetPeriodo === periodo);
    }
    return (modalidadDePagoParaPeriodo && modalidadDePagoParaPeriodo.porcentaje > 0) ?
      `${modalidadDePagoParaPeriodo.porcentaje}%` : "";
  }

  getModalidadDeCobroDescr(offsetPeriodo) {
    return {
      0: "Contado",
      1: "a 30 Dias",
      2: "a 60 Dias",
      3: "a 90 Dias"
    }[offsetPeriodo];
  }

  getCantidadDecisionesTomadas() {
    return this.decisiones ? this.decisiones.filter(d => d.opcionTomada).length : 0;
  }

  getOpcionesTomadas() {
    return this.decisiones.map(d => d.opciones.find(o => o.id === d.opcionTomada)).filter(d => d);
  }

  getAumentoCostoFijo() {
    var aumentoCostoFijo = 0;
    this.getOpcionesTomadas().forEach(o => aumentoCostoFijo = aumentoCostoFijo + o.variacionCostoFijo);
    return aumentoCostoFijo != 0 ? (aumentoCostoFijo % 1 != 0 ? aumentoCostoFijo.toFixed(2) : aumentoCostoFijo) : null;
  }

  getAumentoCostoVariable() {
    var aumentoCostoVariable = this.getProveedorSeleccionado() ? this.getProveedorSeleccionado().variacionCostoVariable : 0;
    this.getOpcionesTomadas().forEach(o => aumentoCostoVariable = aumentoCostoVariable + o.variacionCostoVariable);
    return aumentoCostoVariable != 0 ? (aumentoCostoVariable % 1 != 0 ? aumentoCostoVariable.toFixed(2) : aumentoCostoVariable) : null;
  }

  getAumentoProduccionMensual() {
    var aumentoProduccionMensual = 0;
    this.getOpcionesTomadas().forEach(o => aumentoProduccionMensual = aumentoProduccionMensual + o.variacionProduccion);
    return aumentoProduccionMensual != 0 ? aumentoProduccionMensual : null;
  }

  getAumentoCalidad() {
    var aumentoCalidad = this.getProveedorSeleccionado() ? this.getProveedorSeleccionado().variacionCalidad : 0;
    this.getOpcionesTomadas().forEach(o => aumentoCalidad = aumentoCalidad + o.variacionCalidad);
    return aumentoCalidad != 0 ? aumentoCalidad : null;
  }

  getAumentoPublicidad() {
    var aumentoPublicidad = 0;
    this.getOpcionesTomadas().forEach(o => aumentoPublicidad = aumentoPublicidad + o.variacionPublicidad);
    return aumentoPublicidad != 0 ? aumentoPublicidad : null;
  }

  getAumentoCantidadVendedores() {
    var aumentoCantidadVendedores = 0;
    this.getOpcionesTomadas().forEach(o => aumentoCantidadVendedores = aumentoCantidadVendedores + o.variacionCantidadVendedores);
    return aumentoCantidadVendedores != 0 ? aumentoCantidadVendedores : null;
  }


  simular() {
    if (this.inputsValidos()) {
      //Grabar FORECAST
      this.proyectoService.forecast(this.proyecto.id, this.forecasts)
        .subscribe(_ => {
          //Grabar MODALIDAD COBRO
          this.proyectoService.modalidadCobro(this.proyecto.id, this.modalidadCobro).subscribe(_ => {
            //Grabar PROVEEDORES
            this.proyectoService.proveedorSeleccionado(this.proyecto.id, this.proveedorSeleccionado).subscribe(_ => {
              //Grabar Credito
              this.proyectoService.tomarCredito(this.credito).subscribe(_ => {
                //SIMULAR
                this.proyectoService.simular(this.proyecto.id, this.getOpcionesTomadas())
                  .subscribe(_ => this.router.navigateByUrl(`/simulaciones/escenario/${this.escenario.id}/resultados`))
              })
            })
          })
        });
    }
  }

  _simular() {

  }

  getEvenNumber = function(num) {
    return Math.floor(Number(num)/2) > 0 ? new Array(Math.floor(Number(num)/2)) : new Array(0);
  }

  getOddNumber = function(num) {
    return Number(num) % 2 != 0;
  }

  getTotalActivo() {
    this.totalActivo = this.escenario.balanceInicial.activo.caja + this.escenario.balanceInicial.activo.cuentasPorCobrar + this.escenario.balanceInicial.activo.inventario + this.escenario.balanceInicial.activo.maquinaria + this.escenario.balanceInicial.activo.amortizacionAcumulada;
  }

  getTotalPasivoPatrimonioNeto() {
    this.totalPasivoPatrimonioNeto = this.escenario.balanceInicial.pasivo.proveedores + this.escenario.balanceInicial.pasivo.deudasBancarias + this.escenario.balanceInicial.patrimonioNeto.capitalSocial + this.escenario.balanceInicial.patrimonioNeto.resultadoDelEjercicio;
  }

  inputsValidos() {

    //Validar si se tomaron todas las opciones
    var opcionesTomadas = this.getOpcionesTomadas();
    if (opcionesTomadas.length !== this.decisiones.length) {
      this.messageService.openSnackBar("Toma todas las decisiones antes de simular!");
      return false;
    }

    //Validar si el porcentaje de cobro es 100% en total entre todos los periodos
    var modalidadCobroPorcentajeTotal = this.modalidadCobro.filter(elem => elem.porcentaje > 0)
      .reduce((acum, elem) => acum + elem.porcentaje, 0)
    if (modalidadCobroPorcentajeTotal !== 100) {
      this.messageService.openSnackBar("Los porcentajes en su modalidad de cobro deben sumar 100%!");
      return false;
    }

    //Validar si los valores de forecast de venta son correctos
    var forecastNegativos = this.forecasts.filter(forecast => forecast.cantidadUnidades !== null && forecast.cantidadUnidades < 0)
    if (forecastNegativos.length > 0) {
      this.messageService.openSnackBar("Todas las cantidades vendidas del forecast de venta deben ser numeros positivos");
      return false;
    }

    //Validar Proveedor seleccionado
    if (!this.proveedorSeleccionado) {
      this.messageService.openSnackBar("Por favor seleccione un proveedor");
      return false;
    }

    //Validar Credito: Valores negaticos
    if (this.credito.monto < 0 || this.credito.periodoInicial < 0) {
      this.messageService.openSnackBar("El monto de financiacion y el periodo inicial deben ser numeros positivos");
      return false;
    }

    //Validar Credito seleccionado
    if (this.credito.monto > 0 && !this.credito.financiacionId) {
      this.messageService.openSnackBar("Usted ha dispuesto un monto de financiacion. Por favor seleccione un tipo de credito");
      return false;
    }

     //Validar Credito periodo valido
     if (this.credito.monto > 0 && this.credito.periodoInicial > this.escenario.maximosPeriodos) {
      this.messageService.openSnackBar("El Periodo Inicial del credito no puede ser mayor a los periodos del ejercicio");
      return false;
    }


    return true;
  }

  onSeleccionarProveedor(proveedor) {
    this.proveedorSeleccionado = proveedor.id;
  }

  onSeleccionarFinanciacion(financiacion) {
    this.credito.financiacionId = financiacion.id;
  }

  getProveedorSeleccionado() {
    return this.proveedores.find(p => p.id == this.proveedorSeleccionado);
  }


}
<file_sep>/src/app/flujo-fondos/flujo-fondos.component.html
<table class="mat-table table" *ngIf="flujoFondos">

  <tr class="mat-header-row">
    <th class="mat-header-cell desc-column">Descripcion</th>
    <th *ngFor="let periodo of periodos" class="mat-header-cell value-column"> {{escenario.nombrePeriodos}} {{periodo}} </th>
  </tr>

  <!--INGRESOS_AFECTOS_A_IMPUESTOS-->
  <tr *ngIf="flujoFondos.INGRESOS_AFECTOS_A_IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;" class="desc-cuenta">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p>
        &nbsp;
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, false) }}
      </p>
    </td>
  </tr>

  <!--EGRESOS_AFECTOS_A_IMPUESTOS-->
  <tr *ngIf="flujoFondos.EGRESOS_AFECTOS_A_IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;" class="desc-cuenta">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p>
        &nbsp;
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, true, true) }}
      </p>
    </td>
  </tr>

  <!--GASTOS_NO_DESEMBOLSABLES-->
  <tr *ngIf="flujoFondos.GASTOS_NO_DESEMBOLSABLES as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, true) }}
      </p>
    </td>
  </tr>  

  <!--UTILIDAD_ANTES_DE_IMPUESTOS-->
  <tr *ngIf="flujoFondos.UTILIDAD_ANTES_DE_IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      {{ getCuentaPeriodo(flujo.montosPeriodo,periodo,true, false) }}
    </td>
  </tr>

  <!--IMPUESTOS-->
  <tr *ngIf="flujoFondos.IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      {{ getCuentaPeriodo(flujo.montosPeriodo,periodo,true, true) }}
    </td>
  </tr>

  <!--UTILIDAD_DESPUES_DE_IMPUESTOS-->
  <tr *ngIf="flujoFondos.UTILIDAD_DESPUES_DE_IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      {{ getCuentaPeriodo(flujo.montosPeriodo,periodo,true, false) }}
    </td>
  </tr>

  <!--AJUSTE_DE_GASTOS_NO_DESEMBOLSABLES-->
  <tr *ngIf="flujoFondos.AJUSTE_DE_GASTOS_NO_DESEMBOLSABLES as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, false) }}
      </p>
    </td>
  </tr>

  <!--INGRESOS_NO_AFECTOS_A_IMPUESTOS-->
  <tr *ngIf="flujoFondos.INGRESOS_NO_AFECTOS_A_IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;" class="desc-cuenta">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p>
        &nbsp;
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, false) }}
      </p>
    </td>
  </tr>

  <!--EGRESOS_NO_AFECTOS_A_IMPUESTOS-->
  <tr *ngIf="flujoFondos.EGRESOS_NO_AFECTOS_A_IMPUESTOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;" class="desc-cuenta">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p>
        &nbsp;
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, true) }}
      </p>
    </td>
  </tr>

  <!--INVERSIONES-->
  <tr *ngIf="flujoFondos.INVERSIONES as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ cuenta.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column">
      <p>
        &nbsp;
      </p>
      <p *ngFor="let cuenta of flujo.cuentas;">
        {{ getCuentaPeriodo(cuenta.cuentasPeriodo,periodo,true, true) }}
      </p>
    </td>
  </tr>

  <!--FLUJO_DE_FONDOS-->
  <tr *ngIf="flujoFondos.FLUJO_DE_FONDOS as flujo" class="mat-row">
    <td class="mat-cell ">
      <p class="flujo-column">
        {{ flujo.descripcion }}
      </p>
    </td>
    <td *ngFor="let periodo of periodos;" class="mat-cell value-column flujo-column">
      {{ getCuentaPeriodo(flujo.montosPeriodo,periodo,true, false) }}
    </td>
  </tr>
</table><file_sep>/src/app/escenarios.service.spec.ts
import { TestBed } from '@angular/core/testing';

import { EscenariosService } from './escenarios.service';

describe('EscenariosService', () => {
  beforeEach(() => TestBed.configureTestingModule({}));

  it('should be created', () => {
    const service: EscenariosService = TestBed.get(EscenariosService);
    expect(service).toBeTruthy();
  });
});
<file_sep>/src/app/cursos.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { MessagesService } from './messages.service';
import { environment } from '../environments/environment';
import { catchError, map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class CursosService {

  constructor(private http: HttpClient, private messageService: MessagesService) { }

  getCursos() {
    return this.http.get(`${environment.proyectoServiceHost}/api/curso`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getCurso(cursoId) {
    return this.http.get(`${environment.proyectoServiceHost}/api/curso`)
      .pipe(
        map((cursos: Array<any>) => cursos.find(c => c.id === cursoId)),
        catchError(this.messageService.catchError.bind(this.messageService)));
  }

  removeCurso(curso) {
    return this.http.delete(`${environment.proyectoServiceHost}/api/curso/${curso.id}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  createCurso(curso) {
    return this.http.post(`${environment.proyectoServiceHost}/api/curso`, curso)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  modifyCurso(curso) {
    return this.http.put(`${environment.proyectoServiceHost}/api/curso/${curso.id}`, curso)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

}
<file_sep>/src/app/proyecto.service.ts
import { Injectable, Component } from '@angular/core';
import { environment } from '../environments/environment';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { MessagesService } from './messages.service';
import { descripcionesTipoCuentas, descripcionesTipoFlujoFondos } from './valores-dominio';

@Injectable()
export class ProyectoService {

  constructor(private http: HttpClient,
    private messageService: MessagesService) { }

  getEstado(idProyecto, forecast): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/estado/actual${forecast ? '-forecast' : ''}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getEstados(proyectoId, forecast): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${proyectoId}/estados${forecast ? '-forecast' : ''}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  forecast(idProyecto, forecast) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/forecast`, forecast)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  modalidadCobro(idProyecto, modalidadCobro) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/modalidadCobro`, modalidadCobro)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  proveedorSeleccionado(idProyecto, proveedorSeleccionado) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/proveedor`, proveedorSeleccionado)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  simular(idProyecto, opciones) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/simular-forecast`, opciones)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getFlujoFondos(idProyecto, forecast): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/flujo-fondo${forecast ? '-forecast' : ''}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getPresupuestoFinanciero(idProyecto,forecast): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/presupuesto-financiero${forecast ? '-forecast' : ''}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getPresupuestoEconomico(idProyecto,forecast): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/presupuesto-economico${forecast ? '-forecast' : ''}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getPeriodoActual(idProyecto, forecast) {
    return this.getEstado(idProyecto, forecast).pipe(map(estado => estado.proyecto.escenario.maximosPeriodos))
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getDecisiones(idProyecto): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/decisiones`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getForecast(idProyecto) {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/forecast`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getModalidadCobro(idProyecto) {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/modalidadCobro`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getProveedores(idProyecto): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/proveedor`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getTipoCuentas() {
    return this.http.get(`${environment.proyectoServiceHost}/api/tipoCuentas`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)),
        map(tipoCuentas => tipoCuentas.map(tipoCuenta => {
          return {
            key: tipoCuenta,
            descripcion: descripcionesTipoCuentas[tipoCuenta]
          }
        })));
  }

  getTipoFlujoFondos() {
    return this.http.get(`${environment.proyectoServiceHost}/api/tipoFlujoFondos`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)),
        map(tipoFlujoFondos => tipoFlujoFondos.map(tipoFlujoFondo => {
          return {
            key: tipoFlujoFondo,
            descripcion: descripcionesTipoFlujoFondos[tipoFlujoFondo]
          }
        })));
  }

  getBalanceFinal(idProyecto,forecast) {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/balance-final${forecast ? '-forecast' : ''}`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  getCredito(idProyecto): Observable<any> {
    return this.http.get(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/financiacionTomado`)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  tomarCredito(credito) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proyecto/${credito.proyectoId}/credito`, credito)
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

  entregarProyecto(idProyecto) {
    return this.http.post(`${environment.proyectoServiceHost}/api/proyecto/${idProyecto}/entregar`, {})
      .pipe(catchError(this.messageService.catchError.bind(this.messageService)));
  }

}

<file_sep>/src/app/financiacion.service.spec.ts
import { TestBed } from '@angular/core/testing';

import { FinanciacionService } from './financiacion.service';

describe('FinanciacionService', () => {
  beforeEach(() => TestBed.configureTestingModule({}));

  it('should be created', () => {
    const service: FinanciacionService = TestBed.get(FinanciacionService);
    expect(service).toBeTruthy();
  });
});
<file_sep>/src/app/resultados-admin/resultados-admin.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { EscenariosService } from '../escenarios.service';
import { ActivatedRoute, Router } from '@angular/router';
import { UsuarioService } from '../usuario.service';
import { ProyectoService } from '../proyecto.service';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material';

@Component({
  selector: 'app-resultados',
  templateUrl: './resultados-admin.component.html',
  styleUrls: ['./resultados-admin.component.css']
})
export class ResultadosAdminComponent implements OnInit {
  proyecto;
  escenario;
  decisiones;
  escenarioCurso;
  forecast;
  seccion = "RESULTADOS_FORECAST";
  cursoId;
  usuarioId;

  constructor(private escenarioService: EscenariosService, private route: ActivatedRoute,
    private usuarioService: UsuarioService, private proyectoService: ProyectoService, public dialog: MatDialog, private router: Router) { }

  ngOnInit() {
    var escenarioId = Number(this.route.snapshot.paramMap.get('escenarioId'));
    this.cursoId = Number(this.route.snapshot.paramMap.get('cursoId'));
    this.usuarioId = Number(this.route.snapshot.paramMap.get('usuarioId'));
    this.getEscenario(escenarioId);
    this.getProyecto(escenarioId, this.usuarioId);
    this.getDetalleEscenarioUsuariosPorCurso(escenarioId, this.cursoId);
  }

  getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId) {
    this.escenarioService.getDetalleEscenarioUsuariosPorCurso(escenarioId, cursoId).subscribe(escenarioCurso => {
      this.escenarioCurso = escenarioCurso;
    })
  }

  getProyecto(escenarioId, usuarioId) {
    this.usuarioService.getProyecto(escenarioId, usuarioId).subscribe(estado => {
      this.proyecto = estado.proyecto;
      this.getDecisiones(this.proyecto.id);
      this.getForecast(this.proyecto.id);
    })
  }

  getDecisiones(proyectoId) {
    this.proyectoService.getDecisiones(proyectoId)
      .subscribe(decisiones => this.decisiones = decisiones);
  }

  getEscenario(escenarioId) {
    this.escenarioService.getEscenario(escenarioId).subscribe(escenario => this.escenario = escenario);
  }

  getForecast(proyectoId) {
    this.proyectoService.getForecast(proyectoId).subscribe(forecast => {this.forecast = forecast;
    if(forecast.length <= 0) this.seccion = "RESULTADOS_REALES"});
  }
}
<file_sep>/src/app/home/home.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatSidenav, MatDialog } from '@angular/material';
import { Router } from '@angular/router';
import { AngularFireAuth } from '@angular/fire/auth';
import { UsuarioService } from '../usuario.service';
import { MatriculacionDialogComponent } from '../matriculacion-dialog/matriculacion-dialog.component';
import { MediaMatcher } from '@angular/cdk/layout';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})

export class HomeComponent implements OnInit {
  @ViewChild('sidenav', {
    static: true
  }) sidenav: MatSidenav;

  user: any;
  year = new Date().getFullYear();
  usuario;
  opened = true;
  pendingRequests;
  mobileQuery: MediaQueryList;

  constructor(private router: Router,
    private afAuth: AngularFireAuth,
    private usuarioService: UsuarioService,
    private dialog: MatDialog,
    private media: MediaMatcher) {
    this.mobileQuery = this.media.matchMedia('(max-width: 600px)');
  }

  ngOnInit() {
    this.afAuth.authState.subscribe(user => {
      if (user && !this.user) {
        this.user = user;
        this.getUsuario({
          mail: user.email,
          nombreCompleto: user.displayName || user.email,
          fotoUrl: user.photoURL
        });
      } else {
        // User is not logged in
        this.router.navigateByUrl("/login");
      }
    });


  }

  getUsuario(mail) {
    this.usuarioService.getUsuario(mail).subscribe(usuario => {
      this.usuario = usuario
      if (!usuario.curso && usuario.rol === "JUGADOR") {
        this.matricularUsuario(usuario);
      }
    }
    )
  }

  matricularUsuario(usuario) {
    const dialogRef = this.dialog.open(MatriculacionDialogComponent, {
      width: '400px',
      data: usuario,
      disableClose: true
    });
    dialogRef.afterClosed().subscribe(_ => {
      this.router.navigateByUrl('/simulaciones', { skipLocationChange: true })
      this.getUsuario(usuario)
    });
  }

  close() {
    this.sidenav.close();
  }

  logout() {
    this.afAuth.auth.signOut();
  }


}

