Bugzilla – Attachment 193734 Details for
Bug 14962
Temp Shelving Location / On Display Module
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 14962: On Display Cypress e2e tests
f9b49c7.patch (text/plain), 23.70 KB, created by
Martin Renvoize (ashimema)
on 2026-02-24 13:36:14 UTC
(
hide
)
Description:
Bug 14962: On Display Cypress e2e tests
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2026-02-24 13:36:14 UTC
Size:
23.70 KB
patch
obsolete
>From f9b49c78f4ff12fef0783a8cdf411b3ecd96e08c Mon Sep 17 00:00:00 2001 >From: Jake Deery <jake.deery@openfifth.co.uk> >Date: Mon, 8 Dec 2025 16:22:40 +0000 >Subject: [PATCH] Bug 14962: On Display Cypress e2e tests > >This patch contains the front-end tests for the On Display module. If you'd like to verify the Perl methods, see the perl test harness patch. You can also use the test plan below to test the entire patchset works as expected. > >TO TEST (AUTOMATED E2E): >APPLY PATCHSET >a) cd $PROJECTS_DIR/koha >b) DB_HOSTNAME=localhost npx yarn cypress open > *) you may need to run `corepack enable` if you get "command not found" >c) click E2E testing >d) click start E2E testing in Electron >e) under Display, click Main.ts > *) verify it runs through fully with green >f) return to last page, under Display, click Displays_spec.ts > *) verify it runs through fully with green >SIGN OFF > >TO TEST (MANUALLY): >APPLY PATCHSET >a) go to /cgi-bin/koha/display/display-home.pl > *) notice module is disabled >b) go to /cgi-bin/koha/admin/preferences.pl?tab=&op=search&searchfield=UseDisplayModule > *) set UseDisplayModule syspref to "Use" >c) repeat step a > *) notice module is now enabled >d) click "New display" >e) > >Sponsored-by: ByWater Solutions >Signed-of-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> >--- > .../integration/Display/Displays_spec.ts | 453 ++++++++++++++++++ > t/cypress/integration/Display/Main.ts | 39 ++ > t/cypress/support/e2e.js | 134 ++++++ > 3 files changed, 626 insertions(+) > create mode 100644 t/cypress/integration/Display/Displays_spec.ts > create mode 100644 t/cypress/integration/Display/Main.ts > >diff --git a/t/cypress/integration/Display/Displays_spec.ts b/t/cypress/integration/Display/Displays_spec.ts >new file mode 100644 >index 00000000000..3bb0261170e >--- /dev/null >+++ b/t/cypress/integration/Display/Displays_spec.ts >@@ -0,0 +1,453 @@ >+const dayjs = require("dayjs"); /* Cannot use our calendar JS code, it's in an include file (!) >+ Also note that moment.js is deprecated */ >+ >+const dates = { >+ today_iso: dayjs().format("YYYY-MM-DD"), >+ today_us: dayjs().format("MM/DD/YYYY"), >+ tomorrow_iso: dayjs().add(1, "day").format("YYYY-MM-DD"), >+ tomorrow_us: dayjs().add(1, "day").format("MM/DD/YYYY"), >+}; >+ >+describe("Displays - CRUD operations", () => { >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.intercept( >+ "GET", >+ "/api/v1/displays/config", >+ '{"settings":{"enabled":"1"}}' >+ ); >+ }); >+ >+ it("Add display", () => { >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, []); >+ cy.visit("/cgi-bin/koha/display/display-home.pl"); >+ >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ >+ cy.contains("New display").click(); >+ cy.get("#display_name").type(display.display_name); >+ cy.get("#display_branch .vs__search").type("centerville" + "{enter}", { >+ force: true, >+ }); >+ cy.get("#display_holding_branch .vs__search").type( >+ "centerville" + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ cy.get("#display_location .vs__search").type( >+ "general stacks" + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ cy.get("#display_code .vs__search").type("reference" + "{enter}", { >+ force: true, >+ }); >+ cy.get("#display_itype .vs__search").type("books" + "{enter}", { >+ force: true, >+ }); >+ cy.get("#display_return_over .vs__search").type( >+ "yes, any library" + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ cy.get("#start_date") >+ .invoke("attr", "value", display.start_date) >+ .should("have.attr", "value", display.start_date); >+ cy.get("#end_date") >+ .invoke("attr", "value", display.start_date) >+ .should("have.attr", "value", display.start_date); >+ cy.get("#display_days").type(display.display_days); >+ cy.get("#staff_note").type(display.staff_note); >+ cy.get("#public_note").type(display.public_note); >+ cy.get("#enabled_yes").click(); >+ >+ // Submit the form, get 500 >+ cy.intercept("POST", "/api/v1/displays", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#displays_add").contains("Save").click(); >+ cy.get("main div[class='alert alert-warning']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("POST", "/api/v1/displays", { >+ statusCode: 201, >+ body: display, >+ }); >+ cy.get("#displays_add").contains("Save").click(); >+ cy.get("main div[class='alert alert-info']").contains( >+ "Display created" >+ ); >+ }); >+ >+ it("List displays", () => { >+ // GET displays returns 500 >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 500, >+ }); >+ cy.visit("/cgi-bin/koha/display/display-home.pl"); >+ cy.get(".sidebar_menu a").contains("Displays").click(); >+ cy.get("main div[class='alert alert-warning']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // GET displays returns empty list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, []); >+ cy.visit("/cgi-bin/koha/display/displays"); >+ cy.get("#displays_list").contains("There are no displays defined"); >+ >+ // GET displays returns something >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }); >+ cy.visit("/cgi-bin/koha/display/displays"); >+ cy.get("#displays_list").contains("Showing 1 to 1 of 1 entries"); >+ }); >+ >+ it("Show display", () => { >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ let item = cy.getItem(); >+ >+ // Click the "name" link from the list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }).as("get-displays"); >+ cy.intercept("GET", /\/api\/v1\/displays\/\d+/, display).as( >+ "get-display" >+ ); >+ cy.intercept("GET", /\/api\/v1\/items\/\d+/, { >+ statusCode: 200, >+ body: item, >+ }).as("get-item"); >+ >+ cy.visit("/cgi-bin/koha/display/displays"); >+ cy.wait("@get-displays"); >+ let id_cell = cy.get("#displays_list table tbody tr:first td:first"); >+ id_cell.contains(display.display_id); >+ >+ let name_link = cy >+ .get("#displays_list table tbody tr:first td") >+ .eq(1) >+ .find("a"); >+ name_link.should("have.text", display.display_name); >+ name_link.click(); >+ cy.wait("@get-item"); >+ cy.get("#displays_show h2").contains("Display #" + display.display_id); >+ cy.left_menu_active_item_is("Displays"); >+ }); >+ >+ it("Edit display", () => { >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ let item = cy.getItem(); >+ >+ // Click the 'Edit' button from the list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }).as("get-displays"); >+ cy.intercept("GET", /\/api\/v1\/displays\/\d+/, display).as( >+ "get-display" >+ ); >+ cy.intercept("GET", /\/api\/v1\/items\/\d+/, { >+ statusCode: 200, >+ body: item, >+ }).as("get-item"); >+ >+ cy.visit("/cgi-bin/koha/display/displays"); >+ cy.wait("@get-displays"); >+ cy.get("#displays_list table tbody tr:first").contains("Edit").click(); >+ cy.wait("@get-item"); >+ cy.get("#displays_add h2").contains("Edit display"); >+ cy.left_menu_active_item_is("Displays"); >+ >+ // Form has been correctly filled in >+ cy.get("#display_name").should("have.value", display.display_name); >+ >+ cy.get("#display_holding_branch .vs__selected").contains("Centerville"); >+ cy.get("#display_code .vs__selected").contains("Reference"); >+ cy.get("#start_date").invoke("val").should("eq", "2025-01-01"); >+ cy.get("#end_date").invoke("val").should("eq", "2036-12-31"); >+ cy.get("#public_note").invoke("val").should("eq", "A public note"); >+ >+ // Test related item >+ cy.get("#display_items_barcode_0") >+ .invoke("val") >+ .should("eq", item.external_id); >+ >+ // Submit the form, get 500 >+ cy.intercept("PUT", "/api/v1/displays/*", { >+ statusCode: 500, >+ }); >+ cy.get("#displays_add").contains("Save").click(); >+ cy.get("main div[class='alert alert-warning']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("PUT", "/api/v1/displays/*", { >+ statusCode: 200, >+ body: display, >+ }); >+ cy.get("#displays_add").contains("Save").click(); >+ cy.get("main div[class='alert alert-info']").contains( >+ "Display updated" >+ ); >+ }); >+ >+ it("Delete display", () => { >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ let item = cy.getItem(); >+ >+ // Click the 'Delete' button from the list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }); >+ cy.intercept("GET", /\/api\/v1\/displays\/\d+/, display); >+ cy.visit("/cgi-bin/koha/display/displays"); >+ >+ cy.get("#displays_list table tbody tr:first") >+ .contains("Delete") >+ .click(); >+ cy.get(".alert-warning.confirmation h1").contains( >+ "remove this display" >+ ); >+ cy.contains(display.display_name); >+ >+ // Accept the confirmation dialog, get 500 >+ cy.intercept("DELETE", "/api/v1/displays/*", { >+ statusCode: 500, >+ }); >+ cy.contains("Yes, delete").click(); >+ cy.get("main div[class='alert alert-warning']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Accept the confirmation dialog, success! >+ cy.intercept("DELETE", "/api/v1/displays/*", { >+ statusCode: 204, >+ body: null, >+ }); >+ cy.get("#displays_list table tbody tr:first") >+ .contains("Delete") >+ .click(); >+ cy.get(".alert-warning.confirmation h1").contains( >+ "remove this display" >+ ); >+ cy.contains("Yes, delete").click(); >+ cy.get("main div[class='alert alert-info']") >+ .contains("Display") >+ .contains("deleted"); >+ >+ // Delete from show >+ // Click the "name" link from the list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }).as("get-displays"); >+ cy.intercept("GET", /\/api\/v1\/displays\/\d+/, display).as( >+ "get-display" >+ ); >+ cy.intercept("GET", /\/api\/v1\/items\/\d+/, { >+ statusCode: 200, >+ body: item, >+ }).as("get-item"); >+ >+ cy.visit("/cgi-bin/koha/display/displays"); >+ cy.wait("@get-displays"); >+ let id_cell = cy.get("#displays_list table tbody tr:first td:first"); >+ id_cell.contains(display.display_id); >+ >+ let name_link = cy >+ .get("#displays_list table tbody tr:first td") >+ .eq(1) >+ .find("a"); >+ name_link.should("have.text", display.display_name); >+ name_link.click(); >+ cy.wait("@get-display"); >+ cy.get("#displays_show h2").contains("Display #" + display.display_id); >+ >+ cy.get("#displays_show #toolbar").contains("Delete").click(); >+ cy.get(".alert-warning.confirmation h1").contains( >+ "remove this display" >+ ); >+ cy.contains("Yes, delete").click(); >+ >+ //Make sure we return to list after deleting from show >+ cy.get("#displays_list table tbody tr:first"); >+ }); >+ >+ it("Batch add items", () => { >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ let batch = { >+ job_id: 1, >+ message: "Batch add operation queued", >+ }; >+ let batches = [batch]; >+ let item = cy.getItem(); >+ >+ // Click the "batch add" link from the list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }).as("get-displays"); >+ cy.intercept("GET", /\/api\/v1\/displays\/\d+/, display).as( >+ "get-display" >+ ); >+ cy.intercept("GET", /\/api\/v1\/items\/\d+/, { >+ statusCode: 200, >+ body: item, >+ }).as("get-item"); >+ >+ cy.visit("/cgi-bin/koha/display/display-home.pl"); >+ cy.contains("Batch add items from list").click(); >+ >+ cy.get("#barcodes").type("39999000002355\n39999000002331\n\n"); >+ cy.get("#display_id .vs__search").type( >+ display.display_name + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ cy.get("#date_remove") >+ .invoke("attr", "value", display.end_date) >+ .should("have.attr", "value", display.end_date); >+ >+ // Submit the form, get 500 >+ cy.intercept("POST", "/api/v1/display/items/batch", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#list").contains("Save").click(); >+ cy.get("main div[class='alert alert-warning']").contains( >+ "Internal Server Error. Please check the browser console for diagnostic information." >+ ); >+ >+ cy.get("#barcodes").type("39999000002355\n39999000002331\n\n"); >+ cy.get("#display_id .vs__search").type( >+ display.display_name + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ cy.get("#date_remove") >+ .invoke("attr", "value", display.end_date) >+ .should("have.attr", "value", display.end_date); >+ >+ // Submit the form, success! >+ cy.intercept("POST", "/api/v1/display/items/batch", { >+ statusCode: 202, >+ body: batch, >+ }); >+ cy.get("#list").contains("Save").click(); >+ cy.get("main div[class='alert alert-info']").contains( >+ "Batch job successfully queued" >+ ); >+ }); >+ >+ it("Batch delete items", () => { >+ let display = cy.getDisplay(); >+ let displays = [display]; >+ let batch = { >+ job_id: 2, >+ message: "Batch add operation queued", >+ }; >+ let batches = [batch]; >+ let item = cy.getItem(); >+ >+ // Click the "batch add" link from the list >+ cy.intercept("GET", /\/api\/v1\/displays.(?!config).*/, { >+ statusCode: 200, >+ body: displays, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }).as("get-displays"); >+ cy.intercept("GET", /\/api\/v1\/displays\/\d+/, display).as( >+ "get-display" >+ ); >+ cy.intercept("GET", /\/api\/v1\/items\/\d+/, { >+ statusCode: 200, >+ body: item, >+ }).as("get-item"); >+ >+ cy.visit("/cgi-bin/koha/display/display-home.pl"); >+ cy.contains("Batch remove items from list").click(); >+ >+ cy.get("#barcodes").type("39999000002355\n39999000002331\n\n"); >+ cy.get("#display_id .vs__search").type( >+ display.display_name + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ >+ // Submit the form, get 500 >+ cy.intercept("DELETE", "/api/v1/display/items/batch", { >+ statusCode: 500, >+ }); >+ cy.get("#list").contains("Save").click(); >+ cy.get("main div[class='alert alert-warning']").contains( >+ "Internal Server Error. Please check the browser console for diagnostic information." >+ ); >+ >+ cy.get("#barcodes").type("39999000002355\n39999000002331\n\n"); >+ cy.get("#display_id .vs__search").type( >+ display.display_name + "{enter}", >+ { >+ force: true, >+ } >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("DELETE", "/api/v1/display/items/batch", { >+ statusCode: 202, >+ body: batch, >+ }); >+ cy.get("#list").contains("Save").click(); >+ cy.get("main div[class='alert alert-info']").contains( >+ "Batch job successfully queued" >+ ); >+ }); >+}); >diff --git a/t/cypress/integration/Display/Main.ts b/t/cypress/integration/Display/Main.ts >new file mode 100644 >index 00000000000..ef30b7295b0 >--- /dev/null >+++ b/t/cypress/integration/Display/Main.ts >@@ -0,0 +1,39 @@ >+describe("Main component - pref off", () => { >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.intercept( >+ "GET", >+ "/api/v1/displays/config", >+ '{"settings":{"enabled":"0"}}' >+ ); >+ }); >+ >+ it("Home", () => { >+ cy.visit("/cgi-bin/koha/display/display-home.pl"); >+ cy.get(".main .sidebar_menu").should("not.exist"); >+ cy.get(".main div[class='alert alert-warning']").contains( >+ "The displays module is disabled, turn on UseDisplayModule to use it" >+ ); >+ cy.get(".main div[class='alert alert-warning'] a").click(); >+ cy.url().should("match", /\/cgi-bin\/koha\/admin\/preferences.pl/); >+ }); >+}); >+ >+describe("Main component - pref on", () => { >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.intercept( >+ "GET", >+ "/api/v1/displays/config", >+ '{"settings":{"enabled":"1"}}' >+ ); >+ }); >+ >+ it("Home", () => { >+ cy.visit("/cgi-bin/koha/display/display-home.pl"); >+ cy.get(".main .sidebar_menu").should("exist"); >+ cy.get(".main div[class='alert alert-warning']").should("not.exist"); >+ }); >+}); >diff --git a/t/cypress/support/e2e.js b/t/cypress/support/e2e.js >index 287616198fb..b180d49c151 100644 >--- a/t/cypress/support/e2e.js >+++ b/t/cypress/support/e2e.js >@@ -1963,6 +1963,64 @@ cy.getSIP2Institution = () => { > }; > }; > >+cy.getItem = () => { >+ return { >+ acquisition_date: "2014-09-04", >+ acquisition_source: null, >+ biblio_id: 58, >+ bookable: false, >+ call_number_sort: "_", >+ call_number_source: null, >+ callnumber: null, >+ checked_out_date: null, >+ checkouts_count: null, >+ coded_location_qualifier: null, >+ collection_code: "REF", >+ copy_number: null, >+ damaged_date: null, >+ damaged_status: 0, >+ effective_bookable: false, >+ effective_collection_code: "REF", >+ effective_holding_library_id: "CPL", >+ effective_home_library_id: "CPL", >+ effective_item_type_id: "BK", >+ effective_location: "GEN", >+ effective_not_for_loan_status: 0, >+ exclude_from_local_holds_priority: false, >+ extended_subfields: null, >+ external_id: "39999000002355", >+ holding_library_id: "CPL", >+ holds_count: null, >+ home_library_id: "CPL", >+ internal_notes: null, >+ inventory_number: null, >+ item_id: 125, >+ item_type_id: "BK", >+ last_checkout_date: null, >+ last_seen_date: "2014-09-04T00:00:00+00:00", >+ localuse: null, >+ location: "GEN", >+ lost_date: null, >+ lost_status: 0, >+ materials_notes: null, >+ new_status: null, >+ not_for_loan_status: 0, >+ permanent_location: "GEN", >+ public_notes: null, >+ purchase_price: null, >+ renewals_count: null, >+ replacement_price: null, >+ replacement_price_date: "2014-09-04", >+ restricted_status: null, >+ serial_issue_number: null, >+ shelving_control_number: null, >+ timestamp: "2020-01-29T13:06:19+00:00", >+ uri: null, >+ withdrawn: 0, >+ withdrawn_date: null, >+ }; >+}; >+ > cy.getItemTypes = () => { > return [ > { >@@ -2144,6 +2202,82 @@ cy.getItemTypes = () => { > ]; > }; > >+cy.getDisplay = () => { >+ return { >+ display_branch: "CPL", >+ display_code: "REF", >+ display_days: 4382, >+ display_holding_branch: "CPL", >+ display_id: 1, >+ display_items: [ >+ { >+ biblionumber: 58, >+ date_added: null, >+ date_remove: "2036-12-31", >+ display_id: 1, >+ display_item_id: 1, >+ itemnumber: 125, >+ }, >+ ], >+ display_itype: "BK", >+ display_location: "GEN", >+ display_name: "Test display #1", >+ display_return_over: "yes - any library", >+ enabled: true, >+ end_date: "2036-12-31", >+ item_type: { >+ automatic_checkin: false, >+ bookable: false, >+ checkin_message: null, >+ checkin_message_type: "message", >+ checkprevcheckout: "inherit", >+ daily_rental_charge: null, >+ daily_rental_charge_calendar: true, >+ default_replacement_cost: null, >+ description: "Books", >+ hide_in_opac: false, >+ hourly_rental_charge: null, >+ hourly_rental_charge_calendar: true, >+ image_url: "bridge/book.png", >+ item_type_id: "BK", >+ not_for_loan_status: false, >+ parent_type: null, >+ process_fee: null, >+ rentalcharge: 0.0, >+ searchcategory: null, >+ sip_media_type: null, >+ summary: "", >+ }, >+ library: { >+ address1: "Jefferson Summit", >+ address2: null, >+ address3: null, >+ city: null, >+ country: null, >+ email: null, >+ fax: null, >+ geolocation: null, >+ ill_email: null, >+ ip: null, >+ library_id: "CPL", >+ marc_org_code: null, >+ name: "Centerville", >+ notes: null, >+ phone: null, >+ pickup_location: true, >+ postal_code: null, >+ public: true, >+ reply_to_email: null, >+ return_path_email: null, >+ state: null, >+ url: null, >+ }, >+ public_note: "A public note", >+ staff_note: "A staff-only note", >+ start_date: "2025-01-01", >+ }; >+}; >+ > Cypress.Commands.add("set_syspref", (variable, value) => { > cy.window().then(win => { > const client = win.APIClient.sysprefs; >-- >2.53.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 14962
:
190216
|
190217
|
190218
|
190219
|
190220
|
190221
|
190222
|
190223
|
190224
|
190225
|
190226
|
190227
|
190228
|
190229
|
190230
|
190231
|
190232
|
190571
|
190572
|
193719
|
193720
|
193721
|
193722
|
193723
|
193724
|
193725
|
193726
|
193727
|
193728
|
193729
|
193730
|
193731
|
193732
|
193733
|
193734
|
193735
|
193736
|
193737
|
193738
|
193739
|
193740
|
193741
|
193742
|
193743
|
193744
|
193745
|
193746
|
193747
|
193748
|
193749
|
193750
|
193751
|
193752
|
193753
|
193754
|
193755
|
194285
|
194286
|
194287
|
194288
|
194289
|
194290
|
194291
|
194292
|
194293
|
194294
|
194295
|
194296
|
194297
|
194298
|
194299
|
194300
|
194301
|
194302
|
194303
|
194304
|
194305
|
194306
|
194307
|
194308
|
194309
|
194310
|
194311
|
194312
|
194313
|
194314
|
194315
|
194316
|
194317
|
194318
|
194320
|
194321
|
194322