From: Galen Charlton Date: Sun, 28 Mar 2021 05:04:42 +0000 (-0400) Subject: distribution formulas: feature-complete X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=3dd3cc707280c3ef4c3a685d6c376b5ce4c19dd1;p=working%2FEvergreen.git distribution formulas: feature-complete Signed-off-by: Galen Charlton --- diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formula-edit-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formula-edit-dialog.component.html new file mode 100644 index 0000000000..4149e5eeaa --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formula-edit-dialog.component.html @@ -0,0 +1,132 @@ + + + + + + +{{clonedLabel}} (clone) + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formula-edit-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formula-edit-dialog.component.ts new file mode 100644 index 0000000000..912d95410c --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formula-edit-dialog.component.ts @@ -0,0 +1,205 @@ +import {Component, Input, ViewChild, TemplateRef, OnInit} from '@angular/core'; +import {DialogComponent} from '@eg/share/dialog/dialog.component'; +import {NgForm} from '@angular/forms'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {EventService} from '@eg/core/event.service'; +import {OrgService} from '@eg/core/org.service'; +import {NetService} from '@eg/core/net.service'; +import {AuthService} from '@eg/core/auth.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {Pager} from '@eg/share/util/pager'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {StringComponent} from '@eg/share/string/string.component'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {PermService} from '@eg/core/perm.service'; + +@Component({ + selector: 'eg-distribution-formula-edit-dialog', + templateUrl: './distribution-formula-edit-dialog.component.html' +}) + +export class DistributionFormulaEditDialogComponent + extends DialogComponent implements OnInit { + + @Input() mode: string = 'create'; + @Input() formulaId: number; + @Input() cloneSource: number; + + @ViewChild('defaultCloneLabel', { static: true }) defaultCloneLabel: StringComponent; + formula: IdlObject; + deadEntries: IdlObject[]; + clonedLabel = ''; + + constructor( + private idl: IdlService, + private evt: EventService, + private net: NetService, + private auth: AuthService, + private org: OrgService, + private pcrud: PcrudService, + private perm: PermService, + private toast: ToastService, + private modal: NgbModal + ) { + super(modal); + } + + ngOnInit() { + this.onOpen$.subscribe(() => this._initRecord()); + } + + private _initRecord() { + this.formula = null; + this.deadEntries = []; + this.clonedLabel = ''; + if (this.mode === 'update') { + this.pcrud.retrieve('acqdf', this.formulaId, { + flesh: 1, + flesh_fields: { acqdf: ['entries'] } + }).subscribe(res => { + this.formula = res; + this._generateFormulaInputs(); + }); + } else if (this.mode === 'clone') { + this.pcrud.retrieve('acqdf', this.cloneSource, { + flesh: 1, + flesh_fields: { acqdf: ['entries'] } + }).subscribe(res => { + this.clonedLabel = res.name(); + this.formula = this.idl.clone(res); + this.formula.id(null); + this.defaultCloneLabel.current().then(str => this.formula.name(str)); + this.formula.entries().forEach((e) => e.formula(null)); + this._generateFormulaInputs(); + }); + } else if (this.mode === 'create') { + this.formula = this.idl.create('acqdf'); + this.formula.entries([]); + this._generateFormulaInputs(); + } + } + + _generateFormulaInputs() { + this.formula.entries().sort((a, b) => { return a.position() < b.position() ? -1 : 1 }); + const entry = this.idl.create('acqdfe'); + entry.id(-9999); // magic placeholder for new record + this.formula.entries().push(entry); + } + + org_root(): number { + return this.org.root().id(); + } + + addRow() { + if (this.formula.entries().slice(-1)[0].id() === -9999) { + this.formula.entries().slice(-1)[0].id(-1); // magic placheholder for new entry that we intend to keep + } + const entry = this.idl.create('acqdfe'); + entry.id(-9999); // magic placeholder for new record + this.formula.entries().push(entry); + } + removeRow(idx: number) { + this.deadEntries.push(this.formula.entries().splice(idx, 1)[0]); + } + moveUp(idx: number) { + const temp = this.formula.entries()[idx - 1]; + this.formula.entries()[idx - 1] = this.formula.entries()[idx]; + this.formula.entries()[idx] = temp; + } + moveDown(idx: number) { + const temp = this.formula.entries()[idx + 1]; + this.formula.entries()[idx + 1] = this.formula.entries()[idx]; + this.formula.entries()[idx] = temp; + } + + + save() { + // grab a copy to preserve the list of entries + const formulaCopy = this.idl.clone(this.formula); + if (this.formula.id() === undefined || this.formula.id() === null) { + this.formula.isnew(true); + this.formula.owner(this.formula.owner().id()); + } else { + this.formula.ischanged(true); + } + this.pcrud.autoApply([this.formula]).subscribe(res => { + const dfId = this.mode === 'update' ? res : res.id(); + const updates: IdlObject[] = []; + if (this.mode === 'create' || this.mode === 'clone') { + formulaCopy.entries().forEach((entry, idx) => { + if (entry.id() === -1) { entry.id(null); } + if (entry.id() === -9999) { entry.id(null); } + if (entry.item_count() == null) { + // we got nothing; ignore + return; + } + if (entry.owning_lib() == null && + entry.fund() == null && + entry.location() == null && + entry.circ_modifier() == null && + entry.collection_code() == null + ) { + // this is a pointless entry; ignore + return; + } + + entry.formula(dfId); + if (entry.owning_lib()) { entry.owning_lib(entry.owning_lib().id()); } + entry.id(null); + entry.position(idx); // re-writing all the positions + entry.isnew(true); + updates.push(entry); + }); + } else { + // updating an existing set + formulaCopy.entries().forEach((entry, idx) => { + if (entry.id() === -1) { entry.id(null); } + if (entry.id() === -9999) { entry.id(null); } + if (entry.id()) { + entry.formula(dfId); + entry.position(idx); + if (entry.owning_lib()) { entry.owning_lib(entry.owning_lib().id()); } + const delEntry = this.idl.clone(entry); + // have to delete and recreate because of the + // check constraint on formula, position + this.deadEntries.push(delEntry); + entry.isnew(true); + updates.push(entry); + } else { + if (entry.item_count() == null) { + // we got nothing; ignore + return; + } + if (entry.owning_lib() == null && + entry.fund() == null && + entry.location() == null && + entry.circ_modifier() == null && + entry.collection_code() == null + ) { + // this is a pointless entry; ignore + return; + } + + entry.formula(dfId); + if (entry.owning_lib()) { entry.owning_lib(entry.owning_lib().id()); } + entry.position(idx); // re-writing all the positions + entry.isnew(true); + updates.push(entry); + } + }); + } + this.deadEntries.forEach((entry) => { + if (entry.id()) { + entry.isdeleted(true); + updates.unshift(entry); // deletions have to be processed first + } + }); + this.pcrud.autoApply(updates).subscribe( + res => {}, + err => this.close(err), + () => this.close(true) + ); + }, err => this.close(false)); + } + +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.html b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.html index 2df555c20f..09bc051535 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.html @@ -58,11 +58,14 @@ + + + @@ -76,3 +79,15 @@ [preloadLinkedValues]="true" [readonlyFields]="readonlyFields"> + + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.ts index e1052cc52c..73bc3478d0 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.component.ts @@ -13,7 +13,12 @@ import {OrgService} from '@eg/core/org.service'; import {PermService} from '@eg/core/perm.service'; import {AuthService} from '@eg/core/auth.service'; import {NetService} from '@eg/core/net.service'; +import {map, mergeMap} from 'rxjs/operators'; import {StringComponent} from '@eg/share/string/string.component'; +import {DistributionFormulaEditDialogComponent} from './distribution-formula-edit-dialog.component'; +import {Observable, forkJoin, of} from 'rxjs'; +import {AlertDialogComponent} from '@eg/share/dialog/alert.component'; +import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component'; @Component({ templateUrl: './distribution-formulas.component.html' @@ -24,6 +29,9 @@ export class DistributionFormulasComponent extends AdminPageComponent implements classLabel: string; @ViewChild('grid', { static: true }) grid: GridComponent; + @ViewChild('distributionFormulaEditDialog', { static: false }) distributionFormulaEditDialog: DistributionFormulaEditDialogComponent; + @ViewChild('alertDialog', {static: false}) private alertDialog: AlertDialogComponent; + @ViewChild('confirmDel', { static: true }) confirmDel: ConfirmDialogComponent; cellTextGenerator: GridCellTextGenerator; @@ -64,7 +72,11 @@ export class DistributionFormulasComponent extends AdminPageComponent implements const searchOps = { offset: pager.offset, limit: pager.limit, - order_by: orderBy + order_by: orderBy, + flesh: 1, + flesh_fields: { + acqdf: ['entries'] + } }; const reqOps = { fleshSelectors: true, @@ -73,7 +85,8 @@ export class DistributionFormulasComponent extends AdminPageComponent implements if (!this.contextOrg && !Object.keys(this.dataSource.filters).length) { // No org filter -- fetch all rows return this.pcrud.retrieveAll( - this.idlClass, searchOps, reqOps); + this.idlClass, searchOps, reqOps) + .pipe(mergeMap((row) => this.countItems(row))); } const search: any = new Array(); @@ -92,7 +105,8 @@ export class DistributionFormulasComponent extends AdminPageComponent implements }); return this.pcrud.search( - this.idlClass, search, searchOps, reqOps); + this.idlClass, search, searchOps, reqOps) + .pipe(mergeMap((row) => this.countItems(row))); }; super.ngOnInit(); @@ -101,4 +115,75 @@ export class DistributionFormulasComponent extends AdminPageComponent implements this.includeOrgDescendants = true; } + countItems(row: IdlObject): Observable { + row['item_count'] = 0; + row.entries().forEach((e) => row['item_count'] += e.item_count()); + return of(row); + } + + showEditDistributionFormulaDialog(successString: StringComponent, failString: StringComponent): Promise { + return new Promise((resolve, reject) => { + this.distributionFormulaEditDialog.open({size: 'xl', scrollable: true}).subscribe( + result => { + this.successString.current() + .then(str => this.toast.success(str)); + resolve(result); + }, + error => { + this.updateFailedString.current() + .then(str => this.toast.danger(str)); + reject(error); + }, + () => this.grid.reload() + ); + }); + } + + createNew() { + this.distributionFormulaEditDialog.mode = 'create'; + this.showEditDistributionFormulaDialog(this.createString, this.createErrString); + } + + editSelected(rows: IdlObject[]) { + if (rows.length <= 0) { return; } + this.distributionFormulaEditDialog.mode = 'update'; + this.distributionFormulaEditDialog.formulaId = rows[0].id(); + this.showEditDistributionFormulaDialog(this.successString, this.updateFailedString); + } + + cloneSelected(rows: IdlObject[]) { + if (rows.length <= 0) { return; } + this.distributionFormulaEditDialog.mode = 'clone'; + this.distributionFormulaEditDialog.cloneSource = rows[0].id(); + this.showEditDistributionFormulaDialog(this.createString, this.createErrString); + } + + deleteSelected(rows: IdlObject[]) { + if (rows.length > 0) { + const id = rows[0].id(); + let can: boolean = true; + forkJoin([ + this.pcrud.search('acqdfa', { formula: id }, { limit: 1 }, { atomic: true }), + ]).subscribe( + results => { + results.forEach((res) => { + if (res.length > 0) { + can = false; + } + }); + }, + err => {}, + () => { + if (can) { + this.confirmDel.open().subscribe(confirmed => { + if (!confirmed) { return; } + super.deleteSelected([ rows[0] ]); + }); + } else { + this.alertDialog.open(); + } + } + ); + } + } } diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.module.ts index 86365d7edc..313f4b79bc 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/acq/distribution_formula/distribution-formulas.module.ts @@ -3,14 +3,18 @@ import {StaffCommonModule} from '@eg/staff/common.module'; import {AdminCommonModule} from '@eg/staff/admin/common.module'; import {DistributionFormulasRoutingModule} from './routing.module'; import {DistributionFormulasComponent} from './distribution-formulas.component'; +import {DistributionFormulaEditDialogComponent} from './distribution-formula-edit-dialog.component'; +import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module'; @NgModule({ declarations: [ - DistributionFormulasComponent + DistributionFormulasComponent, + DistributionFormulaEditDialogComponent ], imports: [ StaffCommonModule, AdminCommonModule, + ItemLocationSelectModule, DistributionFormulasRoutingModule ], exports: [