diff --git a/package-lock.json b/package-lock.json index 2b60e7d8..b2dec330 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2761,6 +2761,31 @@ "tslib": "^1.9.0" } }, + "@ngrx/effects": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-8.5.0.tgz", + "integrity": "sha512-Ac60DGM2rV4Xd9DONFuhStGZJMffQF+uL69pd1a144WPtfSWWBWzo5sHeZOKs7O9YmfqXiihs1DfCKmGaJOciw==" + }, + "@ngrx/entity": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@ngrx/entity/-/entity-8.5.0.tgz", + "integrity": "sha512-3s/ULg43C8TPTTHPVwF2RS4/1xngZM9NbwfNARnS1jF/DN1EKh0R0z4OEq42016WsQm/qjGS7k4kq8OrJt6KIA==" + }, + "@ngrx/router-store": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-8.5.0.tgz", + "integrity": "sha512-jbdQAn7xjt8Huo6llaQ53c6JnPT7mhEvF8EeaL+u/zfbdfBgU+nh5Ty2ZSDMvLhdf8JJ1g+QcHKwsWRRkYHXNA==" + }, + "@ngrx/store": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-8.5.0.tgz", + "integrity": "sha512-PF+yi3CjRDkuJ7uPNW/2WAXSIs6iZHpC9RRnvbQ76D7c2qaq5AThnGGhPaB/tEuNZS0wv9z+0IoxeESJOe9jIg==" + }, + "@ngrx/store-devtools": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-8.5.0.tgz", + "integrity": "sha512-v3LPXb9KzK+CDySYBUGl9jxTwQ/mRrgE8cF4e2Fsl72GiBDuSY0ZGapM612hB6w0QMxm+NI50D/jRNwOMqjM2Q==" + }, "@ngtools/webpack": { "version": "8.0.6", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.0.6.tgz", diff --git a/package.json b/package.json index dde2ae95..4516c035 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,11 @@ "@nebular/security": "4.1.2", "@nebular/theme": "4.1.2", "@ng-bootstrap/ng-bootstrap": "^5.3.1", + "@ngrx/effects": "8.5.0", + "@ngrx/entity": "8.5.0", + "@ngrx/router-store": "8.5.0", + "@ngrx/store": "8.5.0", + "@ngrx/store-devtools": "8.5.0", "@ngx-translate/core": "^11.0.1", "@ngx-translate/http-loader": "^4.0.0", "@swimlane/ngx-charts": "^10.0.0", diff --git a/src/app/@core/_custom-validators/custom-validators.ts b/src/app/@core/_custom-validators/custom-validators.ts new file mode 100644 index 00000000..e7877099 --- /dev/null +++ b/src/app/@core/_custom-validators/custom-validators.ts @@ -0,0 +1,19 @@ +import {AbstractControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'; + +function customRequired(msg: string): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + + return Validators.required(control) ? { + required: true, + msg + } : null; + }; +} + +const CustomValidators = { + customRequired +}; + +export { + CustomValidators +}; diff --git a/src/app/@core/_resolver/entities.resolver.ts b/src/app/@core/_resolver/entities.resolver.ts new file mode 100644 index 00000000..8f39b370 --- /dev/null +++ b/src/app/@core/_resolver/entities.resolver.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; +import { Store, select } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { filter, take } from 'rxjs/operators'; +import { fromCuentaActions } from '../_store/actions'; +import { CuentaPartialState } from '../_store/reducer'; +import { selectCuentaLoaded, selectNaturalezaLoaded} from '../_store/selectors'; + + + +@Injectable() +export class CuentasResolver implements Resolve { + constructor(private store: Store) {} + + resolve(): Observable { + const loaded$ = this.store.pipe(select(selectCuentaLoaded)); + + const loadedNaturaleza$ = this.store.pipe(select(selectNaturalezaLoaded)); + + return loaded$.pipe( + filter(loaded => { + if (loaded === false) { + this.store.dispatch(fromCuentaActions.loadCuentas()); + } + + return loaded; + }), + take(1) + ); + } +} diff --git a/src/app/@core/_store/actions.ts b/src/app/@core/_store/actions.ts new file mode 100644 index 00000000..487b78af --- /dev/null +++ b/src/app/@core/_store/actions.ts @@ -0,0 +1,87 @@ +import { createAction, props } from '@ngrx/store'; + +export enum CuentaActionTypes { + LoadCuentas = '[Cuenta] Load Cuentas', + LoadCuentasSuccess = '[Cuenta] Load Cuentas Success', + LoadCuentasFail = '[Cuenta] Load Cuentas Fail', + LoadCuenta = '[Cuenta] Load Cuenta', + LoadCuentaSuccess = '[Cuenta] Load Cuenta Success', + LoadCuentaFail = '[Cuenta] Load Cuenta Fail', + UpdateCuenta = '[Cuenta] Update Cuenta', + UpdateCuentaSuccess = '[Cuenta] Update Cuenta Success', + UpdateCuentaFail = '[Cuenta] Update Cuenta Fail', + LoadNaturalezas = '[Naturaleza] Load Naturaleza', + LoadNaturalezasSuccess = '[Naturaleza] Load Naturalezas Success', + LoadNaturalezasFail = '[Naturaleza] Load Naturalezas Fail', +} + +export const loadCuentas = createAction(CuentaActionTypes.LoadCuentas); + +export const loadCuentasSuccess = createAction( + CuentaActionTypes.LoadCuentasSuccess, + props<{ data: any[] }>() +); + +export const loadCuentasFail = createAction( + CuentaActionTypes.LoadCuentasFail, + props<{ error: Error | any }>() +); + +export const loadCuenta = createAction( + CuentaActionTypes.LoadCuenta, + props<{ id: string | number }>() +); + +export const loadCuentaSuccess = createAction( + CuentaActionTypes.LoadCuentaSuccess, + props<{ id: string | number; item: any }>() +); + +export const loadCuentaFail = createAction( + CuentaActionTypes.LoadCuentaFail, + props<{ error: Error | any }>() +); + +export const updateCuenta = createAction( + CuentaActionTypes.UpdateCuenta, + props<{ id: number | string; originalItem: any; updatedItem: any }>() +); + +export const updateCuentaSuccess = createAction( + CuentaActionTypes.UpdateCuentaSuccess, + props<{ id: number | string; originalItem: any; updatedItem: any }>() +); + +export const updateCuentaFail = createAction( + CuentaActionTypes.UpdateCuentaFail, + props<{ + id: number | string; + originalItem: any; + updatedItem: any; + error: Error | any; + }>() +); + +export const loadNaturalezas = createAction(CuentaActionTypes.LoadNaturalezas); + +export const loadNaturalezasSuccess = createAction( + CuentaActionTypes.LoadNaturalezasSuccess, + props<{ data: any[] }>() +); + +export const loadNaturalezasFail = createAction( + CuentaActionTypes.LoadNaturalezasFail, + props<{ error: Error | any }>() +); + +export const fromCuentaActions = { + loadCuentas, + loadCuentasFail, + loadCuentasSuccess, + loadCuenta, + loadCuentaFail, + loadCuentaSuccess, + loadNaturalezas, + loadNaturalezasFail, + loadNaturalezasSuccess, +}; diff --git a/src/app/@core/_store/effects.ts b/src/app/@core/_store/effects.ts new file mode 100644 index 00000000..2a158c06 --- /dev/null +++ b/src/app/@core/_store/effects.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@angular/core'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { map, switchMap, catchError } from 'rxjs/operators'; + +import { fromCuentaActions } from './actions'; +import { ArbolHelper } from '../helpers/arbol/arbolHelper'; + +@Injectable() +export class CuentaEffects { + loadCuentas$ = createEffect(() => + this.actions$.pipe( + ofType(fromCuentaActions.loadCuentas), + switchMap(() => + this.entityService.getTree().pipe( + map((res: any) => fromCuentaActions.loadCuentasSuccess({ + data: res + }) + ), + catchError(error => + of( + fromCuentaActions.loadCuentasFail({ + error + }) + ) + ) + ) + ) + ) + ); + + loadNaturalezas$ = createEffect(() => + this.actions$.pipe( + ofType(fromCuentaActions.loadNaturalezas), + switchMap(() => + this.entityService.getNaturalezaCuenta().pipe( + map((res: any) => fromCuentaActions.loadNaturalezasSuccess({ + data: res + }) + ), + catchError(error => + of( + fromCuentaActions.loadNaturalezasFail({ + error + }) + ) + ) + ) + ) + ) + ); + + + constructor( + private actions$: Actions, + private entityService: ArbolHelper + ) {} +} diff --git a/src/app/@core/_store/index.ts b/src/app/@core/_store/index.ts new file mode 100644 index 00000000..fff0ed55 --- /dev/null +++ b/src/app/@core/_store/index.ts @@ -0,0 +1,85 @@ +import { InjectionToken } from '@angular/core'; +import { ActionReducerMap, MetaReducer } from '@ngrx/store'; +import * as fromRouter from '@ngrx/router-store'; +import { Params } from '@angular/router'; + +import { + State as CuentaState, + reducer as CuentaReducer, + CUENTA_FEATURE_KEY, + NATURALEZA_FEATURE_KEY +} from './reducer'; +import { CuentaEffects } from './effects'; +import { environment } from '../../../environments/environment'; + +export interface RouterStateUrl { + url: string; + params: Params; + queryParams: Params; +} + +/** + * Reset the state the store on logout + */ +function resetState(reducer) { + return (state, action) => { + // if (action.type === fromUser.logoutSuccess().type) { + // state = undefined; + // } + + return reducer(state, action); + }; +} + +/** + * Every reducer module's default export is the reducer function itself. In + * addition, each module should export a type or interface that describes + * the state of the reducer plus any selector functions. The `* as` + * notation packages up all of the exports into a single object. + */ + +// import * as fromLayout from './layout'; + +/** + * As mentioned, we treat each reducer like a table in a database. This means + * our top level state interface is just a map of keys to inner state types. + */ +export interface AppState { + router: fromRouter.RouterReducerState; + [CUENTA_FEATURE_KEY]: CuentaState; + [NATURALEZA_FEATURE_KEY]: CuentaState; +} + +/** + * Our state is composed of a map of action reducer functions. + * These reducer functions are called with each dispatched action + * and the current or initial state and return a new immutable state. + */ +const reducers: ActionReducerMap = { + router: fromRouter.routerReducer, + [CUENTA_FEATURE_KEY]: CuentaReducer, + [NATURALEZA_FEATURE_KEY]: CuentaReducer +}; + +/** + * By default, @ngrx/store uses combineReducers with the reducer map to compose + * the root meta-reducer. To add more meta-reducers, provide an array of meta-reducers + * that will be composed to form the root meta-reducer. + */ +const metaReducers: MetaReducer[] = !environment.production + ? [resetState] + : [resetState]; + +export const getMetaReducers = (): MetaReducer[] => metaReducers; + +/** + * We need to inject reducers to our application to make AOT build worked + */ +export const REDUCER_TOKEN = new InjectionToken>( + 'Registered Reducers', + { + factory: () => reducers + } +); + +export const appEffects = [CuentaEffects]; diff --git a/src/app/@core/_store/reducer.ts b/src/app/@core/_store/reducer.ts new file mode 100644 index 00000000..d53ddcb8 --- /dev/null +++ b/src/app/@core/_store/reducer.ts @@ -0,0 +1,69 @@ +import { createReducer, on, Action } from '@ngrx/store'; +import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; + +import { fromCuentaActions } from './actions'; + +export const CUENTA_FEATURE_KEY = 'Cuenta'; +export const NATURALEZA_FEATURE_KEY = 'naturaleza'; + +export interface State extends EntityState { + loaded: boolean; + error?: Error | any; +} + +export const adapter: EntityAdapter = createEntityAdapter({ + // In this case this would be optional since primary key is id + selectId: item => item.data.Codigo, +}); + +export interface CuentaPartialState { + readonly [CUENTA_FEATURE_KEY]: State; + readonly [NATURALEZA_FEATURE_KEY]: State; +} + +export const initialState: State = adapter.getInitialState({ + // Additional entity state properties + loaded: false, + error: null +}); + +const _reducer = createReducer( + initialState, + on(fromCuentaActions.loadCuentasSuccess, (state, { data }) => { + return adapter.addMany(data, { + ...state, + loaded: true + }); + }), + on(fromCuentaActions.loadCuentasFail, (state, { error }) => { + return { + ...state, + error + }; + }), + on(fromCuentaActions.loadCuentaSuccess, (state, { id, item }) => { + return adapter.addOne(item, state); + }), + on(fromCuentaActions.loadCuentaFail, (state, { error }) => { + return { + ...state, + error + }; + }), + on(fromCuentaActions.loadNaturalezasSuccess, (state, { data }) => { + return adapter.addMany(data, { + ...state, + loaded: true + }); + }), + on(fromCuentaActions.loadNaturalezasFail, (state, { error }) => { + return { + ...state, + error + }; + }), +); + +export function reducer(state: State | undefined, action: Action) { + return _reducer(state, action); +} diff --git a/src/app/@core/_store/selectors.ts b/src/app/@core/_store/selectors.ts new file mode 100644 index 00000000..df0a2c77 --- /dev/null +++ b/src/app/@core/_store/selectors.ts @@ -0,0 +1,51 @@ +import { createFeatureSelector, createSelector } from '@ngrx/store'; + +import { State, adapter, CUENTA_FEATURE_KEY, NATURALEZA_FEATURE_KEY } from './reducer'; + +// Lookup the 'Cuenta' feature state managed by NgRx +const getCuentaState = createFeatureSelector(CUENTA_FEATURE_KEY); +const getNaturalezaState = createFeatureSelector(NATURALEZA_FEATURE_KEY); + +// get the selectors +const { selectIds, selectAll, selectTotal } = adapter.getSelectors(); + +// select the array of Cuenta ids +export const selectCuentaIds = createSelector( + getCuentaState, + selectIds +); + +// select the array of Cuentas +export const selectAllCuentas = createSelector( + getCuentaState, + selectAll +); + +// select the total Cuenta count +export const selectCuentaCount = createSelector( + getCuentaState, + selectTotal +); + +// select entity loaded flag +export const selectCuentaLoaded = createSelector( + getCuentaState, + state => state.loaded +); + +export const selectNaturalezaLoaded = createSelector( + getNaturalezaState, + state => state.loaded +); + + +// select entity error +export const selectError = createSelector( + getCuentaState, + state => state.error +); + +export const selectAllNaturalezas = createSelector( + getNaturalezaState, + selectAll +); diff --git a/src/app/@theme/components/dinamicform/dinamicform.component.scss b/src/app/@theme/components/dinamicform/dinamicform.component.scss index 93ee47e8..f6959790 100755 --- a/src/app/@theme/components/dinamicform/dinamicform.component.scss +++ b/src/app/@theme/components/dinamicform/dinamicform.component.scss @@ -22,9 +22,6 @@ background-color: nb-theme(form-control-border-color); } - nb-card-body { - overflow: visible; - } .input-group { margin-bottom: 1rem; diff --git a/src/app/@theme/components/index.ts b/src/app/@theme/components/index.ts index b5153993..42741ae0 100644 --- a/src/app/@theme/components/index.ts +++ b/src/app/@theme/components/index.ts @@ -5,3 +5,6 @@ export * from './tiny-mce/tiny-mce.component'; export * from './dinamicform/dinamicform.component'; export * from './crud/list-component/list.component'; export * from './crud/crud-component/crud.component'; +export * from './tree-icon/nb-fs-icon.component'; +export * from './reactive-form/reactive-form.component'; +export * from './reactive-form/reactive-control/reactive-control.component'; diff --git a/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.html b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.html new file mode 100644 index 00000000..83b6eef4 --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.html @@ -0,0 +1,50 @@ + + + {{data.label}} + + + + {{option[data.optionList.labelKey]}} + + + + {{control.errors.msg || data.errorMsg || '' }} + + {{data.hintMsg}} + + + + {{data.label}} + + {{data.prefix}} + + {{control.errors.msg || data.errorMsg || '' }} + + {{data.hintMsg}} + + +
+ {{data.label}} + + {{control.errors.msg || data.errorMsg || '' }} + +
+ + + {{data.label}} + + + {{option[data.optionList.labelKey]}} + + + + {{control.errors.msg || data.errorMsg || '' }} + + + + +
diff --git a/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.scss b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.scss new file mode 100644 index 00000000..f692df8c --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.scss @@ -0,0 +1,3 @@ +.form-group { + width: 100%; +} diff --git a/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.spec.ts b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.spec.ts new file mode 100644 index 00000000..e950b270 --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReactiveControlComponent } from './reactive-control.component'; + +describe('ReactiveControlComponent', () => { + let component: ReactiveControlComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ReactiveControlComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReactiveControlComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.ts b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.ts new file mode 100644 index 00000000..9b70feaf --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-control/reactive-control.component.ts @@ -0,0 +1,100 @@ +import { FormControl } from '@angular/forms'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ReactiveFormControl } from '../reactive-form-structure'; +import { map, startWith } from 'rxjs/operators'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'ngx-reactive-control', + templateUrl: './reactive-control.component.html', + styleUrls: ['./reactive-control.component.scss'] +}) +export class ReactiveControlComponent implements OnInit { + + private _data: ReactiveFormControl; + + @Input('data') + public get data(): ReactiveFormControl { + return this._data; + } + public set data(value: ReactiveFormControl) { + this._data = value; + if (value && this.control) { + this.buildControl(); + } + } + + @Input('control') control: FormControl; + + @Output() controlChange ?: EventEmitter < FormControl > = new EventEmitter(); + + filterList: Observable < any[] > ; + selectList: any[]; + + constructor() {} + + ngOnInit() { + this.buildControl(); + } + + private buildControl() { + if (this.data) { + + switch (this.data.type) { + case 'autocomplete': + this.buildAutocomplete(this.data); + break; + case 'input': + break; + case 'button': + this.buildButton(this.data); + break; + case 'select': + this.buildSelect(this.data); + break; + default: + break; + } + this.control.setValidators(this.data.validators); + this.controlChange.emit(this.control); + } + } + + buildAutocomplete(item: ReactiveFormControl) { + this.filterList = this.control.valueChanges.pipe( + startWith(''), + map(value => { + if (item.valueChanges) item.valueChanges(this.control.parent); + return item.optionList.elements(this.control.parent).filter(option => { + const labelKey: string = option[item.optionList.labelKey] || option; + const valueKey: string = ( value && typeof value === 'object' ) ? value[item.optionList.labelKey] : value; + return labelKey.toLowerCase().includes(valueKey ? valueKey.toLowerCase() : ''); + }); + }), + ); + } + + focusAutocomplete() { + if (!this.control.value) { + this.control.setValue(''); + } + this.control.setValue(this.control.value); + } + + dislayAutocomplete = (value) => { + return ( value && typeof value === 'object' ) ? value[this.data.optionList.labelKey] : value; + } + + buildSelect(item: ReactiveFormControl) { + this.selectList = item.optionList.elements(); + if (this.control.value && !item.optionList.valueid) { + this.control.setValue(this.selectList.find(option => option[item.optionList.idKey] === this.control.value[item.optionList.idKey])); + } + } + + buildButton(item: ReactiveFormControl) { + const formControl = new FormControl(item.label); + this.control.setValue(item.label); + } + +} diff --git a/src/app/@theme/components/reactive-form/reactive-form-structure.ts b/src/app/@theme/components/reactive-form/reactive-form-structure.ts new file mode 100644 index 00000000..63f9ef84 --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-form-structure.ts @@ -0,0 +1,53 @@ +import { ValidatorFn } from '@angular/forms'; +import { MatFormFieldAppearance } from '@angular/material'; + +export interface ReactiveFormStructure { + cols?: number; + controls: (ReactiveFormGroup | ReactiveFormControl)[]; + appareance ?: MatFormFieldAppearance; + validators ?: ValidatorFn[]; +} + +export interface ReactiveFormGroup { + class ?: string; + title ?: string; + key: string; + validators ?: ValidatorFn[]; + appareance ?: MatFormFieldAppearance; + controls: (ReactiveFormGroup | ReactiveFormControl)[]; +} + +export interface ReactiveFormControl { + type: FormControlType; + defaultValue ?: string; + class ?: string; + label ?: string; + key: string; + validators ?: ValidatorFn[]; + appareance ?: MatFormFieldAppearance; + optionList ?: OptionList; + controls ?: null; + errorMsg ?: string; + placeholder ?: string; + inputType ?: FormControlInputType; + hintMsg ?: string; + buttonAction ?: (FormGroup) => void; + disabled ?: (FormGroup) => boolean; + valueChanges ?: (FormGroup) => void; + prefix?: string; +} + +export type FormControlType = 'autocomplete' | 'input' | 'date' | 'checkbox' | 'radio' | 'select' | 'button'; + +export type FormControlInputType = 'button' | 'checkbox' | 'color' | 'date' | 'datetime-local' | 'email' | 'file' | 'hidden' | 'image' | 'month' | 'number' | 'password' | 'radio' | 'range' | 'reset' | 'search' | 'submit' | 'tel' | 'text' | 'time' | 'url' | 'week'; + +export interface OptionList { + elements: (parent ?: any) => Array < any > ; + labelKey?: string; + idKey?: string; + valueid?: boolean; +} + +export function isGroup(object: any): object is ReactiveFormGroup { + return !!object.controls; +} diff --git a/src/app/@theme/components/reactive-form/reactive-form.component.html b/src/app/@theme/components/reactive-form/reactive-form.component.html new file mode 100644 index 00000000..61f9f343 --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-form.component.html @@ -0,0 +1,23 @@ + +
+
+ + +
+
+
+ + +
+ + + + +
+
+ + + + diff --git a/src/app/@theme/components/reactive-form/reactive-form.component.scss b/src/app/@theme/components/reactive-form/reactive-form.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/@theme/components/reactive-form/reactive-form.component.spec.ts b/src/app/@theme/components/reactive-form/reactive-form.component.spec.ts new file mode 100644 index 00000000..3f57a608 --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-form.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { ReactiveFormComponent } from './reactive-form.component'; + +describe('ReactiveFormComponent', () => { + let component: ReactiveFormComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ReactiveFormComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReactiveFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/@theme/components/reactive-form/reactive-form.component.ts b/src/app/@theme/components/reactive-form/reactive-form.component.ts new file mode 100644 index 00000000..063ea55c --- /dev/null +++ b/src/app/@theme/components/reactive-form/reactive-form.component.ts @@ -0,0 +1,76 @@ +import { FormBuilder, FormGroup, FormControl } from '@angular/forms'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ReactiveFormControl, ReactiveFormStructure, ReactiveFormGroup, isGroup } from './reactive-form-structure'; + +@Component({ + selector: 'ngx-reactive-form', + templateUrl: './reactive-form.component.html', + styleUrls: ['./reactive-form.component.scss'] +}) +export class ReactiveFormComponent implements OnInit { + private _reactiveForm: FormGroup; + private _value: any = {}; + private _dataForm: ReactiveFormStructure; + + @Input('dataForm') + public get dataForm(): ReactiveFormStructure { + return this._dataForm; + } + public set dataForm(value: ReactiveFormStructure) { + this._dataForm = value; + this.build(); + } + + @Input('value') + public get value(): FormGroup { + return this._value; + } + public set value(value: FormGroup) { + this._value = value; + } + + @Input('reactiveForm') + public get reactiveForm(): FormGroup { + return this._reactiveForm; + } + public set reactiveForm(value: FormGroup) { + this._reactiveForm = value; + this.reactiveFormChange.emit(this._reactiveForm); + } + + @Output() reactiveFormChange: EventEmitter < FormGroup > = new EventEmitter(); + + constructor(private builder: FormBuilder) {} + + ngOnInit() { + this.build(); + } + + build() { + const valueForm = this.reactiveForm ? this.reactiveForm.value : {}; + this.reactiveForm = this.builderForm(this.dataForm); + this.reactiveForm.setValue({ + ...this.reactiveForm.value, + ...this._value, + ...valueForm + }); + } + + builderForm(form: ReactiveFormStructure) { + const node = this.buildElement(form.controls); + return this.builder.group(node); + } + + private buildElement(controls: (ReactiveFormControl | ReactiveFormGroup)[]) { + const node = {}; + controls.forEach(element => { + if (isGroup(element)) { + node[element.key] = this.builder.group(this.buildElement(element.controls)); + } else { + node[element.key] = new FormControl(); + } + }); + return node; + } + +} diff --git a/src/app/@theme/components/tree-icon/nb-fs-icon.component.ts b/src/app/@theme/components/tree-icon/nb-fs-icon.component.ts new file mode 100644 index 00000000..3dcc7279 --- /dev/null +++ b/src/app/@theme/components/tree-icon/nb-fs-icon.component.ts @@ -0,0 +1,33 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ngx-nb-fs-icon', + template: ` + + + + + +   + `, + }) + export class FsIconAComponent { + @Input() kind: string; + + @Input() expanded: boolean; + + isDir(): boolean { + return this.kind === 'dir'; + } + + isDoc(): boolean { + return this.kind === 'doc'; + } + } diff --git a/src/app/@theme/styles/themes.scss b/src/app/@theme/styles/themes.scss index d4129a6a..c060586d 100644 --- a/src/app/@theme/styles/themes.scss +++ b/src/app/@theme/styles/themes.scss @@ -114,3 +114,85 @@ $nb-themes: nb-register-theme(( text-disabled-color: #8f9bb3, actions-icon-color: #dd9900, ), kronos, default); + +// $nb-themes: nb-register-theme(( + +// color-primary-100: #FDF4CA, +// color-primary-200: #FBE696, +// color-primary-300: #F4D061, +// color-primary-400: #EABA3A, +// color-primary-500: #DD9900, +// color-primary-600: #BE7D00, +// color-primary-700: #9F6400, +// color-primary-800: #804C00, +// color-primary-900: #6A3C00, + +// color-success-100: #F0FDD1, +// color-success-200: #DEFBA5, +// color-success-300: #C3F476, +// color-success-400: #A8EA53, +// color-success-500: #81DD1F, +// color-success-600: #64BE16, +// color-success-700: #4B9F0F, +// color-success-800: #358009, +// color-success-900: #266A05, + +// color-info-100: #CBF3FE, +// color-info-200: #98E2FD, +// color-info-300: #64CAFB, +// color-info-400: #3EB1F8, +// color-info-500: #008AF4, +// color-info-600: #006AD1, +// color-info-700: #004FAF, +// color-info-800: #00388D, +// color-info-900: #002875, + +// color-warning-100: #FEF9CC, +// color-warning-200: #FEF399, +// color-warning-300: #FEE966, +// color-warning-400: #FDE040, +// color-warning-500: #FCD202, +// color-warning-600: #D8B001, +// color-warning-700: #B59001, +// color-warning-800: #927200, +// color-warning-900: #785C00, + +// color-danger-100: #FFECD2, +// color-danger-200: #FFD3A4, +// color-danger-300: #FFB478, +// color-danger-400: #FF9656, +// color-danger-500: #FF651E, +// color-danger-600: #DB4715, +// color-danger-700: #B72E0F, +// color-danger-800: #931909, +// color-danger-900: #7A0B05, + +// /* Basic colors - for backgrounds and borders and texts */ + +// color-basic-100: #ffffff, +// color-basic-200: #f7f9fc, +// color-basic-300: #edf1f7, +// color-basic-400: #e4e9f2, +// color-basic-500: #c5cee0, +// color-basic-600: #ffffff, +// color-basic-700: #2e3a59, +// color-basic-800: #222b45, +// color-basic-900: #1a2138, +// color-basic-1000: #151a30, +// color-basic-1100: #101426, + +// color-gray-500: #7f7f7f, +// color-green-ok: color-success-800, +// color-green-ok-title: color-success-700, +// color-red-invalid: color-danger-600, +// menu-background-color: color-primary-500, +// sidebar-background-color: color-primary-500, +// menu-item-hover-background-color: color-primary-900, +// menu-text-color: color-basic-100, +// //background-basic-color-1: #dd9900, +// context-menu-border-color: #0000, +// text-basic-color: #101426, +// text-disabled-color: #8f9bb3, +// actions-icon-color: color-primary-500, + +// ), kronos-propuesta, default); diff --git a/src/app/@theme/theme.module.ts b/src/app/@theme/theme.module.ts index 50d390ee..206765fd 100644 --- a/src/app/@theme/theme.module.ts +++ b/src/app/@theme/theme.module.ts @@ -30,6 +30,9 @@ import { DinamicformComponent, ListEntityComponent, CrudEntityComponent, + FsIconAComponent, + ReactiveFormComponent, + ReactiveControlComponent } from './components'; import { CapitalizePipe, @@ -116,6 +119,9 @@ const COMPONENTS = [ ListEntityComponent, CrudEntityComponent, PDFviewerComponent, + FsIconAComponent, + ReactiveFormComponent, + ReactiveControlComponent ]; const PIPES = [ CapitalizePipe, @@ -181,7 +187,7 @@ const NB_THEME_PROVIDERS = [ @NgModule({ imports: [SharedModule, CommonModule, FormsModule, ReactiveFormsModule, ...NB_MODULES, ...MAT_MODULES, Ng2SmartTableModule, CurrencyMaskModule], exports: [CommonModule, FormsModule, ReactiveFormsModule, TranslateModule, ...PIPES, ...COMPONENTS, ...MAT_MODULES], - declarations: [...COMPONENTS, ...PIPES, DinamicformComponent, ListEntityComponent, CrudEntityComponent], + declarations: [...COMPONENTS, ...PIPES, DinamicformComponent, FsIconAComponent, ListEntityComponent, CrudEntityComponent], }) export class ThemeModule { static forRoot(): ModuleWithProviders { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c3c857d2..b7c07efa 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -77,6 +77,12 @@ import { CdkTreeModule } from '@angular/cdk/tree'; import { CdkTableModule } from '@angular/cdk/table'; import { ReactiveFormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms'; +import { StoreModule } from '@ngrx/store'; +import { appEffects, REDUCER_TOKEN } from './@core/_store'; +import { StoreRouterConnectingModule } from '@ngrx/router-store'; +import { environment } from '../environments/environment'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http, './assets/i18n/', '.json'); } @@ -166,6 +172,17 @@ export class MaterialModule {} deps: [HttpClient], }, }), + StoreModule.forRoot(REDUCER_TOKEN), + StoreRouterConnectingModule.forRoot(), + ...(environment.production + ? [] + : [ + StoreDevtoolsModule.instrument({ + name: 'CRUD Application', + logOnly: environment.production + }) + ]), + EffectsModule.forRoot([...appEffects]) ], bootstrap: [AppComponent], providers: [ImplicitAutenticationService, diff --git a/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.html b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.html new file mode 100644 index 00000000..392785be --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.html @@ -0,0 +1,29 @@ +
+ + + + + + + + + + + + + + + + + +
+ {{customColumn}} + + + {{row.data[customColumn]}} + + {{column}} + {{row.data[column] || '-'}}
+
diff --git a/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.scss b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.scss new file mode 100644 index 00000000..c44d11c1 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.scss @@ -0,0 +1,51 @@ +.accounting-tree { + table { + tr { + &.selected, + &.selected:hover { + background-color: lightblue; + } + + &:hover { + background: #e6e6e6; + } + } + } + + .search-label { + display: block; + } + + .search-input { + margin-bottom: 1rem; + } + + @media screen and (min-width: 400px) { + + .nb-column-name, + .nb-column-size { + width: 50%; + } + } + + @media screen and (min-width: 500px) { + + .nb-column-name, + .nb-column-size, + .nb-column-kind { + width: 33.333%; + } + } + + @media screen and (min-width: 600px) { + .nb-column-name { + width: 31%; + } + + .nb-column-size, + .nb-column-kind, + .nb-column-items { + width: 23%; + } + } +} diff --git a/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.spec.ts b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.spec.ts new file mode 100644 index 00000000..83c86db5 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { ArbolContableComponent } from './arbol-contable.component'; + +describe('ArbolContableComponent', () => { + let component: ArbolContableComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ArbolContableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ArbolContableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.ts b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.ts new file mode 100644 index 00000000..d99ee0f3 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/arbol-contable/arbol-contable.component.ts @@ -0,0 +1,74 @@ +import { selectAllCuentas } from './../../../@core/_store/selectors'; +import { Component, EventEmitter, OnInit, Output} from '@angular/core'; +import { NbGetters, NbSortDirection, NbTreeGridDataSource, NbTreeGridDataSourceBuilder, NbSortRequest } from '@nebular/theme'; +import { EstructuraCuentaContable } from './cuenta-contable.model'; +import { Store, select } from '@ngrx/store'; + +@Component({ + selector: 'ngx-arbol-contable', + templateUrl: './arbol-contable.component.html', + styleUrls: ['./arbol-contable.component.scss'], +}) +export class ArbolContableComponent implements OnInit { + + @Output() onSelection: EventEmitter = new EventEmitter(); + + customColumn = 'Codigo'; + defaultColumns = ['Nombre', 'Activo']; + allColumns = [this.customColumn, ...this.defaultColumns]; + dataSource: NbTreeGridDataSource < EstructuraCuentaContable > ; + sortColumn: string; + sortDirection: NbSortDirection = NbSortDirection.NONE; + data: any; + selectedNode: EstructuraCuentaContable; + + dataTree = this.store.pipe(select(selectAllCuentas)); + + constructor( + private dataSourceBuilder: NbTreeGridDataSourceBuilder < EstructuraCuentaContable >, + private store: Store + ) {} + + ngOnInit() { + this.loadTree(); + } + + loadTree() { + const getters: NbGetters < any, any > = { + dataGetter: (node: any) => node.data || undefined, + childrenGetter: (node: any) => + ( !!node.children && !!node.children.length ) ? node.children : [], + expandedGetter: (node: any) => !!node.expanded, + }; + + this.dataTree.subscribe(res => { + this.dataSource = this.dataSourceBuilder.create(res, getters); + this.data = res; + }); + + } + + onSelect(row) { + this.selectedNode = row.data; + this.onSelection.emit(this.selectedNode ); + } + + updateSort(sortRequest: NbSortRequest): void { + this.dataSource.sort(sortRequest); + this.sortColumn = sortRequest.column; + this.sortDirection = sortRequest.direction; + } + + getSortDirection(column: string): NbSortDirection { + if (this.sortColumn === column) { + return this.sortDirection; + } + return NbSortDirection.NONE; + } + + getShowOn(index: number) { + const minWithForMultipleColumns = 400; + const nextColumnStep = 100; + return minWithForMultipleColumns + nextColumnStep * index; + } +} diff --git a/src/app/pages/arbol-cuentas-contables/arbol-contable/cuenta-contable.model.ts b/src/app/pages/arbol-cuentas-contables/arbol-contable/cuenta-contable.model.ts new file mode 100644 index 00000000..64a13bfe --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/arbol-contable/cuenta-contable.model.ts @@ -0,0 +1,15 @@ +export interface EstructuraCuentaContable { + Codigo: string; + Nombre?: string; + ValorInicial: number; + Hijos?: EstructuraCuentaContable[]; + Movimientos?: string[]; + Padre?: string; + UnidadEjecutora: number; + Estado?: string; + IsLeaf: boolean; + expanded?: boolean; + isHighlighted?: boolean; + data?: EstructuraCuentaContable; + children?: EstructuraCuentaContable[]; +} diff --git a/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.html b/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.html index d15b0152..08cd37d9 100644 --- a/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.html +++ b/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.html @@ -1,69 +1,39 @@ - - {{ 'CUENTAS_CONTABLES.arbol_cuentas_contables' | translate }} - - -
-
-
- -
-
- + +
+
+

+ {{ 'CUENTAS_CONTABLES.arbol_cuentas_contables' | translate }} +

+ +
+
+
+ + +
+ + +
+ +
+
+ + +
+
-
- +
+ {{tab.title}}
-
- +
+
+ +
+
-
- - - - - - - - - - - - - - -
- {{ customColumn }} - - - {{ row.data[customColumn] }} - - {{ column }} - - {{ column === "Activo" ? (row.data[column] ? "Si" : "No" ): (row.data[column] || "-") }} -
-
- - - - - \ No newline at end of file + + +
\ No newline at end of file diff --git a/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.scss b/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.scss index 2b2f9bcf..eebfbbdd 100644 --- a/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.scss +++ b/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.scss @@ -1,61 +1,23 @@ -button[nbTreeGridRowToggle] { - background: transparent; - border: none; - padding: 0; +.aligned { + display: flex; + justify-content: center; } -.table-row { - &:hover { - background: #e6e6e6; - } +.floating { + margin-bottom: 20px; + position: sticky; + top: 50px; } -:host ::ng-deep table tr td { - padding: -0.125rem 1.25rem !important; - font-size: 12px !important; -} -.aligned-code { - padding-left: 8px; -} -.search-label { - display: block; -} -.search-input { - margin-bottom: 1rem; -} -.nb-column-name { +:host ::ng-deep .mat-tab-label-content { width: 100%; -} - -@media screen and (min-width: 400px) { - .nb-column-name, - .nb-column-size { - width: 50%; + align-content: space-between; + .tab-title { + padding: 0 10px; + width: 100%; + text-align: center; + word-break: break-word; } -} -@media screen and (min-width: 500px) { - .nb-column-name, - .nb-column-size, - .nb-column-kind { - width: 33.333%; - } } - -@media screen and (min-width: 600px) { - .nb-column-name { - width: 31%; - } - .nb-column-size, - .nb-column-kind, - .nb-column-items { - width: 23%; - } -} - - -.moneyField { - text-align: right; -} - diff --git a/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.ts b/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.ts index 46b77277..92c840ad 100644 --- a/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.ts +++ b/src/app/pages/arbol-cuentas-contables/arbol-cuentas-contables.component.ts @@ -1,395 +1,93 @@ -import { Component, Input, EventEmitter, Output, OnInit, OnChanges, ViewChildren, ElementRef, Renderer2 } from '@angular/core'; -import { - NbGetters, - NbSortDirection, - NbTreeGridRowComponent, - NbTreeGridDataSource, - NbTreeGridDataSourceBuilder, - NbSortRequest, -} from '@nebular/theme'; -import { Observable } from 'rxjs'; -// import { ArbolHelper } from '../../../@core/helpers/arbol/arbolHelper'; -// import { RubroHelper } from '../../../@core/helpers/rubros/rubroHelper'; +import { EstructuraCuentaContable } from './arbol-contable/cuenta-contable.model'; +import { Component, OnInit } from '@angular/core'; import { registerLocaleData } from '@angular/common'; import locales from '@angular/common/locales/es-CO'; -import { ArbolHelper } from '../../@core/helpers/arbol/arbolHelper'; -import { FormManager } from '../../@core/managers/formManager'; -import { FORM_NODO_CUENTA_CONTABLE } from './form_nodo_cuenta_contable'; -import { TranslateService } from '@ngx-translate/core'; -import { PopUpManager } from '../../@core/managers/popUpManager'; registerLocaleData(locales, 'co'); -interface EstructuraArbolRubrosApropiaciones { - Codigo: string; - Descripcion?: string; - ValorInicial: number; - Hijos?: EstructuraArbolRubrosApropiaciones[]; - Movimientos?: string[]; - Padre?: string; - UnidadEjecutora: number; - Estado?: string; - IsLeaf: boolean; - expanded?: boolean; - isHighlighted?: boolean; - data?: EstructuraArbolRubrosApropiaciones; - children?: EstructuraArbolRubrosApropiaciones[]; -} @Component({ selector: 'ngx-arbol-cuentas-contables', templateUrl: './arbol-cuentas-contables.component.html', styleUrls: ['./arbol-cuentas-contables.component.scss'], }) -export class ArbolCuentasContablesComponent implements OnInit, OnChanges { - @Output() rubroSeleccionado = new EventEmitter(); - @Input() updateSignal: Observable; - @Input() optionSelect: string; - @Input() vigencia: string; - @Input() externalSearch: string; - @Input('paramsFieldsName') paramsFieldsName: object; - opcionSeleccionada: string; - vigenciaSeleccionada: string; - @ViewChildren(NbTreeGridRowComponent, { read: ElementRef }) treeNodes: ElementRef[]; - - update: any; - customColumn = 'Codigo'; - defaultColumns = ['Nombre']; - hasListener: any[] = []; - oldHighlight: ElementRef; - - allColumns = [this.customColumn, ...this.defaultColumns]; - dataSource: NbTreeGridDataSource; - dataSource2: NbTreeGridDataSource; - - sortColumn: string; - sortDirection: NbSortDirection = NbSortDirection.NONE; - idHighlight: any; - isSelected: boolean; - searchValue: string; - formData: any; - nodeData: any; - selectedNodeData: any; - - cleanForm: boolean; - - showTree: boolean = true; - viewTab: boolean = false; - - cuentaAlterna: any; - cuentaAlternaAnt: number; - - constructor( - private renderer: Renderer2, - private dataSourceBuilder: NbTreeGridDataSourceBuilder, - // private dataSourceBuilder2: NbTreeGridDataSourceBuilder, - private treeHelper: ArbolHelper, - private translate: TranslateService, - private pUpManager: PopUpManager, - // private rubroHelper: RubroHelper, - ) { - } - - ngOnInit() { - - this.loadTree(); - - this.formData = FORM_NODO_CUENTA_CONTABLE; - this.nodeData = undefined; - this.construirForm(); - } - construirForm() { - this.formData.btn = this.translate.instant('GLOBAL.guardar'); - for (let i = 0; i < this.formData.campos.length; i++) { - this.formData.campos[i].label = this.formData.campos[i].label_i18n; - this.formData.campos[i].placeholder = this.formData.campos[i].label_i18n; - } - - const naturalezaIndex = FormManager.getIndexForm(this.formData, 'NaturalezaCuentaID'); - const tipoMonedaIndex = FormManager.getIndexForm(this.formData, 'MonedaID'); - const detalleCuentaIndex = FormManager.getIndexForm(this.formData, 'DetalleCuentaID'); - const centroCostosIndex = FormManager.getIndexForm(this.formData, 'CentroDecostosID'); - - this.treeHelper.getNaturalezaCuenta().subscribe(res => { - this.formData.campos[naturalezaIndex].opciones = res; - }); - - this.treeHelper.getTipoMoneda().subscribe(res => { - this.formData.campos[tipoMonedaIndex].opciones = res; - }); - - this.treeHelper.getDetalleCuenta().subscribe(res => { - this.formData.campos[detalleCuentaIndex].opciones = res; - }); - - this.treeHelper.getCentroCostos().subscribe(res => { - this.formData.campos[centroCostosIndex].opciones = res; - }); - - - } - ngOnChanges(changes) { - if (changes['updateSignal'] && this.updateSignal) { - this.updateSignal.subscribe(() => { - this.loadTree(); - }); - } - if (changes['externalSearch'] && changes['externalSearch'].currentValue) { - this.searchValue = changes['externalSearch'].currentValue; - } - if (changes['paramsFieldsName'] && changes['paramsFieldsName'].currentValue) { - this.paramsFieldsName = changes['paramsFieldsName'].currentValue; - } - } - - // private data: TreeNode[] | TreeNode[]; - - private data: []; - - loadTree() { - const getters: NbGetters = { - dataGetter: (node: any) => node.data || undefined, - childrenGetter: (node: any) => !!node.children && !!node.children.length ? node.children : [], - expandedGetter: (node: any) => !!node.expanded, - }; - this.customColumn = 'Codigo'; - this.defaultColumns = ['Nombre', 'Activo']; - this.allColumns = [this.customColumn, ...this.defaultColumns]; - this.treeHelper.getTree(true).subscribe(res => { - this.data = res; - this.dataSource = this.dataSourceBuilder.create(this.data, getters); - }); - } - updateTreeSignal($event) { - this.loadTree(); - } - - updateSort(sortRequest: NbSortRequest): void { - this.sortColumn = sortRequest.column; - this.sortDirection = sortRequest.direction; - } - - getSortDirection(column: string): NbSortDirection { - if (this.sortColumn === column) { - return this.sortDirection; - } - return NbSortDirection.NONE; - } - - async onSelect(selectedItem: any, treegrid) { - this.idHighlight = treegrid.elementRef.nativeElement.getAttribute('data-picker'); - this.rubroSeleccionado.emit(selectedItem.data); - this.selectedNodeData = selectedItem.data; - - } - - cleanInterface() { - this.loadTree(); - this.cleanForm = !this.cleanForm; - // this.selectedNodeData = undefined; - this.formData.campos[FormManager.getIndexForm(this.formData, 'Codigo')].prefix.value = ''; - this.nodeData = undefined; - } - - getShowOn(index: number) { - const minWithForMultipleColumns = 400; - const nextColumnStep = 100; - return minWithForMultipleColumns + nextColumnStep * index; - } - - updateHighlight(newHighlight: ElementRef, row) { - this.oldHighlight && this.renderer.setStyle(this.oldHighlight.nativeElement, 'background', 'white'); - if (row.Codigo === this.idHighlight) { - this.renderer.setStyle(newHighlight.nativeElement, 'background', 'lightblue'); +export class ArbolCuentasContablesComponent implements OnInit { + + defaultOptions = [{ + key: 'crear', + label: 'crear', + color: 'yellow', + icon: 'plus' + }]; + + selectedOptions = [ + { + key: 'ver', + label: 'ver', + color: 'yellow', + icon: 'glasses' + }, + { + key: 'editar', + label: 'editar', + color: 'yellow', + icon: 'edit' + }, + { + key: 'eliminar', + label: 'eliminar', + color: 'yellow', + icon: 'trash' } - this.oldHighlight = newHighlight; - } + ]; - validHighlight(selectedItem: any, treegrid) { + menu = { + direction: 'row', + style: 'basic-dark', + expanded: true, + options: [ + ...this.defaultOptions + ] + }; - if (selectedItem.data.Codigo === this.idHighlight) { - this.updateHighlight(treegrid.elementRef, selectedItem.data); - return true; - } - return false; - } + tabs = []; - validarCampo($event) { - // Cambios por campo - switch ($event.nombre) { - // Cuando se cambia el campo de cuenta alterna, se oculta o muestran el campo de cód y nombre - case 'CuentaAlterna': { - const codCuenta = this.formData.campos[FormManager.getIndexForm(this.formData, 'CodigoCuentaAlterna')]; - const nomCuenta = this.formData.campos[FormManager.getIndexForm(this.formData, 'NombreCuentaAlterna')]; - if ($event.valor !== undefined && $event.valor.Id) { - codCuenta.claseGrid = codCuenta.claseGrid.replaceAll(' d-none', ''); - nomCuenta.claseGrid = nomCuenta.claseGrid.replaceAll(' d-none', ''); - codCuenta.requerido = true; - } else { - this.cuentaAlterna = null; - codCuenta.claseGrid += ' d-none'; - nomCuenta.claseGrid += ' d-none'; - codCuenta.requerido = false; - } - break; - } - // Cuando se cambia el cód de cuenta, se debe buscar la cuenta - case 'CodigoCuentaAlterna': { - if (this.cuentaAlternaAnt !== $event.valor.toString()) { - this.cuentaAlternaAnt = $event.valor; - const nomCuenta = this.formData.campos[FormManager.getIndexForm(this.formData, 'NombreCuentaAlterna')]; - const cAlterna = $event.valor.toString(); - let cA = ''; - // Cuenta númerica a cuenta con guiones - if (cAlterna.length >= 6) { - cA = cAlterna[0] + '-' + cAlterna[1] + '-' + cAlterna.substring(2, 4) + '-' + cAlterna.substring(4, 6); - if (cAlterna.length >= 8) - cA += '-' + cAlterna.substring(6, 8); - } - // Valores para cuenta inválida o no encontrada - this.cuentaAlterna = null; - nomCuenta.requerido = true; - nomCuenta.valor = ''; - nomCuenta.alerta = 'Cuenta inválida o no encontrada'; - // Solicitud de datos de cuenta - this.treeHelper.getInfoCuenta(cA, false).subscribe(res => { - if (res && res !== undefined && res.Codigo && res.Nombre) { - // Valores para cuenta válida - this.cuentaAlterna = res.Codigo; - nomCuenta.requerido = false; - nomCuenta.valor = res.Codigo + ' ' + res.Nombre; - nomCuenta.alerta = null; - } - }); - } - break; - } - } - } - - validarForm($event) { - if (!$event.valid) return; - const nodeData = $event.data.NodoCuentaContable; - - nodeData['DetalleCuentaID'] = nodeData['DetalleCuentaID']['Id']; - nodeData['MonedaID'] = nodeData['MonedaID']['Id']; - nodeData['NaturalezaCuentaID'] = nodeData['NaturalezaCuentaID']['Id']; - nodeData['CentroDecostosID'] = nodeData['CentroDecostosID']['Id']; - nodeData['Ajustable'] = nodeData['Ajustable']['Id']; - nodeData['RequiereTercero'] = nodeData['RequiereTercero']['Id']; - nodeData['Nmnc'] = nodeData['Nmnc']['Id']; - nodeData['CodigoCuentaAlterna'] = this.cuentaAlterna !== null ? this.cuentaAlterna.replaceAll('-', '') : null; - nodeData['Codigo'] = nodeData['Codigo'] + ''; - nodeData['Activo'] = nodeData['Activa']['Id']; - if (this.selectedNodeData) { - nodeData['Padre'] = this.selectedNodeData['Codigo']; - } - if (this.nodeData) { - this.treeHelper.updateNode($event.data.NodoCuentaContable.Codigo, $event.data.NodoCuentaContable).subscribe(res => { - if (res) { - this.pUpManager.showAlert('success', 'Cuenta contable', 'Cuenta actualizada correctamente'); - this.cleanInterface(); - this.showTreeTab(); - } - }); - } else { - - this.treeHelper.addNode($event.data.NodoCuentaContable).subscribe(res => { - if (res) { - this.pUpManager.showAlert('success', 'Cuenta contable', 'Cuenta registrada correctamente'); - this.cleanInterface(); - this.showTreeTab(); - } - }); - } + tabItemsActions = []; + selected: number = 0; + selectedCoumt: EstructuraCuentaContable; + constructor() {} + ngOnInit() { } - showViewTab(option = '') { - this.showTree = false; - this.viewTab = true; - this.formData.campos[FormManager.getIndexForm(this.formData, 'Codigo')].prefix.value = this.selectedNodeData ? this.selectedNodeData.Codigo + '-' : ''; - switch (option) { - case 'group': - this.selectedNodeData = undefined; - this.formData.campos[FormManager.getIndexForm(this.formData, 'Codigo')].prefix.value = ''; + selectAction(action: string) { + let node = {}; + switch (action) { + case 'delete': + return; + case 'crear': + node = { title: 'Nueva Cuenta', type: action}; break; - default: + node = {title: `${this.selectedCoumt.Codigo} ${this.selectedCoumt.Nombre}`, data: this.selectedCoumt, type: action, code: this.selectedCoumt.Codigo}; break; } - } - - makeFormEditable(editable: boolean, update = false) { - for (let i = 0; i < this.formData.campos.length; i++) { - this.formData.campos[i].deshabilitar = editable; + const index: number = this.tabs.findIndex(item => item.type === action && (action === 'crear' || item.code === this.selectedCoumt.Codigo) ); + if (index < 0 ) { + this.selected = this.tabs.push(node); + return; } - // always "Codigo" field is disabled. - if (update === true) { - const codigoIndex = FormManager.getIndexForm(this.formData, 'Codigo'); - this.formData.campos[codigoIndex].deshabilitar = true; - } - this.formData.campos[FormManager.getIndexForm(this.formData, 'NombreCuentaAlterna')].deshabilitar = true; - } - - showTreeTab() { - this.showTree = true; - this.viewTab = false; + this.selected = index + 1; } - getTreeInfo() { - this.treeHelper.getInfoCuenta(this.selectedNodeData.Codigo).subscribe(res => { - this.nodeData = res; - const naturalezaIndex = FormManager.getIndexForm(this.formData, 'NaturalezaCuentaID'); - const tipoMonedaIndex = FormManager.getIndexForm(this.formData, 'MonedaID'); - const detalleCuentaIndex = FormManager.getIndexForm(this.formData, 'DetalleCuentaID'); - const centroCostosIndex = FormManager.getIndexForm(this.formData, 'CentroDecostosID'); - this.nodeData['DetalleCuentaID'] = this.formData.campos[detalleCuentaIndex].opciones.find(element => element.Id === this.nodeData['DetalleCuentaID']); - this.nodeData['CentroDecostosID'] = this.formData.campos[centroCostosIndex].opciones.find(element => element.Id === this.nodeData['CentroDecostosID']); - this.nodeData['MonedaID'] = this.formData.campos[tipoMonedaIndex].opciones.find(element => element.Id === this.nodeData['MonedaID']); - this.nodeData['NaturalezaCuentaID'] = this.formData.campos[naturalezaIndex].opciones.find(element => element.Id === this.nodeData['NaturalezaCuentaID']); - this.nodeData['Ajustable'] = this.nodeData['Ajustable'] === true ? { Label: 'Si', Id: true } : { Label: 'No', Id: false }; - this.nodeData['RequiereTercero'] = this.nodeData['RequiereTercero'] === true ? { Label: 'Si', Id: true } : { Label: 'No', Id: false }; - this.nodeData['Nmnc'] = this.nodeData['Nmnc'] === true ? { Label: 'Si', Id: true } : { Label: 'No', Id: false }; - this.nodeData['CuentaAlterna'] = - this.nodeData['CodigoCuentaAlterna'] !== null && this.nodeData['CodigoCuentaAlterna'] !== '' ? { Label: 'Si', Id: true } : { Label: 'No', Id: false }; - this.nodeData['Activa'] = this.nodeData['Activo'] === true ? { Label: 'Si', Id: true } : { Label: 'No', Id: false }; - }); + removeTab(index: number) { + this.tabs.splice(index, 1); } -} - - - -@Component({ - selector: 'ngx-nb-fs-icon', - template: ` - - - - - -    - `, -}) -export class FsIconAComponent { - @Input() kind: string; - - @Input() expanded: boolean; - - isDir(): boolean { - return this.kind === 'dir'; + onSelectNode(e) { + this.selectedCoumt = e; + this.menu.options = [...this.defaultOptions, ...this.selectedOptions]; } - isDoc(): boolean { - return this.kind === 'doc'; - } } diff --git a/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.html b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.html new file mode 100644 index 00000000..0db86046 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.html @@ -0,0 +1,142 @@ + + + + + + +
+ + + + + Codigo + + {{prefix}}- + + {{form.controls['Codigo'].errors.msg || 'ingrese un dato valido' }} + + Ingrese el código consecutivo + + + + +
+ Disponible +
+
+ + + + Nombre + + + {{form.controls['Nombre'].errors.msg || 'ingrese un dato valido' }} + + Ingrese el nombre de la cuenta + + + + + + Naturaleza + + {{naturaleza.Label}} + + + {{form.controls['NaturalezaCuentaID'].errors.msg || 'ingrese un dato valido' }} + + + + + + + Tipo Cuenta + + {{tipo.Label}} + + + {{form.controls['TipoCuenta'].errors.msg || 'ingrese un dato valido' }} + + + + + + + Detalle + + {{detalle.Label}} + + + {{form.controls['DetalleCuentaID'].errors.msg || 'ingrese un dato valido' }} + + + + + +
+ Requiere Tercero +
+
+ + +
+ Ajustable +
+
+ + +
+ NMNC +
+
+ + +
+ Tiene Centro de Costos +
+
+ + + + Centro De costos + + {{centro.Label}} + + + {{form.controls['CentroDecostosID'].errors.msg || 'ingrese un dato valido' }} + + + + + +
+ Requiere Banco +
+
+ + + + Banco + + + {{form.controls['Banco'].errors.msg || 'ingrese un dato valido' }} + + + + + + + Moneda + + {{moneda.Label}} + + + {{form.controls['MonedaID'].errors.msg || 'ingrese un dato valido' }} + + + +
+ +
+
+
diff --git a/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.scss b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.scss new file mode 100644 index 00000000..f711737d --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.scss @@ -0,0 +1,6 @@ +.cuenta-contable { + .form-group { + width: 100%; + padding: 0 10px; + } +} diff --git a/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.spec.ts b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.spec.ts new file mode 100644 index 00000000..42442365 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { CuentaContableComponent } from './cuenta-contable.component'; + +describe('CuentaContableComponent', () => { + let component: CuentaContableComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CuentaContableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CuentaContableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.ts b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.ts new file mode 100644 index 00000000..bd73b2df --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/cuenta-contable/cuenta-contable.component.ts @@ -0,0 +1,121 @@ +import { SelectorContableComponent } from './../selector-contable/selector-contable.component'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { FormCuentaContable} from './form_nodo_cuenta_contable'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { forkJoin } from 'rxjs'; +import { ArbolHelper } from '../../../@core/helpers/arbol/arbolHelper'; +import { PopUpManager } from '../../../@core/managers/popUpManager'; + +@Component({ + selector: 'ngx-cuenta-contable', + templateUrl: './cuenta-contable.component.html', + styleUrls: ['./cuenta-contable.component.scss'] +}) +export class CuentaContableComponent implements OnInit { + + @Input() cuenta; + + @ViewChild(SelectorContableComponent, { + static: false + }) + private selectorContable!: SelectorContableComponent; + + formCuenta = new FormCuentaContable(this.builder); + form: FormGroup = this.formCuenta.getForm(); + prefix: string; + naturalezas = []; + detalleCuentas = []; + tipoMonedas = []; + centroCostos = []; + tipoCuentas = [{ + Id: 'activos', + Label: 'Activos' + }]; + + constructor(private builder: FormBuilder, + private acountService: ArbolHelper, + private pUpManager: PopUpManager, + ) {} + + ngOnInit() { + this.buildData(); + this.loadParams(); + } + + private loadParams() { + forkJoin([ + this.acountService.getNaturalezaCuenta(), + this.acountService.getDetalleCuenta(), + this.acountService.getTipoMoneda(), + this.acountService.getCentroCostos() + ]).subscribe(results => { + this.naturalezas = results[0]; + this.detalleCuentas = results[1]; + this.tipoMonedas = results[2]; + this.centroCostos = results[3]; + }); + } + + private buildData() { + if (this.cuenta) { + const codes = this.cuenta.Codigo.split('-'); + const code = codes.pop(); + this.prefix = `${codes.join('-')}`; + this.acountService.getInfoCuenta(this.cuenta.Codigo).subscribe(res => { + this.cuenta = res; + this.form.patchValue({ + ...res, + Codigo: code + }); + }); + } + } + + setSelectedCount(account) { + if (account && account.data) { + const data = account.data; + this.prefix = data.Codigo; + } + } + + addNode(node) { + this.acountService.addNode(node).subscribe(res => { + if (res) { + this.pUpManager.showAlert('success', 'Cuenta contable', 'Cuenta registrada correctamente'); + this.form.reset(); + } + }); + } + + updateNode(node, code) { + const acount = { + ...this.cuenta, + ...node, + }; + if (!this.prefix) { + delete acount['Padre']; + } + this.acountService.updateNode(code, acount).subscribe(res => { + if (res) { + this.pUpManager.showAlert('success', 'Cuenta contable', 'Cuenta actualizada correctamente'); + } + }); + } + + onSubmit() { + if (this.form.valid && this.selectorContable.form.valid) { + const code = `${this.prefix ? this.prefix + '-' : '' }${this.form.value.Codigo}`; + const newAccount = { + ...this.form.value, + Nivel: code.split('-').length, + Padre: this.prefix, + }; + this.cuenta ? this.updateNode(newAccount, code) : this.addNode(newAccount); + + } else { + this.form.markAllAsTouched(); + this.selectorContable.form.markAllAsTouched(); + } + } + +} diff --git a/src/app/pages/arbol-cuentas-contables/cuenta-contable/form_nodo_cuenta_contable.ts b/src/app/pages/arbol-cuentas-contables/cuenta-contable/form_nodo_cuenta_contable.ts new file mode 100644 index 00000000..5420b67c --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/cuenta-contable/form_nodo_cuenta_contable.ts @@ -0,0 +1,30 @@ +import { FormBuilder, Validators, FormGroup } from '@angular/forms'; + +export class FormCuentaContable { + + formAcount: FormGroup = this.builder.group({ + Codigo: ['', [Validators.required, Validators.pattern('^[0-9]*$')]], + Activo: [true], + Nombre: ['', Validators.required], + NaturalezaCuentaID: ['', Validators.required], + TipoCuenta: ['', Validators.required], + DetalleCuentaID: ['', Validators.required], + TieneCentroCostos: [false], + CentroDecostosID: ['', Validators.required], + RequiereTercero: [false], + Tercero: [''], + RequiereBanco: [false], + Banco: [''], + Ajustable: [false], + Nmnc: [false], + MonedaID: ['', Validators.required], + }); + + constructor(private builder: FormBuilder, + ) {} + + public getForm(): FormGroup { + return this.formAcount; + } + +} diff --git a/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.html b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.html new file mode 100644 index 00000000..af555c49 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.html @@ -0,0 +1,10 @@ +
+
+ + +
+
diff --git a/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.scss b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.scss new file mode 100644 index 00000000..81b361de --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.scss @@ -0,0 +1,53 @@ +@import '../../../@theme/styles/themes'; + +.float-menu { + overflow: auto; + display: flex; + align-content: flex-end; + + .item { + .menu-icon { + * { + font-size: 1.6rem; + } + + text-align: center; + } + + .menu-label { + padding: 0 15px; + font-size: 1.2rem; + } + + display: flex; + flex-direction: row; + flex-flow: wrap; + cursor: pointer; + align-items: center; + align-self: flex-end; + padding: 10px; + margin: 4px; + border-radius: 15px; + &:hover { + box-shadow: 3px 1px 6px 0 #000000; + } + + } + + &.primary { + .item { + color: nb-theme(menu-text-color); + background-color: nb-theme(color-primary-900); + } + + } + + &.basic-dark { + .item { + color: nb-theme(color-primary-500); + background-color: nb-theme(color-basic-1100); + } + + } + +} diff --git a/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.spec.ts b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.spec.ts new file mode 100644 index 00000000..f517dbd6 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FloatMenuComponent } from './float-menu.component'; + +describe('FloatMenuComponent', () => { + let component: FloatMenuComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FloatMenuComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FloatMenuComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.ts b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.ts new file mode 100644 index 00000000..7916a0c1 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/float-menu/float-menu.component.ts @@ -0,0 +1,34 @@ +import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core'; + +export interface MenuType { + direction: 'column' | 'row'; + style: 'primary' | 'basic-dark'; + expanded: boolean; + options: { + showLabel: boolean; + key: string; + label: string; + color: string; + icon: string; + }[]; +} + +@Component({ + selector: 'ngx-float-menu', + templateUrl: './float-menu.component.html', + styleUrls: ['./float-menu.component.scss'] +}) +export class FloatMenuComponent implements OnInit { + + @Input() menu: MenuType; + @Output() itemClick: EventEmitter = new EventEmitter(); + constructor() { } + + ngOnInit() { + } + + onSelect(e) { + this.itemClick.emit(e); + } + +} diff --git a/src/app/pages/arbol-cuentas-contables/form_nodo_cuenta_contable.ts b/src/app/pages/arbol-cuentas-contables/form_nodo_cuenta_contable.ts deleted file mode 100644 index 1dc9adc7..00000000 --- a/src/app/pages/arbol-cuentas-contables/form_nodo_cuenta_contable.ts +++ /dev/null @@ -1,183 +0,0 @@ -const activeData = { Label: 'Si', Id: true }; -const unActiveData = { Label: 'No', Id: false }; -export let FORM_NODO_CUENTA_CONTABLE = { - - tipo_formulario: 'mini', - alertas: true, - modelo: 'NodoCuentaContable', - campos: [ - { - etiqueta: 'input', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'Codigo', - label_i18n: 'Código', - placeholder_i18n: 'Código', - requerido: true, - pattern: '^[1-9]{1,9}', - tipo: 'text', - prefix: { - value: '', - }, - }, - { - etiqueta: 'select', - claseGrid: 'col-md-12', - nombre: 'Activa', - label_i18n: 'Activa', - placeholder_i18n: 'Activa', - requerido: true, - tipo: 'Activa', - key: 'Label', - opciones: [ - activeData, - unActiveData, - ], - }, - { - etiqueta: 'input', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'Nombre', - label_i18n: 'Nombre', - placeholder_i18n: 'Nombre del Rubro', - requerido: true, - tipo: 'text', - }, - - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'DetalleCuentaID', - label_i18n: 'Detalle Cuenta', - placeholder_i18n: 'Detalle Cuenta', - requerido: true, - tipo: 'Detalle Cuenta', - key: 'Label', - opciones: [ - { Label: 1 }, - { Label: 2 }, - { Label: 3 } - ], - }, - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'NaturalezaCuentaID', - label_i18n: 'Naturaleza', - placeholder_i18n: 'Naturaleza', - requerido: true, - tipo: 'NaturalezaCuentaID', - key: 'Label', - opciones: [ - { ID: 1, Label: 2 }, - { ID: 2 , Label: 2}, - { ID: 3 , Label: 2} - ], - }, - { - etiqueta: 'select', - claseGrid: 'col-md-6', - nombre: 'CuentaAlterna', - label_i18n: 'Cuenta Alterna', - placeholder_i18n: 'Cuenta Alterna', - requerido: true, - tipo: 'CuentaAlterna', - key: 'Label', - opciones: [ - activeData, - unActiveData, - ], - }, - { - etiqueta: 'input', - claseGrid: 'col-md-6', - nombre: 'CodigoCuentaAlterna', - label_i18n: 'Código alterno', - placeholder_i18n: 'Código alterno', - requerido: true, - pattern: '^[0-9]{1,9}', - tipo: 'number', - }, - { - etiqueta: 'input', - claseGrid: 'col-md-12', - nombre: 'NombreCuentaAlterna', - label_i18n: 'Nombre cuenta alterna', - placeholder_i18n: 'Nombre cuenta', - requerido: false, - tipo: 'text', - }, - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'Ajustable', - label_i18n: 'Ajustable', - placeholder_i18n: 'Ajustable', - requerido: true, - tipo: 'Ajustable', - key: 'Label', - opciones: [ - activeData, - unActiveData, - ], - }, - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'MonedaID', - label_i18n: 'Tipo Moneda', - placeholder_i18n: 'Tipo Moneda', - requerido: true, - tipo: 'MonedaID', - key: 'Label', - opciones: [ - { Label: 1 }, - { Label: 2 }, - { Label: 3 } - ], - }, - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'RequiereTercero', - label_i18n: 'Requiere tercero', - placeholder_i18n: 'Requiere tercero', - requerido: true, - tipo: 'RequiereTercero', - key: 'Label', - opciones: [ - activeData, - unActiveData, - ], - }, - - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'CentroDecostosID', - label_i18n: 'Centro de costos', - placeholder_i18n: 'Centro de costos', - requerido: true, - tipo: 'CentroDecostosID', - key: 'Label', - opciones: [ - { Label: 1 }, - { Label: 2 }, - ], - }, - - { - etiqueta: 'select', - claseGrid: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', - nombre: 'Nmnc', - label_i18n: 'NMNC', - placeholder_i18n: 'NMNC', - requerido: true, - tipo: 'Nmnc', - key: 'Label', - opciones: [ - activeData, - unActiveData, - ], - }, - ], -}; diff --git a/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.html b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.html new file mode 100644 index 00000000..1c215201 --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.html @@ -0,0 +1,20 @@ + + +
+
+ +
+
+
+ + + + + +
\ No newline at end of file diff --git a/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.scss b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.spec.ts b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.spec.ts new file mode 100644 index 00000000..352a491e --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SelectorContableComponent } from './selector-contable.component'; + +describe('SelectorContableComponent', () => { + let component: SelectorContableComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SelectorContableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SelectorContableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.ts b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.ts new file mode 100644 index 00000000..64c9655d --- /dev/null +++ b/src/app/pages/arbol-cuentas-contables/selector-contable/selector-contable.component.ts @@ -0,0 +1,179 @@ +import { log } from 'console'; +import { ReactiveFormControl, ReactiveFormGroup } from './../../../@theme/components/reactive-form/reactive-form-structure'; +import { Component, OnInit, ViewEncapsulation, ChangeDetectionStrategy, Output, EventEmitter, Input } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; +import { select, Store } from '@ngrx/store'; +import { CustomValidators } from '../../../@core/_custom-validators/custom-validators'; +import { selectAllCuentas } from '../../../@core/_store/selectors'; +import { ReactiveFormStructure } from '../../../@theme/components/reactive-form/reactive-form-structure'; + +@Component({ + selector: 'ngx-selector-contable', + templateUrl: './selector-contable.component.html', + styleUrls: ['./selector-contable.component.scss'], +}) +export class SelectorContableComponent implements OnInit { + + dataTree = this.store.pipe(select(selectAllCuentas)); + cuentasList = []; + form = new FormGroup({}); + formValue: any = {}; + status = false; + normaContable = [{ + key: 'grupo', + label: 'Grupo', + parent: null, + child: 'cuenta', + }, + { + key: 'cuenta', + label: 'Cuenta', + parent: 'grupo', + child: 'subcuenta', + }, + { + key: 'subcuenta', + label: 'Subcuenta', + parent: 'cuenta', + child: 'auxiliar', + }, + { + key: 'auxiliar', + label: 'Auxiliar', + parent: 'subcuenta', + child: 'subauxiliar', + }, + { + key: 'subauxiliar', + label: 'Subauxiliar', + parent: 'auxiliar', + child: null, + }, + ]; + + selectorForm: ReactiveFormStructure; + + @Output() selectAcount: EventEmitter < any > = new EventEmitter(); + + @Input() account: string; + + @Input() claseMinima: number; + + constructor(private store: Store < any > ) {} + + ngOnInit() { + this.dataTree.subscribe((res) => { + this.cuentasList = res; + }); + try { + this.setAcount(); + this.buildForm(); + } catch (error) { + console.error(error); + } + + this.form.valueChanges.subscribe((value) => { + + const control = this.selectorForm.controls[this.selectorForm.controls.length - 1]; + if (value && control ) { + this.selectAcount.emit(value[control.key]); + } + }); + } + + public setAcount() { + if (this.account) { + const codigos = this.account.split('-'); + this.claseMinima = codigos.length; + codigos.forEach((codigo, index) => { + if (index === 0) { + this.formValue[this.normaContable[index].key] = this.cuentasList.find((element) => { + element.Nombre = `${element.data.Codigo} ${element.data.Nombre}`; + return element.data.Codigo === codigo; + }); + } else { + this.formValue[this.normaContable[index].key] = this.formValue[this.normaContable[index].parent].children.find( + (element) => { + + element.Nombre = `${element.data.Codigo} ${element.data.Nombre}`; + return element.data.Codigo === codigos.slice(0, index + 1).join('-'); + } + ); + } + }); + } + } + + buildForm() { + this.selectorForm = { + controls: this.normaContable.slice(0, this.claseMinima).map((item, index) => { + const control = this.createItemSelector(item); + const formControl = new FormControl(this.formValue[item.key]); + if (index === 0) { + (control as any).optionList.elements = () => + this.cuentasList.map((element) => ({ + ...element, + Nombre: `${element.data.Codigo} ${element.data.Nombre}`, + })); + } + this.form.addControl(control.key, formControl); + return control; + }), + }; + } + + addControl() { + const norma = this.normaContable[this.selectorForm.controls.length]; + if (norma) { + const control = this.createItemSelector(norma); + this.selectorForm = { + controls: [ + ...this.selectorForm.controls, + control + ] + }; + this.form.addControl(control.key, new FormControl('')); + this.form.controls[norma.parent].valueChanges.subscribe((value) => { + if (value) { + this.form.get(norma.key).setValue(''); + } + }); + } + } + + deleteControl() { + const norma = this.normaContable[this.selectorForm.controls.length - 1]; + if (norma && this.selectorForm.controls.length > this.claseMinima) { + this.selectorForm.controls.pop(); + this.form.removeControl(norma.key); + } + } + + createItemSelector(item): (ReactiveFormGroup | ReactiveFormControl) { + return { + key: item.key, + type: 'autocomplete', + label: item.label, + defaultValue: '', + placeholder: item.label, + validators: [CustomValidators.customRequired('Campo Requerido')], + optionList: { + elements: (parent) => + parent && + parent.value[item.parent] && + parent.value[item.parent].children ? + parent.value[item.parent].children.map( + (element) => ({ + ...element, + Nombre: `${element.data.Codigo} ${element.data.Nombre}`, + }) + ) : [], + labelKey: 'Nombre', + }, + valueChanges: (parent) => { + if (parent.get(item.child) && typeof parent.get(item.key).value !== 'object') parent.get(item.child).setValue(''); + } + }; + } + +} diff --git a/src/app/pages/conceptos/cuentas-contables/cuentas-contables.component.scss b/src/app/pages/conceptos/cuentas-contables/cuentas-contables.component.scss index 974d5710..bc2c7cfa 100644 --- a/src/app/pages/conceptos/cuentas-contables/cuentas-contables.component.scss +++ b/src/app/pages/conceptos/cuentas-contables/cuentas-contables.component.scss @@ -39,7 +39,6 @@ } nb-list { text-align: left; - overflow: scroll; height: 40rem; } nb-list-item { diff --git a/src/app/pages/conceptos/wizard/wizard.component.scss b/src/app/pages/conceptos/wizard/wizard.component.scss index 54ca79d2..0e6b5dac 100644 --- a/src/app/pages/conceptos/wizard/wizard.component.scss +++ b/src/app/pages/conceptos/wizard/wizard.component.scss @@ -3,9 +3,7 @@ display: none !important; } -:host ::ng-deep nb-card-body { - overflow: hidden; -} + .nbButton-close { position: absolute; right: 0; diff --git a/src/app/pages/pages-routing.module.ts b/src/app/pages/pages-routing.module.ts index 0e67420a..a766e5c9 100644 --- a/src/app/pages/pages-routing.module.ts +++ b/src/app/pages/pages-routing.module.ts @@ -7,7 +7,10 @@ import { ListTipoComprobanteComponent } from './comprobantes/list-tipo-comproban import { ListComprobanteComponent } from './comprobantes/list-comprobante/list-comprobante.component'; import { ArbolCuentasContablesComponent } from './arbol-cuentas-contables/arbol-cuentas-contables.component'; import { ConceptosComponent } from './conceptos/conceptos.component'; +import { ArbolContableComponent } from './arbol-cuentas-contables/arbol-contable/arbol-contable.component'; +import { CuentasResolver } from '../@core/_resolver/entities.resolver'; +export const entityResolvers = [CuentasResolver]; const routes: Routes = [ { @@ -29,7 +32,14 @@ const routes: Routes = [ }, { path: 'arbol_cuentas_contables', - component: ArbolCuentasContablesComponent + component: ArbolCuentasContablesComponent, + resolve: { + cuentas: CuentasResolver + } + }, + { + path: 'arbol-contable', + component: ArbolContableComponent }, { path: 'conceptos', diff --git a/src/app/pages/pages.component.ts b/src/app/pages/pages.component.ts index 51691a97..ff80c3ef 100644 --- a/src/app/pages/pages.component.ts +++ b/src/app/pages/pages.component.ts @@ -86,6 +86,16 @@ export class PagesComponent implements OnInit { key: itemMenu.Nombre, children: this.mapMenuChildrenObject(itemMenu.Opciones) }; + if (this.object.title === 'plan_cuentas') { + this.object.children.push({ + title: 'arbol-contable', + icon: '', + link: `/pages/arbol-contable`, + home: false, + key: 'arbol-contable', + children: null + }); + } this.menu.push(this.object); }); } diff --git a/src/app/pages/pages.module.ts b/src/app/pages/pages.module.ts index 1a00fb7d..b7334a2d 100644 --- a/src/app/pages/pages.module.ts +++ b/src/app/pages/pages.module.ts @@ -22,7 +22,7 @@ import { MatStepperModule } from '@angular/material'; import { CurrencyMaskModule } from 'ng2-currency-mask'; import { ThemeModule } from '../@theme/theme.module'; import { PagesComponent } from './pages.component'; -import { PagesRoutingModule } from './pages-routing.module'; +import { entityResolvers, PagesRoutingModule } from './pages-routing.module'; import { MiscellaneousModule } from './miscellaneous/miscellaneous.module'; import { ConfiguracionService } from '../@core/data/configuracion.service'; import { MenuService } from '../@core/data/menu.service'; @@ -31,7 +31,7 @@ import { NbIconModule } from '@nebular/theme'; import { ListTipoComprobanteComponent } from './comprobantes/list-tipo-comprobante/list-tipo-comprobante.component'; import { ListComprobanteComponent } from './comprobantes/list-comprobante/list-comprobante.component'; import { ParametrosComprobanteComponent } from './comprobantes/parametros-comprobante/parametros-comprobante.component'; -import { ArbolCuentasContablesComponent, FsIconAComponent } from './arbol-cuentas-contables/arbol-cuentas-contables.component'; +import { ArbolCuentasContablesComponent } from './arbol-cuentas-contables/arbol-cuentas-contables.component'; import { ConceptosComponent } from './conceptos/conceptos.component'; import { WizardComponent } from './conceptos/wizard/wizard.component'; import { CuentasContablesComponent } from './conceptos/cuentas-contables/cuentas-contables.component'; @@ -42,6 +42,10 @@ import { ProvisionesComponent } from './provisiones/provisiones.component'; import { ConciliacionesComponent } from './conciliaciones/conciliaciones.component'; import { RegistroNominaComponent } from './registro-nomina/registro-nomina.component'; import { InformesContablesComponent } from './informes-contables/informes-contables.component'; +import { CuentaContableComponent } from './arbol-cuentas-contables/cuenta-contable/cuenta-contable.component'; +import { ArbolContableComponent } from './arbol-cuentas-contables/arbol-contable/arbol-contable.component'; +import { FloatMenuComponent } from './arbol-cuentas-contables/float-menu/float-menu.component'; +import { SelectorContableComponent } from './arbol-cuentas-contables/selector-contable/selector-contable.component'; const MODULES = [ @@ -79,7 +83,6 @@ const COMPONENTS = [ ListComprobanteComponent, ParametrosComprobanteComponent, ArbolCuentasContablesComponent, - FsIconAComponent, ConceptosComponent, WizardComponent, CuentasContablesComponent, @@ -87,12 +90,16 @@ const COMPONENTS = [ EditModalComponent, ProvisionesComponent, ConciliacionesComponent, + CuentaContableComponent, + ArbolContableComponent, + CuentasContablesComponent ]; const ENTRY_COMPONENTS = [ ListTipoComprobanteComponent, ListComprobanteComponent, CuentasContablesComponent, - EditModalComponent + EditModalComponent, + CuentaContableComponent ]; const SERVICES = [ ConfiguracionService, @@ -108,13 +115,12 @@ const SERVICES = [ TableComponent, RegistroNominaComponent, InformesContablesComponent, - - - - + FloatMenuComponent, + SelectorContableComponent, ], providers: [ ...SERVICES, + entityResolvers ], entryComponents: [ ...ENTRY_COMPONENTS, diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 22909b0c..2db5e320 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -228,7 +228,8 @@ }, "CUENTAS_CONTABLES": { "arbol_cuentas_contables": "Cuentas Contables", - "cuentas-contables": "Cuentas-contables" + "cuentas-contables": "Cuentas-contables", + "arbol-contable": "Arbol Contable" }, "CONCEPTOS":{ "crear-concepto":"Crear concepto",