Skip to content

Commit

Permalink
feat(item): add fees on item detail view
Browse files Browse the repository at this point in the history
* Adds fees on item detail view.
* Changes patron link on fees brief view to redirect to the
  patron circulation fees tab.
* Closes rero/rero-ils#3578.

Co-Authored-by: Bertrand Zuchuat <bertrand.zuchuat@rero.ch>
  • Loading branch information
Garfield-fr committed Apr 9, 2024
1 parent c4e3726 commit eef0e07
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 18 deletions.
13 changes: 10 additions & 3 deletions projects/admin/src/app/api/patron-transaction-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
*/
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ApiService } from '@rero/ng-core';
import { Observable } from 'rxjs';
import { ApiService, Record, RecordService } from '@rero/ng-core';
import { Observable, map } from 'rxjs';
import { FeeFormModel } from '../circulation/patron/patron-transactions/patron-fee/patron-fee.component';

@Injectable({
Expand All @@ -31,7 +31,8 @@ export class PatronTransactionApiService {
*/
constructor(
private _httpClient: HttpClient,
private _apiService: ApiService
private _apiService: ApiService,
private recordService: RecordService
) {}

/**
Expand All @@ -41,4 +42,10 @@ export class PatronTransactionApiService {
addFee(model: FeeFormModel): Observable<Object> {
return this._httpClient.post(this._apiService.getEndpointByType('patron_transactions/'), model);
}

getActiveFeesByItemPid(itemPid: string): any {
const query = `item.pid:${itemPid} AND total_amount:>0`;
return this.recordService.getRecords('patron_transactions', query)
.pipe(map((result: Record) => result.hits.hits));
}
}
15 changes: 8 additions & 7 deletions projects/admin/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { PrimengImportModule } from '@app/admin/shared/primeng-import/primeng-import.module';
import { HotkeysModule, HotkeysService } from '@ngneat/hotkeys';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyPrimeNGModule } from '@ngx-formly/primeng';
import { FormlyFieldSelect, FormlySelectModule } from '@ngx-formly/primeng/select';
import { FormlyFieldSelect } from '@ngx-formly/primeng/select';
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client';
import { TranslateLoader as BaseTranslateLoader, TranslateModule } from '@ngx-translate/core';
import {
Expand Down Expand Up @@ -145,26 +144,29 @@ import { RelatedResourceComponent } from './record/detail-view/document-detail-v
import { EntitiesLocalDetailViewComponent } from './record/detail-view/entities-detail-view/local/entities-local-detail-view.component';
import { EntitiesLocalGlobalComponent } from './record/detail-view/entities-detail-view/local/entities-local-global.component';
import { LocalOrganisationDetailViewComponent } from './record/detail-view/entities-detail-view/local/local-organisation-detail-view/local-organisation-detail-view.component';
import { LocalPageDetailComponent } from './record/detail-view/entities-detail-view/local/local-page-detail/local-page-detail.component';
import { LocalPersonDetailViewComponent } from './record/detail-view/entities-detail-view/local/local-person-detail-view/local-person-detail-view.component';
import { LocalPlaceDetailViewComponent } from './record/detail-view/entities-detail-view/local/local-place-detail-view/local-place-detail-view.component';
import { LocalTopicDetailViewComponent } from './record/detail-view/entities-detail-view/local/local-topic-detail-view/local-topic-detail-view.component';
import { LocalWorkDetailViewComponent } from './record/detail-view/entities-detail-view/local/local-work-detail-view/local-work-detail-view.component';
import { RemoteEntitiesDetailViewComponent } from './record/detail-view/entities-detail-view/remote/entities-remote-detail-view.component';
import { RemoteEntitiesOrganisationDetailViewComponent } from './record/detail-view/entities-detail-view/remote/remote-organisation-detail-view/remote-entities-organisation-detail-view.component';
import { RemotePageDetailComponent } from './record/detail-view/entities-detail-view/remote/remote-page-detail/remote-page-detail.component';
import { RemoteEntitiesPersonDetailViewComponent } from './record/detail-view/entities-detail-view/remote/remote-person-detail-view/remote-entities-person-detail-view.component';
import { RemoteTopicDetailViewComponent } from './record/detail-view/entities-detail-view/remote/remote-topic-detail-view/remote-topic-detail-view.component';
import { HoldingDetailViewComponent } from './record/detail-view/holding-detail-view/holding-detail-view.component';
import { HoldingPageDetailComponent } from './record/detail-view/holding-detail-view/holding-page-detail/holding-page-detail.component';
import { ExpectedIssueComponent } from './record/detail-view/holding-detail-view/serial-holding-detail-view/expected-issue/expected-issue.component';
import { ReceivedIssueComponent } from './record/detail-view/holding-detail-view/serial-holding-detail-view/received-issue/received-issue.component';
import {
SerialHoldingDetailViewComponent
} from './record/detail-view/holding-detail-view/serial-holding-detail-view/serial-holding-detail-view.component';
import { HoldingPageDetailComponent } from './record/detail-view/holding-detail-view/holding-page-detail/holding-page-detail.component';
import { IllRequestDetailViewComponent } from './record/detail-view/ill-request-detail-view/ill-request-detail-view.component';
import { ItemDetailViewComponent } from './record/detail-view/item-detail-view/item-detail-view.component';
import { ItemFeesComponent } from './record/detail-view/item-detail-view/item-fees/item-fees.component';
import { ItemPageDetailComponent } from './record/detail-view/item-detail-view/item-page-detail/item-page-detail.component';
import { ItemTransactionComponent } from './record/detail-view/item-detail-view/item-transaction/item-transaction.component';
import { ItemTransactionsComponent } from './record/detail-view/item-detail-view/item-transactions/item-transactions.component';
import { ItemPageDetailComponent } from './record/detail-view/item-detail-view/item-page-detail/item-page-detail.component';
import { ItemTypeDetailViewComponent } from './record/detail-view/item-type-detail-view/item-type-detail-view.component';
import { DayOpeningHoursComponent } from './record/detail-view/library-detail-view/day-opening-hours/day-opening-hours.component';
import { ExceptionDateComponent } from './record/detail-view/library-detail-view/exception-date/exception-date.component';
Expand Down Expand Up @@ -212,8 +214,6 @@ import { PreviewEmailModule } from './shared/preview-email/preview-email.module'
import { CurrentLibraryPermissionValidator } from './utils/permissions';
import { CustomShortcutHelpComponent } from './widgets/custom-shortcut-help/custom-shortcut-help.component';
import { FrontpageComponent } from './widgets/frontpage/frontpage.component';
import { LocalPageDetailComponent } from './record/detail-view/entities-detail-view/local/local-page-detail/local-page-detail.component';
import { RemotePageDetailComponent } from './record/detail-view/entities-detail-view/remote/remote-page-detail/remote-page-detail.component';

/** Init application factory */
export function appInitFactory(appInitializerService: AppInitializerService): () => Promise<any> {
Expand Down Expand Up @@ -359,7 +359,8 @@ export function appInitFactory(appInitializerService: AppInitializerService): ()
HoldingPageDetailComponent,
ItemPageDetailComponent,
LocalPageDetailComponent,
RemotePageDetailComponent
RemotePageDetailComponent,
ItemFeesComponent
],
imports: [
AppRoutingModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PatronTransactionService } from '@app/admin/circulation/services/patron-transaction.service';
import { PatronTransaction, PatronTransactionEventType, PatronTransactionStatus } from '@app/admin/classes/patron-transaction';
import { OrganisationService } from '@app/admin/service/organisation.service';
Expand Down Expand Up @@ -73,17 +74,22 @@ export class PatronTransactionComponent implements OnInit {
* @param organisationService - OrganisationService
* @param patronTransactionService - PatronTransactionService
* @param modalService - BsModalService
* @param translateService - TranslateService
* @param router - ActivatedRoute
*/
constructor(
private organisationService: OrganisationService,
private patronTransactionService: PatronTransactionService,
private modalService: BsModalService
private modalService: BsModalService,
private router: ActivatedRoute
) {}

/** OnInit hook */
ngOnInit() {
if (this.transaction) {
// Open the current event if the url parameter match with transaction pid
if (this.router.snapshot.queryParams.event === this.transaction.pid) {
this.isCollapsed = false;
}
this.patronTransactionService.loadTransactionHistory(this.transaction);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ <h5 class="mb-0">
}
<dt class="label-title col-3" translate>Patron</dt>
<dd class="col-9">
<a [routerLink]="['/records', 'patrons', 'detail', parent.patron.pid]">{{ parent.patron.pid | patronName | async }}</a>
@if (parent.patron.barcode) {
<a [routerLink]="['/circulation', 'patron', parent.patron.barcode, 'fees']" [queryParams]="{event: parent.pid}">{{ parent.patron.pid | patronName | async }}</a>
} @else {
<a [routerLink]="['/records', 'patrons', 'detail', parent.patron.pid]">{{ parent.patron.pid | patronName | async }}</a>
}
</dd>
</dl>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ <h3 translate>Issue data</h3>
<div class="mt-2">
<admin-circulation-logs-dialog [resourcePid]="record.metadata.pid" [resourceType]="'item'"></admin-circulation-logs-dialog>
<admin-item-transactions [itemPid]="record.metadata.pid" (requestEvent)="updateItemStatus()"></admin-item-transactions>
<admin-item-fees
[itemPid]="record.metadata.pid"
[permissions]="permissions.PTTR_ACCESS"
></admin-item-fees>
<!-- LEGACY -->
@if ((record.metadata | keyExists:'legacy_checkout_count') || (record.metadata | keyExists:'legacy_circulation_rules')) {
<section class="mb-4 mt-4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { IssueService } from '@app/admin/service/issue.service';
import { RecordPermissionService } from '@app/admin/service/record-permission.service';
import { RecordService } from '@rero/ng-core';
import { DetailRecord } from '@rero/ng-core/lib/record/detail/view/detail-record';
import { IPermissions, IssueItemStatus, PERMISSIONS, UserService } from '@rero/shared';
import { IPermissions, IssueItemStatus, PERMISSIONS, PERMISSION_OPERATOR, UserService } from '@rero/shared';
import moment from 'moment';
import { Observable, Subscription } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
Expand Down Expand Up @@ -99,11 +99,9 @@ export class ItemDetailViewComponent implements DetailRecord, OnInit, OnDestroy
.sort((a: string, b: string) => new Date(b).getTime() - new Date(a).getTime());
}

/**
* Permissions
* @return Object with all permissions
*/
/** Permissions */
permissions: IPermissions = PERMISSIONS;
permissionOperator = PERMISSION_OPERATOR;

/**
* Constructor
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!--
RERO ILS UI
Copyright (C) 2019-2024 RERO
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
@if (fees.length > 0) {
<div class="card" id="item-fees">
<div class="card-header">
<b translate>Fees</b>
<div class="float-right">
<span class="font-weight-bold mr-2" translate>Total</span>
<span class="badge badge-danger ml-1">{{ total | currency: organisation.default_currency }}</span>
</div>
</div>
<div class="card-body">
<div class="row mb-2">
<div class="col-10 font-weight-bold" translate>Patron</div>
<div class="col-2 font-weight-bold text-right" translate>Amount</div>
</div>
@for (fee of fees; track fee) {
@if (fee.metadata.patron.pid | getRecord:'patrons' | async; as patron) {
<div class="row mb-2">
<div class="col-10">
<a [routerLink]="['/circulation', 'patron', $any(patron).metadata.patron.barcode[0], 'fees']" [queryParams]="{event: fee.metadata.pid}">
{{ $any(patron).metadata.last_name }} {{ $any(patron).metadata.first_name }} ({{ $any(patron).metadata.patron.barcode[0] }})
</a>
</div>
<div class="col-2 text-right">{{ fee.metadata.total_amount | currency: organisation.default_currency }}</div>
</div>
}
}
</div>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* RERO ILS UI
* Copyright (C) 2019-2024 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Input, OnInit } from '@angular/core';
import { PatronTransactionApiService } from '@app/admin/api/patron-transaction-api.service';
import { OrganisationService } from '@app/admin/service/organisation.service';
import { tap } from 'rxjs';

@Component({
selector: 'admin-item-fees',
templateUrl: './item-fees.component.html'
})
export class ItemFeesComponent implements OnInit {

/** Item pid */
@Input() itemPid: string;

/** Fees */
fees: any[] = [];

/** Total fees */
total: number = 0;

/**
* Get the current organisation
* @return Organisation
*/
get organisation() {
return this.organisationService.organisation;
}

/**
* Constructor
* @param patronTransactionApiService - PatronTransactionApiService
* @param organisationService - OrganisationService
*/
public constructor(
private patronTransactionApiService: PatronTransactionApiService,
private organisationService: OrganisationService
) {}

/** OnInit hook */
ngOnInit(): void {
this.patronTransactionApiService.getActiveFeesByItemPid(this.itemPid)
.pipe(tap((fees: any) => fees.map((fee: any) => this.total += fee.metadata.total_amount)))
.subscribe((fees: any) => this.fees = fees);
}
}

0 comments on commit eef0e07

Please sign in to comment.