</span>
</div>
+ <div class="d-flex ml-3 justify-content-center flex-column h-100">
+ <button type="button" (click)="toggleFilterSort()"
+ class="btn btn-sm btn-outline-dark mr-1">
+ <span *ngIf="showFilterSort" i18n>Hide Filter & Sort Options</span>
+ <span *ngIf="!showFilterSort" i18n>Show Filter & Sort Options</span>
+ </button>
+ </div>
+
<div class="flex-1"></div>
<div class="btn-toolbar">
</div>
</div><!-- buttons -->
</div>
+ <div [hidden]="!showFilterSort">
+ <div class="col-lg-12 d-flex">
+ <div class="d-flex justify-content-center flex-column h-100">
+ <div class="form-group form-inline">
+ <label for="sort-order-select" class="form-check-label mr-1">Sort by:</label>
+ <select name="sort-order-select" id="sort-order-select"
+ [ngModel]="sortOrder" (ngModelChange)="sortOrderChange($event)"
+ class="form-control">
+ <option value="li_id_asc" i18n>Lineitem ID Ascending</option>
+ <option value="li_id_desc" i18n>Lineitem ID Descending</option>
+ <option value="title_asc" i18n>Title Ascending</option>
+ <option value="title_desc" i18n>Title Descending</option>
+ <option value="author_asc" i18n>Author Ascending</option>
+ <option value="author_desc" i18n>Author Descending</option>
+ <option value="publisher_asc" i18n>Publisher Ascending</option>
+ <option value="publisher_desc" i18n>Publisher Descending</option>
+ <option value="order_ident_asc" i18n>Order Identifier Ascending</option>
+ <option value="order_ident_desc" i18n>Order Identifier Descending</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
<!-- LINEITEM LIST -->
'new', 'selector-ready', 'order-ready', 'approved', 'pending-order'
];
+const DEFAULT_SORT_ORDER = 'li_id_asc';
+const SORT_ORDER_MAP = {
+ li_id_asc: { 'order_by': [{'class': 'jub', 'field': 'id', 'direction': 'ASC'}] },
+ li_id_desc: { 'order_by': [{'class': 'jub', 'field': 'id', 'direction': 'DESC'}] },
+ title_asc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'ASC'}], 'order_by_attr': 'title' },
+ title_desc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'DESC'}], 'order_by_attr': 'title' },
+ author_asc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'ASC'}], 'order_by_attr': 'author' },
+ author_desc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'DESC'}], 'order_by_attr': 'author' },
+ publisher_asc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'ASC'}], 'order_by_attr': 'publisher' },
+ publisher_desc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'DESC'}], 'order_by_attr': 'publisher' },
+ order_ident_asc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'ASC'}],
+ 'order_by_attr': ['isbn', 'issn', 'upc'] },
+ order_ident_desc: { 'order_by': [{'class': 'acqlia', 'field': 'attr_value', 'direction': 'DESC'}],
+ 'order_by_attr': ['isbn', 'issn', 'upc'] },
+};
+
@Component({
templateUrl: 'lineitem-list.component.html',
selector: 'eg-lineitem-list',
// a lot of repetitive looping.
liMarcAttrs: {[id: number]: {[name: string]: IdlObject[]}} = {};
+ // sorting and filtering
+ sortOrder = DEFAULT_SORT_ORDER;
+ showFilterSort = false;
+
batchNote: string;
noteIsPublic = false;
batchSelectPage = false;
action = '';
batchFailure: EgEvent;
focusLi: number;
+ firstLoad = true; // using this to ensure that we avoid loading the LI table
+ // until the page size and sort order WS settings have been fetched
+ // TODO: route guard might be better
@ViewChild('cancelDialog') cancelDialog: CancelDialogComponent;
this.route.queryParamMap.subscribe((params: ParamMap) => {
this.pager.offset = +params.get('offset');
this.pager.limit = +params.get('limit');
- this.load();
+ if (!this.firstLoad) {
+ this.load();
+ }
});
this.route.fragment.subscribe((fragment: string) => {
this.picklistId = +params.get('picklistId');
this.poId = +params.get('poId');
this.recordId = +params.get('recordId');
- this.load();
+ if (!this.firstLoad) {
+ this.load();
+ }
});
this.store.getItem('acq.lineitem.page_size').then(count => {
this.pager.setLimit(count || 20);
- this.load();
+ this.store.getItem('acq.lineitem.sort_order').then(sortOrder => {
+ if (sortOrder && (sortOrder in SORT_ORDER_MAP)) {
+ this.sortOrder = sortOrder;
+ } else {
+ this.sortOrder = DEFAULT_SORT_ORDER;
+ }
+ this.load();
+ this.firstLoad = false;
+ });
});
+
}
pageSizeChange(count: number) {
});
}
+ sortOrderChange(sortOrder: string) {
+ this.store.setItem('acq.lineitem.sort_order', sortOrder).then(_ => {
+ this.sortOrder = sortOrder;
+ if (this.pager.isFirstPage()) {
+ this.load();
+ } else {
+ this.pager.toFirst();
+ this.goToPage();
+ }
+ });
+ }
+
// Focus the selected lineitem, which may not yet exist in the
// DOM for focusing.
focusLineitem(id?: number) {
loadIds(): Promise<any> {
this.lineitemIds = [];
- let id = this.poId;
- let options: any = {flesh_lineitem_ids: true, li_limit: 10000};
- let method = 'open-ils.acq.purchase_order.retrieve';
- let handler = (po) => po.lineitems();
- let sort = true;
+ const searchTerms = {};
+ const opts = { id_list: true, limit: 1000 };
if (this.picklistId) {
-
- id = this.picklistId;
- options = {idlist: true, limit: 1000};
- method = 'open-ils.acq.lineitem.picklist.retrieve.atomic';
- handler = (ids) => ids;
-
+ Object.assign(searchTerms, { jub: [ { picklist: this.picklistId } ] });
} else if (this.recordId) {
+ Object.assign(searchTerms, { jub: [ { eg_bib_id: this.recordId } ] });
+ } else {
+ Object.assign(searchTerms, { jub: [ { purchase_order: this.poId } ] });
+ }
- id = this.recordId;
- method = 'open-ils.acq.lineitems_for_bib.by_bib_id.atomic';
- options = {idlist: true, limit: 1000};
- handler = (ids) => ids;
- // The API sorts the newest to oldest, which is what
- // we want here.
- sort = false;
+ if (!(this.sortOrder in SORT_ORDER_MAP)) {
+ this.sortOrder = DEFAULT_SORT_ORDER;
}
+ Object.assign(opts, SORT_ORDER_MAP[this.sortOrder]);
return this.net.request(
- 'open-ils.acq', method, this.auth.token(), id, options
+ 'open-ils.acq',
+ 'open-ils.acq.lineitem.unified_search.atomic',
+ this.auth.token(),
+ searchTerms, // "and" terms
+ {}, // "or" terms
+ null,
+ opts
).toPromise().then(resp => {
- const ids = handler(resp);
-
- if (sort) {
- this.lineitemIds = ids
- .map(i => Number(i))
- .sort((id1, id2) => id1 < id2 ? -1 : 1);
- } else {
- this.lineitemIds = ids.map(i => Number(i));
- }
-
- this.pager.resultCount = ids.length;
+ this.lineitemIds = resp.map(i => Number(i));
+ this.pager.resultCount = resp.length;
});
}
this.expandAll = !this.expandAll;
}
+ toggleFilterSort() {
+ this.showFilterSort = !this.showFilterSort;
+ }
+
liHasAlerts(li: IdlObject): boolean {
return li.lineitem_notes().filter(n => n.alert_text()).length > 0;
}