LP1819179 PCRUD selector fleshing handles maps
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / core / format.service.ts
1 import {Injectable} from '@angular/core';
2 import {DatePipe, CurrencyPipe} from '@angular/common';
3 import {IdlService, IdlObject} from '@eg/core/idl.service';
4 import {OrgService} from '@eg/core/org.service';
5
6 /**
7  * Format IDL vield values for display.
8  */
9
10 declare var OpenSRF;
11
12 export interface FormatParams {
13     value: any;
14     idlClass?: string;
15     idlField?: string;
16     datatype?: string;
17     orgField?: string; // 'shortname' || 'name'
18     datePlusTime?: boolean;
19 }
20
21 @Injectable({providedIn: 'root'})
22 export class FormatService {
23
24     dateFormat = 'shortDate';
25     dateTimeFormat = 'short';
26     wsOrgTimezone: string = OpenSRF.tz;
27
28     constructor(
29         private datePipe: DatePipe,
30         private currencyPipe: CurrencyPipe,
31         private idl: IdlService,
32         private org: OrgService
33     ) {
34
35         // Create an inilne polyfill for Number.isNaN, which is
36         // not available in PhantomJS for unit testing.
37         // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
38         if (!Number.isNaN) {
39             // "The following works because NaN is the only value
40             // in javascript which is not equal to itself."
41             Number.isNaN = (value: any) => {
42                 return value !== value;
43             };
44         }
45     }
46
47     /**
48      * Create a human-friendly display version of any field type.
49      */
50     transform(params: FormatParams): string {
51         const value = params.value;
52
53         if (   value === undefined
54             || value === null
55             || value === ''
56             || Number.isNaN(value)) {
57             return '';
58         }
59
60         let datatype = params.datatype;
61
62         if (!datatype) {
63             if (params.idlClass && params.idlField) {
64                 datatype = this.idl.classes[params.idlClass]
65                     .field_map[params.idlField].datatype;
66             } else {
67                 // Assume it's a primitive value
68                 return value + '';
69             }
70         }
71
72         switch (datatype) {
73
74             case 'link':
75                 if (typeof value !== 'object') {
76                     return value + ''; // no fleshed value here
77                 }
78
79                 if (!params.idlClass || !params.idlField) {
80                     // Without a full accounting of the field data,
81                     // we can't determine the linked selector field.
82                     return value + '';
83                 }
84
85                 const selector =
86                     this.idl.getLinkSelector(params.idlClass, params.idlField);
87
88                 if (selector && typeof value[selector] === 'function') {
89                     const val = value[selector]();
90
91                     if (Array.isArray(val)) {
92                         // Typically has_many links will not be fleshed,
93                         // but in the off-chance the are, avoid displaying
94                         // an array reference value.
95                         return '';
96                     } else {
97                         return val + '';
98                     }
99
100                 } else {
101                     return value + '';
102                 }
103
104             case 'org_unit':
105                 const orgField = params.orgField || 'shortname';
106                 const org = this.org.get(value);
107                 return org ? org[orgField]() : '';
108
109             case 'timestamp':
110                 const date = new Date(value);
111                 let fmt = this.dateFormat || 'shortDate';
112                 if (params.datePlusTime) {
113                     fmt = this.dateTimeFormat || 'short';
114                 }
115                 return this.datePipe.transform(date, fmt);
116
117             case 'money':
118                 return this.currencyPipe.transform(value);
119
120             case 'bool':
121                 // Slightly better than a bare 't' or 'f'.
122                 // Should probably add a global true/false string.
123                 return Boolean(
124                     value === 't' || value === 1 ||
125                     value === '1' || value === true
126                 ).toString();
127
128             default:
129                 return value + '';
130         }
131     }
132 }
133