1 import {Component, Input, ViewChild, TemplateRef, OnInit} from '@angular/core';
2 import {DialogComponent} from '@eg/share/dialog/dialog.component';
3 import {IdlService, IdlObject} from '@eg/core/idl.service';
4 import {FormatService} from '@eg/core/format.service';
5 import {EventService} from '@eg/core/event.service';
6 import {NetService} from '@eg/core/net.service';
7 import {AuthService} from '@eg/core/auth.service';
8 import {PcrudService} from '@eg/core/pcrud.service';
9 import {StoreService} from '@eg/core/store.service';
10 import {OrgService} from '@eg/core/org.service';
11 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
12 import {GridDataSource, GridCellTextGenerator} from '@eg/share/grid/grid';
13 import {Pager} from '@eg/share/util/pager';
14 import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
15 import {StringComponent} from '@eg/share/string/string.component';
16 import {ToastService} from '@eg/share/toast/toast.service';
17 import {FundTagsComponent} from './fund-tags.component';
18 import {FundTransferDialogComponent} from './fund-transfer-dialog.component';
19 import {map, mergeMap} from 'rxjs/operators';
20 import {Observable, forkJoin, of} from 'rxjs';
23 selector: 'eg-fund-details-dialog',
24 templateUrl: './fund-details-dialog.component.html'
27 export class FundDetailsDialogComponent
28 extends DialogComponent implements OnInit {
30 @Input() fundId: number;
34 acqfaDataSource: GridDataSource;
35 acqftrDataSource: GridDataSource;
36 acqfdebDataSource: GridDataSource;
38 @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
39 @ViewChild('transferDialog', { static: false }) transferDialog: FundTransferDialogComponent;
40 @ViewChild('allocateToFundDialog', { static: true }) allocateToFundDialog: FmRecordEditorComponent;
41 @ViewChild('successString', { static: true }) successString: StringComponent;
42 @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
44 activeTab = 'summary';
45 defaultTabType = 'summary';
46 cellTextGenerator: GridCellTextGenerator;
49 private idl: IdlService,
50 private evt: EventService,
51 private net: NetService,
52 private auth: AuthService,
53 private pcrud: PcrudService,
54 private store: StoreService,
55 private org: OrgService,
56 private format: FormatService,
57 private toast: ToastService,
58 private modal: NgbModal
66 this.store.getLocalItem('eg.acq.fund_details.default_tab') || 'summary';
67 this.activeTab = this.defaultTabType;
70 this.onOpen$.subscribe(() => {
71 this.activeTab = this.defaultTabType;
74 this.idlDef = this.idl.classes['acqf'];
75 this.fieldOrder = 'name,code,year,org,active,currency_type,balance_stop_percentage,balance_warning_percentage,propagate,rollover';
77 this.cellTextGenerator = {
79 row.src_fund().code() + ' (' +
80 row.src_fund().year() + ') (' +
81 this.getOrgShortname(row.src_fund().org()) + ')',
83 row.dest_fund().code() + ' (' +
84 row.dest_fund().year() + ') (' +
85 this.getOrgShortname(row.dest_fund().org()) + ')',
86 funding_source: row =>
87 row.funding_source().code() + ' (' +
88 this.getOrgShortname(row.funding_source().owner()) + ')',
89 funding_source_credit: row =>
90 row.funding_source_credit().funding_source().code() + ' (' +
91 this.getOrgShortname(row.funding_source_credit().funding_source().owner()) + ')',
93 po: row => row.po_name,
94 invoice: row => row.vendor_invoice_id
98 private _initRecord() {
100 this.acqfaDataSource = this._getDataSource('acqfa', 'create_time ASC');
101 this.acqftrDataSource = this._getDataSource('acqftr', 'transfer_time ASC');
102 this.acqfdebDataSource = this._getDataSource('acqfdeb', 'create_time ASC');
103 this.pcrud.retrieve('acqf', this.fundId, {
117 }).subscribe(res => this.fund = res);
120 _getDataSource(idlClass: string, sortField: string): GridDataSource {
121 const gridSource = new GridDataSource();
123 gridSource.getRows = (pager: Pager, sort: any[]) => {
124 const orderBy: any = {};
126 // Sort specified from grid
127 orderBy[idlClass] = sort[0].name + ' ' + sort[0].dir;
128 } else if (sortField) {
129 // Default sort field
130 orderBy[idlClass] = sortField;
134 offset: pager.offset,
139 fleshSelectors: true,
142 const search: any = new Array();
143 if (idlClass === 'acqfa') {
144 search.push({ fund: this.fundId });
145 } else if (idlClass === 'acqftr') {
148 { src_fund: this.fundId },
149 { dest_fund: this.fundId }
152 searchOps['flesh'] = 2;
153 searchOps['flesh_fields'] = {
154 'acqftr': ['funding_source_credit'],
155 'acqfscred': ['funding_source']
157 } else if (idlClass === 'acqfdeb') {
158 search.push({ fund: this.fundId });
159 searchOps['flesh'] = 3;
160 searchOps['flesh_fields'] = {
161 'acqfdeb': ['invoice_entry', 'invoice_items', 'po_items', 'lineitem_details'],
162 'acqie': ['invoice', 'purchase_order', 'lineitem'],
163 'acqii': ['invoice'],
164 'acqpoi': ['purchase_order'],
165 'acqlid': ['lineitem'],
166 'jub': ['purchase_order']
170 Object.keys(gridSource.filters).forEach(key => {
171 Object.keys(gridSource.filters[key]).forEach(key2 => {
172 search.push(gridSource.filters[key][key2]);
176 return this.pcrud.search(idlClass, search, searchOps, reqOps)
177 .pipe(mergeMap((row) => this.doExtraFleshing(row)));
183 doExtraFleshing(row: IdlObject): Observable<IdlObject> {
184 if (row.classname === 'acqfdeb') {
185 row['vendor_invoice_id'] = null;
186 row['invoice_id'] = null;
188 row['po_name'] = null;
190 // TODO need to verify this, but we may be able to get away with
191 // the assumption that a given fund debit never has more than
192 // one line item, purchase order, or invoice associated with it.
193 if (row.invoice_entry()) {
194 if (row.invoice_entry().invoice()) {
195 row['invoice_id'] = row.invoice_entry().invoice().id();
196 row['vendor_invoice_id'] = row.invoice_entry().invoice().inv_ident();
198 if (row.invoice_entry().purchase_order()) {
199 row['po_id'] = row.invoice_entry().purchase_order().id();
200 row['po_name'] = row.invoice_entry().purchase_order().name();
202 if (row.invoice_entry().lineitem()) {
203 row['li_id'] = row.invoice_entry().lineitem().id();
206 if (row.lineitem_details() && row.lineitem_details().length) {
207 if (row.lineitem_details()[0].lineitem().purchase_order()) {
208 row['li_id'] = row.lineitem_details()[0].lineitem().id();
209 row['po_id'] = row.lineitem_details()[0].lineitem().purchase_order().id();
210 row['po_name'] = row.lineitem_details()[0].lineitem().purchase_order().name();
213 if (row.po_items() && row.po_items().length) {
214 if (row.po_items()[0].purchase_order()) {
215 row['po_id'] = row.po_items()[0].purchase_order().id();
216 row['po_name'] = row.po_items()[0].purchase_order().name();
219 if (row.invoice_items() && row.invoice_items().length) {
220 if (row.invoice_items()[0].invoice()) {
221 row['invoice_id'] = row.invoice_items()[0].invoice().id();
222 row['vendor_invoice_id'] = row.invoice_items()[0].invoice().inv_ident();
228 formatCurrency(value: any) {
229 return this.format.transform({
236 this.editDialog.recordId = this.fundId;
237 this.editDialog.mode = 'update';
238 this.editDialog.open({size: 'lg'}).subscribe(
240 this.successString.current()
241 .then(str => this.toast.success(str));
245 this.updateFailedString.current()
246 .then(str => this.toast.danger(str));
252 const allocation = this.idl.create('acqfa');
253 allocation.fund(this.fundId);
254 allocation.allocator(this.auth.user().id());
255 this.allocateToFundDialog.defaultNewRecord = allocation;
256 this.allocateToFundDialog.mode = 'create';
258 this.allocateToFundDialog.hiddenFieldsList = ['id', 'fund', 'allocator', 'create_time'];
259 this.allocateToFundDialog.fieldOrder = 'funding_source,amount,note';
260 this.allocateToFundDialog.open().subscribe(
262 this.successString.current()
263 .then(str => this.toast.success(str));
267 this.updateFailedString.current()
268 .then(str => this.toast.danger(str));
274 this.transferDialog.sourceFund = this.fund;
275 this.transferDialog.open().subscribe(
276 ok => this._initRecord()
281 this.defaultTabType = this.activeTab;
282 this.store.setLocalItem('eg.acq.fund_details.default_tab', this.activeTab);
285 getOrgShortname(ou: any) {
286 if (typeof ou === 'object') {
287 return ou.shortname();
289 return this.org.get(ou).shortname();
293 checkNegativeAmount(val: any) {
294 return Number(val) < 0;