From: Bill Erickson Date: Wed, 12 May 2021 19:12:31 +0000 (-0400) Subject: LP1904036 Barcode checkdigit / dialog support X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=be1e927eed43f0b8e22df503f71fd0bcc173e4ae;p=working%2FEvergreen.git LP1904036 Barcode checkdigit / dialog support Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg Signed-off-by: Galen Charlton --- diff --git a/Open-ILS/src/eg2/src/app/staff/circ/checkin/checkin.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/checkin/checkin.component.ts index e32931787a..fcb897c9bb 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/checkin/checkin.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/checkin/checkin.component.ts @@ -180,7 +180,8 @@ export class CheckinComponent implements OnInit, AfterViewInit { const params: CheckinParams = { copy_barcode: this.barcode, - backdate: this.backdate + backdate: this.backdate, + _checkbarcode: this.strictBarcode }; Object.keys(this.modifiers).forEach(mod => { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts index 30cf8019aa..ed99cb5af3 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts @@ -115,6 +115,7 @@ export class CheckoutComponent implements OnInit, AfterViewInit { const params: CheckoutParams = { patron_id: this.context.summary.id, + _checkbarcode: this.strictBarcode, _worklog: { user: this.context.summary.patron.family_name(), patron_id: this.context.summary.id diff --git a/Open-ILS/src/eg2/src/app/staff/circ/renew/renew.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/renew/renew.component.ts index 7cd30eacd9..473014ce8a 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/renew/renew.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/renew/renew.component.ts @@ -137,7 +137,7 @@ export class RenewComponent implements OnInit, AfterViewInit { const params: CheckoutParams = { copy_barcode: this.barcode, due_date: this.useDueDate ? this.dueDate : null, - _renewal: true + _checkbarcode: this.strictBarcode }; return this.barcodeSelect.getBarcode('asset', this.barcode) diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/bad-barcode-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/circ/bad-barcode-dialog.component.html new file mode 100644 index 0000000000..f7921a4d4e --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/circ/bad-barcode-dialog.component.html @@ -0,0 +1,27 @@ + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/bad-barcode-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/circ/bad-barcode-dialog.component.ts new file mode 100644 index 0000000000..e4427e5f81 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/circ/bad-barcode-dialog.component.ts @@ -0,0 +1,20 @@ +import {Component, OnInit, Output, Input, ViewChild, EventEmitter} from '@angular/core'; +import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap'; +import {DialogComponent} from '@eg/share/dialog/dialog.component'; + +/** Bad Item Barcode Dialog */ + +@Component({ + templateUrl: 'bad-barcode-dialog.component.html', + selector: 'eg-bad-barcode-dialog' +}) +export class BadBarcodeDialogComponent extends DialogComponent { + + barcode: string; + + constructor(private modal: NgbModal) { + super(modal); + } +} + + diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/circ.module.ts b/Open-ILS/src/eg2/src/app/staff/share/circ/circ.module.ts index fef714aee4..ecd51be2da 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/circ/circ.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/circ/circ.module.ts @@ -15,6 +15,7 @@ import {CopyInTransitDialogComponent} from './in-transit-dialog.component'; import {CancelTransitDialogComponent} from './cancel-transit-dialog.component'; import {BackdateDialogComponent} from './backdate-dialog.component'; import {WorkLogModule} from '@eg/staff/share/worklog/worklog.module'; +import {BadBarcodeDialogComponent} from './bad-barcode-dialog.component'; @NgModule({ declarations: [ @@ -28,6 +29,7 @@ import {WorkLogModule} from '@eg/staff/share/worklog/worklog.module'; BackdateDialogComponent, CopyInTransitDialogComponent, CancelTransitDialogComponent, + BadBarcodeDialogComponent, OpenCircDialogComponent ], imports: [ diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts b/Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts index 2f4cf01d3f..c339b73c51 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts @@ -130,6 +130,7 @@ export interface CheckoutParams { // internal tracking _override?: boolean; _renewal?: boolean; + _checkbarcode?: boolean; _worklog?: WorkLogEntry; } @@ -184,6 +185,7 @@ export interface CheckinParams { // internal / local values that are moved from the API request. _override?: boolean; _worklog?: WorkLogEntry; + _checkbarcode?: boolean; } export interface CheckinResult extends CircResultCommon { @@ -383,11 +385,15 @@ export class CircService { let method = 'open-ils.circ.checkout.full'; if (params._override) { method += '.override'; } - return this.net.request( - 'open-ils.circ', method, - this.auth.token(), this.apiParams(params)).toPromise() - .then(result => this.unpackCheckoutData(params, result)) - .then(result => this.processCheckoutResult(result)); + return this.inspectBarcode(params).then(barcodeOk => { + if (!barcodeOk) { return null; } + + return this.net.request( + 'open-ils.circ', method, + this.auth.token(), this.apiParams(params)).toPromise() + .then(result => this.unpackCheckoutData(params, result)) + .then(result => this.processCheckoutResult(result)); + }); } renew(params: CheckoutParams): Promise { @@ -399,11 +405,15 @@ export class CircService { let method = 'open-ils.circ.renew'; if (params._override) { method += '.override'; } - return this.net.request( - 'open-ils.circ', method, - this.auth.token(), this.apiParams(params)).toPromise() - .then(result => this.unpackCheckoutData(params, result)) - .then(result => this.processCheckoutResult(result)); + return this.inspectBarcode(params).then(barcodeOk => { + if (!barcodeOk) { return null; } + + return this.net.request( + 'open-ils.circ', method, + this.auth.token(), this.apiParams(params)).toPromise() + .then(result => this.unpackCheckoutData(params, result)) + .then(result => this.processCheckoutResult(result)); + }); } @@ -708,11 +718,15 @@ export class CircService { let method = 'open-ils.circ.checkin'; if (params._override) { method += '.override'; } - return this.net.request( - 'open-ils.circ', method, - this.auth.token(), this.apiParams(params)).toPromise() - .then(result => this.unpackCheckinData(params, result)) - .then(result => this.processCheckinResult(result)); + return this.inspectBarcode(params).then(barcodeOk => { + if (!barcodeOk) { return null; } + + return this.net.request( + 'open-ils.circ', method, + this.auth.token(), this.apiParams(params)).toPromise() + .then(result => this.unpackCheckinData(params, result)) + .then(result => this.processCheckinResult(result)); + }); } fleshCommonData(result: CircResultCommon): Promise { @@ -1153,5 +1167,59 @@ export class CircService { {order_by : {circ : 'xact_start desc' }, limit : 1} ).toPromise(); } + + // Resolves to true if the barcode is OK or the user confirmed it or + // the user doesn't care to begin with + inspectBarcode(params: CheckoutParams | CheckinParams): Promise { + if (!params._checkbarcode || !params.copy_barcode) { + return Promise.resolve(true); + } + + if (this.checkBarcode(params.copy_barcode)) { + // Avoid prompting again on an override + params._checkbarcode = false; + return Promise.resolve(true); + } + + this.components.badBarcodeDialog.barcode = params.copy_barcode; + return this.components.badBarcodeDialog.open().toPromise(); + } + + checkBarcode(barcode: string): boolean { + if (barcode !== Number(barcode).toString()) { return false; } + + const bc = barcode.toString(); + + // "16.00" == Number("16.00"), but the . is bad. + // Throw out any barcode that isn't just digits + if (bc.search(/\D/) !== -1) { return false; } + + const lastDigit = bc.substr(bc.length - 1); + const strippedBarcode = bc.substr(0, bc.length - 1); + return this.barcodeCheckdigit(strippedBarcode).toString() === lastDigit; + } + + barcodeCheckdigit(bc: string): number { + let checkSum = 0; + let multiplier = 2; + const reverseBarcode = bc.toString().split('').reverse(); + + reverseBarcode.forEach(ch => { + let tempSum = 0; + const product = (Number(ch) * multiplier) + ''; + product.split('').forEach(num => tempSum += Number(num)); + checkSum += Number(tempSum); + multiplier = multiplier === 2 ? 1 : 2; + }); + + const cSumStr = checkSum.toString(); + const nextMultipleOf10 = + (Number(cSumStr.match(/(\d*)\d$/)[1]) * 10) + 10; + + let checkDigit = nextMultipleOf10 - Number(cSumStr); + if (checkDigit === 10) { checkDigit = 0; } + + return checkDigit; + } } diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.html b/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.html index fa41b3ca6b..691e91792f 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.html +++ b/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.html @@ -67,3 +67,5 @@ + + diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.ts b/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.ts index 76dc5f5aab..e2ff1eec63 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/circ/components.component.ts @@ -9,6 +9,7 @@ import {RouteDialogComponent} from './route-dialog.component'; import {CopyInTransitDialogComponent} from './in-transit-dialog.component'; import {CopyAlertManagerDialogComponent } from '@eg/staff/share/holdings/copy-alert-manager.component'; +import {BadBarcodeDialogComponent} from './bad-barcode-dialog.component'; /* Container component for sub-components used by circulation actions. * @@ -36,6 +37,7 @@ export class CircComponentsComponent { @ViewChild('holdShelfStr') holdShelfStr: StringComponent; @ViewChild('catalogingStr') catalogingStr: StringComponent; + @ViewChild('badBarcodeDialog') badBarcodeDialog: BadBarcodeDialogComponent; constructor(private circ: CircService) { this.circ.components = this;