]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/acq/lineitem/copies.component.ts
LP1929741 ACQ Selection List & PO Angluar Port
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / acq / lineitem / copies.component.ts
1 import {Component, OnInit, AfterViewInit, Input, Output, EventEmitter,
2   ViewChild} from '@angular/core';
3 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
4 import {tap} from 'rxjs/operators';
5 import {Pager} from '@eg/share/util/pager';
6 import {IdlService, IdlObject} from '@eg/core/idl.service';
7 import {OrgService} from '@eg/core/org.service';
8 import {NetService} from '@eg/core/net.service';
9 import {PcrudService} from '@eg/core/pcrud.service';
10 import {AuthService} from '@eg/core/auth.service';
11 import {LineitemService} from './lineitem.service';
12 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
13 import {ItemLocationService} from '@eg/share/item-location-select/item-location-select.service';
14
15 const FORMULA_FIELDS = [
16     'owning_lib',
17     'location',
18     'fund',
19     'circ_modifier',
20     'collection_code'
21 ];
22
23 interface FormulaApplication {
24     formula: IdlObject;
25     count: number;
26 }
27
28 @Component({
29   templateUrl: 'copies.component.html'
30 })
31 export class LineitemCopiesComponent implements OnInit, AfterViewInit {
32     static newCopyId = -1;
33
34     lineitemId: number;
35     lineitem: IdlObject;
36     copyCount = 1;
37     batchOwningLib: IdlObject;
38     batchFund: ComboboxEntry;
39     batchCopyLocId: number;
40     saving = false;
41     progressMax = 0;
42     progressValue = 0;
43     formulaFilter = {owner: []};
44     formulaOffset = 0;
45     formulaValues: {[field: string]: {[val: string]: boolean}} = {};
46
47     // Can any changes be applied?
48     liLocked = false;
49
50     constructor(
51         private route: ActivatedRoute,
52         private idl: IdlService,
53         private org: OrgService,
54         private net: NetService,
55         private pcrud: PcrudService,
56         private auth: AuthService,
57         private loc: ItemLocationService,
58         private liService: LineitemService
59     ) {}
60
61     ngOnInit() {
62
63         this.formulaFilter.owner =
64             this.org.fullPath(this.auth.user().ws_ou(), true);
65
66         this.route.paramMap.subscribe((params: ParamMap) => {
67             const id = +params.get('lineitemId');
68             if (id !== this.lineitemId) {
69                 this.lineitemId = id;
70                 if (id) { this.load(); }
71             }
72         });
73
74         this.liService.getLiAttrDefs();
75     }
76
77     load(): Promise<any> {
78         this.lineitem = null;
79         this.copyCount = 1;
80         return this.liService.getFleshedLineitems(
81             [this.lineitemId], {toCache: true, fromCache: true})
82         .pipe(tap(liStruct => this.lineitem = liStruct.lineitem)).toPromise()
83         .then(_ => {
84             this.liLocked =
85               this.lineitem.state().match(/on-order|received|cancelled/);
86         })
87         .then(_ => this.applyCount());
88     }
89
90     ngAfterViewInit() {
91         setTimeout(() => {
92             const node = document.getElementById('copy-count-input');
93             if (node) { (node as HTMLInputElement).select(); }
94         });
95     }
96
97     applyCount() {
98         const copies = this.lineitem.lineitem_details();
99         while (copies.length < this.copyCount) {
100             const copy = this.idl.create('acqlid');
101             copy.id(LineitemCopiesComponent.newCopyId--);
102             copy.isnew(true);
103             copy.lineitem(this.lineitem.id());
104             copies.push(copy);
105         }
106
107         if (copies.length > this.copyCount) {
108             this.copyCount = copies.length;
109         }
110     }
111
112     applyFormula(id: number) {
113
114         const copies = this.lineitem.lineitem_details();
115         if (this.formulaOffset >= copies.length) {
116             // We have already applied a formula entry to every item.
117             return;
118         }
119
120         this.formulaValues = {};
121
122         this.pcrud.retrieve('acqdf', id,
123             {flesh: 1, flesh_fields: {acqdf: ['entries']}})
124         .subscribe(formula => {
125
126             formula.entries(
127                 formula.entries().sort((e1, e2) =>
128                     e1.position() < e2.position() ? -1 : 1));
129
130             let rowIdx = this.formulaOffset - 1;
131
132             while (++rowIdx < copies.length) {
133                 this.formulateOneCopy(formula, rowIdx, true);
134             }
135
136             // No new values will be applied
137             if (!Object.keys(this.formulaValues)) { return; }
138
139             this.fetchFormulaValues().then(_ => {
140
141                 let applied = 0;
142                 let rowIdx2 = this.formulaOffset - 1;
143
144                 while (++rowIdx2 < copies.length) {
145                     applied += this.formulateOneCopy(formula, rowIdx2);
146                 }
147
148                 if (applied) {
149                     this.formulaOffset += applied;
150                     this.saveAppliedFormula(formula);
151                 }
152             });
153         });
154     }
155
156     saveAppliedFormula(formula: IdlObject) {
157         const app = this.idl.create('acqdfa');
158         app.lineitem(this.lineitem.id());
159         app.creator(this.auth.user().id());
160         app.formula(formula.id());
161
162         this.pcrud.create(app).toPromise().then(a => {
163             a.creator(this.auth.user());
164             a.formula(formula);
165             this.lineitem.distribution_formulas().push(a);
166         });
167     }
168
169     // Grab values applied by distribution formulas and cache them before
170     // applying them to their target copies, so the comboboxes, etc.
171     // are not required to go fetch them en masse / en duplicato.
172     fetchFormulaValues(): Promise<any> {
173
174         const funds = Object.keys(this.formulaValues.fund);
175         const mods = Object.keys(this.formulaValues.circ_modifier);
176         const locs = Object.keys(this.formulaValues.location);
177
178         let promise = Promise.resolve();
179
180         if (funds.length > 0) {
181             promise = promise.then(_ => {
182                 return this.pcrud.search('acqf', {id: funds})
183                 .pipe(tap(fund => {
184                     this.liService.fundCache[fund.id()] = fund;
185                     this.liService.batchOptionWanted.emit(
186                         {fund: {id: fund.id(), label: fund.code(), fm: fund}});
187                 })).toPromise();
188             });
189         }
190
191         if (mods.length > 0) {
192             promise = promise.then(_ => {
193                 return this.pcrud.search('ccm', {code: mods})
194                 .pipe(tap(mod => {
195                     this.liService.circModCache[mod.code()] = mod;
196                     this.liService.batchOptionWanted.emit({circ_modifier:
197                         {id: mod.code(), label: mod.code(), fm: mod}});
198                 })).toPromise();
199             });
200         }
201
202         if (locs.length > 0) {
203             promise = promise.then(_ => {
204                 return this.pcrud.search('acpl', {id: locs})
205                 .pipe(tap(loc => {
206                     this.loc.locationCache[loc.id()] = loc;
207                     this.liService.batchOptionWanted.emit({location:
208                         {id: loc.id(), label: loc.name(), fm: loc}});
209                 })).toPromise();
210             });
211         }
212
213         return promise;
214     }
215
216     // Apply a formula entry to a single copy.
217     // extracOnly means we are only collecting the new values we wish to
218     // apply from the formula w/o applying them to the copy in question.
219     formulateOneCopy(formula: IdlObject,
220         rowIdx: number, extractOnly?: boolean): number {
221
222         let targetEntry = null;
223         let entryIdx = this.formulaOffset;
224         const copy = this.lineitem.lineitem_details()[rowIdx];
225
226         // Find the correct entry for the current copy.
227         formula.entries().forEach(entry => {
228             if (!targetEntry) {
229                 entryIdx += entry.item_count();
230                 if (entryIdx > rowIdx) {
231                     targetEntry = entry;
232                 }
233             }
234         });
235
236         // We ran out of copies.
237         if (!targetEntry) { return 0; }
238
239         FORMULA_FIELDS.forEach(field => {
240             const val = targetEntry[field]();
241             if (val === undefined || val === null) { return; }
242
243             if (extractOnly) {
244                 if (!this.formulaValues[field]) {
245                     this.formulaValues[field] = {};
246                 }
247                 this.formulaValues[field][val] = true;
248
249             } else {
250                 copy[field](val);
251             }
252         });
253
254         return 1;
255     }
256
257     save() {
258         this.saving = true;
259         this.progressMax = null;
260         this.progressValue = 0;
261
262         this.liService.updateLiDetails(this.lineitem).subscribe(
263             struct => {
264                 this.progressMax = struct.total;
265                 this.progressValue++;
266             },
267             err => {},
268             () => this.load().then(_ => {
269                 this.liService.activateStateChange.emit(this.lineitem.id());
270                 this.saving = false;
271             })
272         );
273     }
274
275     deleteFormula(formula: IdlObject) {
276         this.pcrud.remove(formula).subscribe(_ => {
277             this.lineitem.distribution_formulas(
278                 this.lineitem.distribution_formulas()
279                 .filter(f => f.id() !== formula.id())
280             );
281         });
282     }
283 }
284
285