View | Details | Raw Unified | Return to bug 41129
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/modals/booking/island.inc (-16 / +21 lines)
Lines 10-15 Link Here
10
    hydrate();
10
    hydrate();
11
11
12
    const island = document.querySelector("booking-modal-island");
12
    const island = document.querySelector("booking-modal-island");
13
    const islandReady = (async () => {
14
        await customElements.whenDefined("booking-modal-island");
15
        return document.querySelector("booking-modal-island");
16
    })();
13
17
14
    const parseJson = value => {
18
    const parseJson = value => {
15
        if (!value) return [];
19
        if (!value) return [];
Lines 21-28 Link Here
21
        }
25
        }
22
    };
26
    };
23
27
24
    const normalizeProps = source => {
28
    const normalizeProps = (targetIsland, source) => {
25
        if (!island || !source) return;
29
        if (!targetIsland || !source) return;
26
30
27
        const bookingId = source.booking ?? source.bookingId ?? null;
31
        const bookingId = source.booking ?? source.bookingId ?? null;
28
        const itemId = source.itemnumber ?? source.itemId ?? null;
32
        const itemId = source.itemnumber ?? source.itemId ?? null;
Lines 36-67 Link Here
36
        const biblionumber =
40
        const biblionumber =
37
            source.biblionumber ?? source.biblio_id ?? source.biblioId;
41
            source.biblionumber ?? source.biblio_id ?? source.biblioId;
38
42
39
        island.bookingId = bookingId;
43
        targetIsland.bookingId = bookingId;
40
        island.itemId = itemId;
44
        targetIsland.itemId = itemId;
41
        island.patronId = patronId;
45
        targetIsland.patronId = patronId;
42
        island.pickupLibraryId = pickupLibraryId;
46
        targetIsland.pickupLibraryId = pickupLibraryId;
43
        island.startDate = startDate;
47
        targetIsland.startDate = startDate;
44
        island.endDate = endDate;
48
        targetIsland.endDate = endDate;
45
        island.itemtypeId = itemtypeId;
49
        targetIsland.itemtypeId = itemtypeId;
46
        island.selectedDateRange = startDate
50
        targetIsland.selectedDateRange = startDate
47
            ? [startDate, endDate || startDate]
51
            ? [startDate, endDate || startDate]
48
            : [];
52
            : [];
49
53
50
        const extendedAttributes =
54
        const extendedAttributes =
51
            source.extendedAttributes ?? source.extended_attributes ?? [];
55
            source.extendedAttributes ?? source.extended_attributes ?? [];
52
        island.extendedAttributes = Array.isArray(extendedAttributes)
56
        targetIsland.extendedAttributes = Array.isArray(extendedAttributes)
53
            ? extendedAttributes
57
            ? extendedAttributes
54
            : parseJson(extendedAttributes);
58
            : parseJson(extendedAttributes);
55
59
56
        if (biblionumber) {
60
        if (biblionumber) {
57
            island.biblionumber = biblionumber;
61
            targetIsland.biblionumber = biblionumber;
58
        }
62
        }
59
    };
63
    };
60
64
61
    const openModal = props => {
65
    const openModal = async props => {
62
        if (!island) return;
66
        const targetIsland = await islandReady;
63
        normalizeProps(props || {});
67
        if (!targetIsland) return;
64
        island.open = true;
68
        normalizeProps(targetIsland, props || {});
69
        targetIsland.open = true;
65
    };
70
    };
66
71
67
    if (island) {
72
    if (island) {
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/BookingModal.vue (+1 lines)
Lines 770-775 onMounted(() => { Link Here
770
            keyboard: false,
770
            keyboard: false,
771
        });
771
        });
772
        modalElement.value.addEventListener("hidden.bs.modal", () => {
772
        modalElement.value.addEventListener("hidden.bs.modal", () => {
773
            modalState.isOpen = false;
773
            emit("close");
774
            emit("close");
774
            resetModalState();
775
            resetModalState();
775
        });
776
        });
(-)a/t/cypress/integration/Circulation/bookingsModalBasic_spec.ts (-1 / +161 lines)
Lines 767-772 describe("Booking Modal Basic Tests", () => { Link Here
767
        });
767
        });
768
    });
768
    });
769
769
770
    it("should refresh edit modal state across consecutive openings", () => {
771
        const today = dayjs().startOf("day");
772
        let secondPatron;
773
        let firstBookingId;
774
        let secondBookingId;
775
776
        const firstBooking = {
777
            start: today.add(8, "day"),
778
            end: today.add(10, "day"),
779
            patron_id: testData.patron.patron_id,
780
            patron_label: testData.patron.surname,
781
        };
782
783
        cy.task("buildSampleObject", {
784
            object: "patron",
785
            values: {
786
                firstname: "Jane",
787
                surname: "Roe",
788
                cardnumber: `TEST${Date.now()}B`,
789
                category_id: "PT",
790
                library_id: testData.libraries[0].library_id,
791
            },
792
        }).then(mockPatron => {
793
            secondPatron = mockPatron;
794
795
            return cy.task("query", {
796
                sql: `INSERT INTO borrowers (borrowernumber, firstname, surname, cardnumber, categorycode, branchcode, dateofbirth)
797
                      VALUES (?, ?, ?, ?, ?, ?, ?)`,
798
                values: [
799
                    mockPatron.patron_id,
800
                    mockPatron.firstname,
801
                    mockPatron.surname,
802
                    mockPatron.cardnumber,
803
                    mockPatron.category_id,
804
                    mockPatron.library_id,
805
                    "1990-01-01",
806
                ],
807
            });
808
        });
809
810
        cy.then(() =>
811
            cy
812
                .task("query", {
813
                    sql: `INSERT INTO bookings (biblio_id, item_id, patron_id, start_date, end_date, pickup_library_id, status)
814
                          VALUES (?, ?, ?, ?, ?, ?, '1')`,
815
                    values: [
816
                        testData.biblio.biblio_id,
817
                        testData.items[0].item_id,
818
                        firstBooking.patron_id,
819
                        firstBooking.start.format("YYYY-MM-DD HH:mm:ss"),
820
                        firstBooking.end.format("YYYY-MM-DD HH:mm:ss"),
821
                        testData.libraries[0].library_id,
822
                    ],
823
                })
824
                .then(result => {
825
                    firstBookingId = result.insertId;
826
                })
827
        );
828
829
        cy.then(() => {
830
            const secondBooking = {
831
                start: today.add(15, "day"),
832
                end: today.add(17, "day"),
833
                patron_id: secondPatron.patron_id,
834
                patron_label: secondPatron.surname,
835
            };
836
837
            return cy
838
                .task("query", {
839
                    sql: `INSERT INTO bookings (biblio_id, item_id, patron_id, start_date, end_date, pickup_library_id, status)
840
                          VALUES (?, ?, ?, ?, ?, ?, '1')`,
841
                    values: [
842
                        testData.biblio.biblio_id,
843
                        testData.items[1].item_id,
844
                        secondBooking.patron_id,
845
                        secondBooking.start.format("YYYY-MM-DD HH:mm:ss"),
846
                        secondBooking.end.format("YYYY-MM-DD HH:mm:ss"),
847
                        testData.libraries[0].library_id,
848
                    ],
849
                })
850
                .then(result => {
851
                    secondBookingId = result.insertId;
852
                })
853
                .then(() => secondBooking);
854
        }).then(secondBooking => {
855
            cy.visit(
856
                `/cgi-bin/koha/catalogue/detail.pl?biblionumber=${testData.biblio.biblio_id}`
857
            );
858
            cy.get("booking-modal-island .modal").should("exist");
859
860
            // First open: booking A
861
            cy.window().then(win => {
862
                win.openBookingModal({
863
                    booking: String(firstBookingId),
864
                    patron: String(firstBooking.patron_id),
865
                    itemnumber: String(testData.items[0].item_id),
866
                    pickup_library: testData.libraries[0].library_id,
867
                    start_date: firstBooking.start.startOf("day").toISOString(),
868
                    end_date: firstBooking.end.endOf("day").toISOString(),
869
                    biblionumber: String(testData.biblio.biblio_id),
870
                });
871
            });
872
873
            cy.get("booking-modal-island .modal", { timeout: 10000 }).should(
874
                "be.visible"
875
            );
876
            cy.vueSelectShouldHaveValue(
877
                "booking_patron",
878
                firstBooking.patron_label
879
            );
880
881
            cy.get("booking-modal-island .modal .btn-close").first().click();
882
            cy.get("booking-modal-island .modal").should("not.be.visible");
883
            cy.get("body").should("not.have.class", "modal-open");
884
885
            // Second open: booking B (must not stay stale with booking A data)
886
            cy.window().then(win => {
887
                win.openBookingModal({
888
                    booking: String(secondBookingId),
889
                    patron: String(secondBooking.patron_id),
890
                    itemnumber: String(testData.items[1].item_id),
891
                    pickup_library: testData.libraries[0].library_id,
892
                    start_date: secondBooking.start
893
                        .startOf("day")
894
                        .toISOString(),
895
                    end_date: secondBooking.end.endOf("day").toISOString(),
896
                    biblionumber: String(testData.biblio.biblio_id),
897
                });
898
            });
899
900
            cy.get("booking-modal-island .modal", { timeout: 10000 }).should(
901
                "be.visible"
902
            );
903
            cy.vueSelectShouldHaveValue(
904
                "booking_patron",
905
                secondBooking.patron_label
906
            );
907
        });
908
909
        cy.then(() => {
910
            if (firstBookingId) {
911
                cy.task("query", {
912
                    sql: "DELETE FROM bookings WHERE booking_id = ?",
913
                    values: [firstBookingId],
914
                });
915
            }
916
            if (secondBookingId) {
917
                cy.task("query", {
918
                    sql: "DELETE FROM bookings WHERE booking_id = ?",
919
                    values: [secondBookingId],
920
                });
921
            }
922
            if (secondPatron) {
923
                cy.task("query", {
924
                    sql: "DELETE FROM borrowers WHERE borrowernumber = ?",
925
                    values: [secondPatron.patron_id],
926
                });
927
            }
928
        });
929
    });
930
770
    it("should handle booking failure gracefully", () => {
931
    it("should handle booking failure gracefully", () => {
771
        /**
932
        /**
772
         * Comprehensive Error Handling and Recovery Test
933
         * Comprehensive Error Handling and Recovery Test
773
- 

Return to bug 41129