From 7379276999c06a7b03b25a9a45538d21589f0cc3 Mon Sep 17 00:00:00 2001 From: Dave Earley Date: Thu, 19 Sep 2024 22:48:05 -0700 Subject: [PATCH 01/43] Rename tickets to products --- .../DomainObjects/AttendeeDomainObject.php | 12 +- .../CapacityAssignmentDomainObject.php | 10 +- .../DomainObjects/CheckInListDomainObject.php | 10 +- .../Enums/CapacityAssignmentAppliesTo.php | 2 +- .../DomainObjects/Enums/MessageTypeEnum.php | 2 +- .../{TicketType.php => ProductPriceType.php} | 2 +- .../app/DomainObjects/Enums/ProductType.php | 11 + .../DomainObjects/Enums/QuestionBelongsTo.php | 2 +- .../app/DomainObjects/EventDomainObject.php | 10 +- .../AttendeeCheckInDomainObjectAbstract.php | 14 +- .../AttendeeDomainObjectAbstract.php | 28 +- ...ventDailyStatisticDomainObjectAbstract.php | 14 +- .../Generated/EventDomainObjectAbstract.php | 2 +- .../EventSettingDomainObjectAbstract.php | 14 +- .../EventStatisticDomainObjectAbstract.php | 14 +- .../Generated/MessageDomainObjectAbstract.php | 14 +- .../OrderItemDomainObjectAbstract.php | 28 +- .../OrganizerDomainObjectAbstract.php | 28 + ...apacityAssignmentDomainObjectAbstract.php} | 20 +- ...roductCheckInListDomainObjectAbstract.php} | 20 +- ...ct.php => ProductDomainObjectAbstract.php} | 20 +- ...p => ProductPriceDomainObjectAbstract.php} | 20 +- ...> ProductQuestionDomainObjectAbstract.php} | 20 +- ...ProductTaxAndFeesDomainObjectAbstract.php} | 20 +- .../PromoCodeDomainObjectAbstract.php | 14 +- .../QuestionAnswerDomainObjectAbstract.php | 14 +- .../TicketCheckInListDomainObjectAbstract.php | 76 -- .../TicketTaxDomainObjectAbstract.php | 62 -- .../DomainObjects/OrderItemDomainObject.php | 20 +- .../ProductCapacityAssignmentDomainObject.php | 7 + ...php => ProductCheckInListDomainObject.php} | 2 +- ...mainObject.php => ProductDomainObject.php} | 54 +- ...bject.php => ProductPriceDomainObject.php} | 8 +- .../ProductQuestionDomainObject.php | 7 + .../ProductTaxAndFeesDomainObject.php | 7 + .../ProductsCheckInListDomainObject.php | 7 + .../DomainObjects/PromoCodeDomainObject.php | 8 +- .../DomainObjects/QuestionDomainObject.php | 10 +- .../{TicketStatus.php => ProductStatus.php} | 2 +- .../TicketAttributeDomainObject.php | 7 - .../TicketCapacityAssignmentDomainObject.php | 7 - .../TicketCheckInListDomainObject.php | 7 - .../TicketQuestionDomainObject.php | 7 - .../TicketTaxAndFeesDomainObject.php | 7 - .../DomainObjects/TicketTaxDomainObject.php | 7 - .../CannotChangeTicketTypeException.php | 2 +- .../app/Exceptions/InvalidTicketPriceId.php | 2 +- .../NoTicketsAvailableException.php | 2 +- backend/app/Exports/AttendeesExport.php | 4 +- backend/app/Helper/Url.php | 2 +- .../Attendees/CreateAttendeeAction.php | 12 +- .../Actions/Attendees/EditAttendeeAction.php | 10 +- .../Attendees/ExportAttendeesAction.php | 2 +- .../Actions/Attendees/GetAttendeeAction.php | 10 +- .../Attendees/GetAttendeeActionPublic.php | 10 +- .../CreateCapacityAssignmentAction.php | 6 +- .../UpdateCapacityAssignmentAction.php | 6 +- .../CheckInLists/CreateCheckInListAction.php | 6 +- .../CheckInLists/UpdateCheckInListAction.php | 6 +- .../Actions/Events/DuplicateEventAction.php | 2 +- .../Http/Actions/Events/GetEventAction.php | 8 +- .../Actions/Messages/SendMessageAction.php | 2 +- .../Orders/CreateOrderActionPublic.php | 4 +- .../CreateProductAction.php} | 26 +- .../DeleteProductAction.php} | 18 +- .../EditProductAction.php} | 26 +- .../Actions/Products/GetProductAction.php | 37 + .../GetProductsAction.php} | 20 +- .../SortProductsAction.php} | 14 +- .../PromoCodes/CreatePromoCodeAction.php | 8 +- .../PromoCodes/UpdatePromoCodeAction.php | 8 +- .../Questions/CreateQuestionAction.php | 2 +- .../Actions/Questions/EditQuestionAction.php | 2 +- .../Actions/Questions/GetQuestionAction.php | 4 +- .../Actions/Questions/GetQuestionsAction.php | 8 +- .../Questions/GetQuestionsPublicAction.php | 4 +- .../Http/Actions/Tickets/GetTicketAction.php | 37 - .../Attendee/CreateAttendeeRequest.php | 4 +- .../Request/Attendee/EditAttendeeRequest.php | 12 +- .../UpsertCapacityAssignmentRequest.php | 4 +- .../CheckInList/UpsertCheckInListRequest.php | 4 +- .../Request/Event/DuplicateEventRequest.php | 2 +- .../UpdateEventSettingsRequest.php | 4 +- .../Request/Message/SendMessageRequest.php | 6 +- .../SortProductsRequest.php} | 4 +- .../UpsertProductRequest.php} | 12 +- .../CreateUpdatePromoCodeRequest.php | 2 +- .../Questions/UpsertQuestionRequest.php | 6 +- .../Jobs/Order/SendOrderDetailsEmailJob.php | 2 +- .../app/Mail/Attendee/AttendeeTicketMail.php | 6 +- backend/app/Models/Attendee.php | 4 +- backend/app/Models/AttendeeCheckIn.php | 4 +- backend/app/Models/CapacityAssignment.php | 6 +- backend/app/Models/CheckInList.php | 6 +- backend/app/Models/Event.php | 4 +- backend/app/Models/Message.php | 2 +- backend/app/Models/OrderItem.php | 8 +- .../app/Models/{Ticket.php => Product.php} | 20 +- .../{TicketPrice.php => ProductPrice.php} | 6 +- ...TicketQuestion.php => ProductQuestion.php} | 8 +- backend/app/Models/PromoCode.php | 4 +- backend/app/Models/Question.php | 6 +- backend/app/Models/QuestionAnswer.php | 2 +- backend/app/Models/TaxAndFee.php | 4 +- .../Providers/RepositoryServiceProvider.php | 12 +- .../Eloquent/AttendeeRepository.php | 4 +- .../Eloquent/CheckInListRepository.php | 4 +- .../Eloquent/ProductPriceRepository.php | 20 + .../Repository/Eloquent/ProductRepository.php | 220 ++++ .../Eloquent/QuestionRepository.php | 34 +- .../Eloquent/TicketPriceRepository.php | 20 - .../Repository/Eloquent/TicketRepository.php | 220 ---- .../ProductPriceRepositoryInterface.php | 15 + .../Interfaces/ProductRepositoryInterface.php | 89 ++ .../QuestionRepositoryInterface.php | 4 +- .../TicketPriceRepositoryInterface.php | 15 - .../Interfaces/TicketRepositoryInterface.php | 89 -- .../Resources/Attendee/AttendeeResource.php | 14 +- .../Attendee/AttendeeResourcePublic.php | 8 +- .../AttendeeWithCheckInPublicResource.php | 4 +- .../CapacityAssignmentResource.php | 10 +- .../CheckInList/CheckInListResource.php | 6 +- .../CheckInList/CheckInListResourcePublic.php | 6 +- backend/app/Resources/Event/EventResource.php | 4 +- .../Resources/Event/EventResourcePublic.php | 8 +- .../Resources/Event/EventSettingsResource.php | 2 +- .../Event/EventSettingsResourcePublic.php | 2 +- .../Event/EventStatisticsResource.php | 2 +- .../app/Resources/Message/MessageResource.php | 2 +- .../app/Resources/Order/OrderItemResource.php | 2 +- .../Order/OrderItemResourcePublic.php | 8 +- .../ProductMinimalResourcePublic.php} | 12 +- .../ProductPriceResource.php} | 8 +- .../ProductPriceResourcePublic.php} | 8 +- .../ProductResource.php} | 26 +- .../ProductResourcePublic.php} | 18 +- .../Resources/PromoCode/PromoCodeResource.php | 2 +- .../Resources/Question/QuestionResource.php | 6 +- .../Question/QuestionResourcePublic.php | 6 +- .../Attendee/SendAttendeeTicketService.php | 6 +- ...cityAssignmentTicketAssociationService.php | 30 +- .../CreateCapacityAssignmentService.php | 42 +- .../TicketsDoNotBelongToEventException.php | 2 +- .../UpdateCapacityAssignmentService.php | 30 +- .../CheckInList/CheckInListDataService.php | 8 +- .../CheckInListTicketAssociationService.php | 30 +- .../CreateAttendeeCheckInService.php | 4 +- .../CheckInList/CreateCheckInListService.php | 20 +- .../CheckInList/UpdateCheckInListService.php | 20 +- .../Domain/Event/CreateEventService.php | 2 +- .../Event/DTO/DuplicateEventDataDTO.php | 2 +- .../Event/DTO/EventDailyStatsResponseDTO.php | 2 +- .../Domain/Event/DuplicateEventService.php | 76 +- .../Domain/Event/EventStatsFetchService.php | 8 +- .../EventStatisticsUpdateService.php | 24 +- .../Mail/SendEventEmailMessagesService.php | 10 +- .../Domain/Mail/SendOrderDetailsService.php | 12 +- .../Domain/Order/OrderCancelService.php | 26 +- .../OrderCreateRequestValidationService.php | 264 ++--- .../Order/OrderItemProcessingService.php | 72 +- .../PaymentIntentSucceededHandler.php | 10 +- ...AvailableProductQuantitiesFetchService.php | 173 ++++ .../Domain/Product/CreateProductService.php | 117 +++ .../DTO/AvailableProductQuantitiesDTO.php} | 8 +- ...AvailableProductQuantitiesResponseDTO.php} | 8 +- .../Domain/Product/DTO/CreateProductDTO.php | 10 + .../DTO/OrderProductPriceDTO.php} | 6 +- .../{Ticket => Product}/DTO/PriceDTO.php | 2 +- .../Domain/Product/DTO/ProductPriceDTO.php | 22 + .../Product/EventProductValidationService.php | 35 + .../UnrecognizedProductIdException.php | 10 + .../Domain/Product/ProductFilterService.php | 223 ++++ .../ProductPriceCreateService.php} | 16 +- .../Domain/Product/ProductPriceService.php | 77 ++ .../ProductPriceUpdateService.php} | 52 +- .../ProductQuantityUpdateService.php} | 26 +- .../PromoCode/CreatePromoCodeService.php | 20 +- .../Domain/Question/CreateQuestionService.php | 4 +- .../Domain/Question/EditQuestionService.php | 10 +- ...s.php => TaxAndProductAssociateParams.php} | 4 +- .../Tax/TaxAndFeeCalculationService.php | 24 +- ...hp => TaxAndProductAssociationService.php} | 12 +- .../AvailableTicketQuantitiesFetchService.php | 173 ---- .../Domain/Ticket/CreateTicketService.php | 117 --- .../Domain/Ticket/DTO/CreateTicketDTO.php | 10 - .../Domain/Ticket/DTO/TicketPriceDTO.php | 22 - .../Ticket/EventTicketValidationService.php | 35 - .../UnrecognizedTicketIdException.php | 10 - .../Domain/Ticket/TicketFilterService.php | 223 ---- .../Domain/Ticket/TicketPriceService.php | 77 -- .../Attendee/CreateAttendeeHandler.php | 86 +- .../Attendee/DTO/CreateAttendeeDTO.php | 4 +- .../Handlers/Attendee/DTO/EditAttendeeDTO.php | 4 +- .../Handlers/Attendee/EditAttendeeHandler.php | 62 +- .../Attendee/PartialEditAttendeeHandler.php | 18 +- .../Attendee/ResendAttendeeTicketHandler.php | 18 +- .../CreateCapacityAssignmentHandler.php | 8 +- .../DTO/UpsertCapacityAssignmentDTO.php | 2 +- .../DeleteCapacityAssignmentHandler.php | 6 +- .../GetCapacityAssignmentHandler.php | 4 +- .../GetCapacityAssignmentsHandler.php | 4 +- .../UpdateCapacityAssignmentHandler.php | 8 +- .../CheckInList/CreateCheckInListHandler.php | 6 +- .../CheckInList/DTO/UpsertCheckInListDTO.php | 2 +- .../CheckInList/GetCheckInListHandler.php | 4 +- .../CheckInList/GetCheckInListsHandler.php | 4 +- .../GetCheckInListAttendeesPublicHandler.php | 4 +- .../Public/GetCheckInListPublicHandler.php | 4 +- .../CheckInList/UpdateCheckInlistHandler.php | 6 +- .../Event/DTO/EventStatsResponseDTO.php | 2 +- .../Handlers/Event/DuplicateEventHandler.php | 2 +- .../Handlers/Event/GetPublicEventHandler.php | 14 +- .../Handlers/Message/DTO/SendMessageDTO.php | 2 +- .../Handlers/Message/SendMessageHandler.php | 16 +- .../Handlers/Order/CompleteOrderHandler.php | 62 +- .../Handlers/Order/CreateOrderHandler.php | 2 +- .../Order/DTO/CompleteOrderAttendeeDTO.php | 2 +- .../Order/DTO/CreateOrderPublicDTO.php | 4 +- .../Order/DTO/TicketOrderDetailsDTO.php | 8 +- .../Handlers/Order/GetOrderPublicHandler.php | 12 +- .../Handlers/Product/CreateProductHandler.php | 60 ++ .../Handlers/Product/DTO/UpsertProductDTO.php | 41 + .../Handlers/Product/DeleteProductHandler.php | 75 ++ .../Handlers/Product/EditProductHandler.php | 138 +++ .../Handlers/Product/GetProductsHandler.php | 37 + .../Handlers/Product/SortProductsHandler.php | 41 + .../PromoCode/CreatePromoCodeHandler.php | 6 +- .../PromoCode/DTO/UpsertPromoCodeDTO.php | 2 +- .../PromoCode/UpdatePromoCodeHandler.php | 18 +- .../Question/CreateQuestionHandler.php | 2 +- .../Question/DTO/UpsertQuestionDTO.php | 2 +- .../Handlers/Question/EditQuestionHandler.php | 2 +- .../Question/SortQuestionsHandler.php | 2 +- .../Handlers/Ticket/CreateTicketHandler.php | 60 -- .../Handlers/Ticket/DTO/UpsertTicketDTO.php | 41 - .../Handlers/Ticket/DeleteTicketHandler.php | 75 -- .../Handlers/Ticket/EditTicketHandler.php | 138 --- .../Handlers/Ticket/GetTicketsHandler.php | 37 - .../Handlers/Ticket/SortTicketsHandler.php | 41 - .../app/Validators/CompleteOrderValidator.php | 28 +- .../Validators/Rules/AttendeeQuestionRule.php | 6 +- .../app/Validators/Rules/BaseQuestionRule.php | 24 +- ...9_20_032323_rename_tickets_to_products.php | 182 ++++ ...20_032838_add_product_type_to_products.php | 24 + backend/routes/api.php | 28 +- .../Domain/Order/OrderCancelServiceTest.php | 6 +- .../Event/GetPublicEventHandlerTest.php | 6 +- frontend/scripts/rename.sh | 67 ++ frontend/src/api/attendee.client.ts | 8 +- frontend/src/api/client.ts | 2 +- frontend/src/api/order.client.ts | 16 +- frontend/src/api/ticket.client.ts | 46 - .../common/AttendeeCheckInTable/QrScanner.tsx | 2 +- .../common/AttendeeCheckInTable/index.tsx | 6 +- .../common/AttendeeDetails/index.tsx | 6 +- .../AttendeeList/AttendeeList.module.scss | 2 +- .../components/common/AttendeeList/index.tsx | 8 +- .../AttendeeProduct.module.scss} | 6 +- .../index.tsx | 30 +- .../components/common/AttendeeTable/index.tsx | 26 +- .../common/CapacityAssignmentList/index.tsx | 22 +- .../common/CheckInListList/index.tsx | 16 +- .../common/CheckoutQuestion/index.tsx | 14 +- .../src/components/common/Currency/index.tsx | 14 +- .../src/components/common/EventCard/index.tsx | 2 +- .../common/EventDocumentHead/index.tsx | 6 +- .../common/HomepageInfoMessage/index.tsx | 2 +- .../components/common/MessageList/index.tsx | 2 +- .../common/PoweredByFooter/index.tsx | 2 +- .../index.tsx | 30 +- .../ProductsTable.module.scss} | 12 +- .../SortableProduct}/index.tsx | 102 +- .../{TicketsTable => ProductsTable}/index.tsx | 52 +- .../common/PromoCodeTable/index.tsx | 18 +- .../common/QuestionsTable/index.tsx | 8 +- .../src/components/common/StatBoxes/index.tsx | 4 +- .../components/common/WidgetEditor/index.tsx | 6 +- .../forms/CapaciyAssigmentForm/index.tsx | 22 +- .../forms/CheckInListForm/index.tsx | 18 +- .../ProductForm.module.scss} | 0 .../{TicketForm => ProductForm}/index.tsx | 100 +- .../components/forms/PromoCodeForm/index.tsx | 18 +- .../components/forms/QuestionForm/index.tsx | 22 +- .../components/forms/TaxAndFeeForm/index.tsx | 8 +- .../layouts/CheckIn/CheckIn.module.scss | 2 +- .../src/components/layouts/CheckIn/index.tsx | 8 +- .../src/components/layouts/Event/index.tsx | 2 +- .../EventHomepage/EventHomepage.module.scss | 4 +- .../ProductSelection.module.scss} | 0 .../EventHomepage/ProductSelection/index.tsx | 10 + .../EventHomepage/TicketSelection/index.tsx | 10 - .../layouts/EventHomepage/index.tsx | 10 +- .../ProductWidget.module.scss} | 0 .../{TicketWidget => ProductWidget}/index.tsx | 8 +- .../modals/CancelOrderModal/index.tsx | 6 +- .../modals/CreateAttendeeModal/index.tsx | 50 +- .../CreateCapacityAssignmentModal/index.tsx | 26 +- .../modals/CreateCheckInListModal/index.tsx | 26 +- .../index.tsx | 30 +- .../modals/CreatePromoCodeModal/index.tsx | 2 +- .../modals/CreateQuestionModal/index.tsx | 8 +- .../modals/DuplicateEventModal/index.tsx | 6 +- .../modals/EditAttendeeModal/index.tsx | 32 +- .../EditCapacityAssignmentModal/index.tsx | 8 +- .../modals/EditCheckInListModal/index.tsx | 8 +- .../index.tsx | 70 +- .../modals/EditPromoCodeModal/index.tsx | 4 +- .../modals/EditQuestionModal/index.tsx | 8 +- .../components/modals/EditUserModal/index.tsx | 2 +- .../modals/InviteUserModal/index.tsx | 2 +- .../modals/SendMessageModal/index.tsx | 38 +- .../modals/ViewAttendeeModal/index.tsx | 6 +- .../modals/ViewOrderModal/index.tsx | 6 +- .../sections/TaxSettings/index.tsx | 2 +- .../components/routes/auth/Register/index.tsx | 2 +- .../routes/event/EventDashboard/index.tsx | 6 +- .../routes/event/GettingStarted/index.tsx | 16 +- .../Settings/Sections/EmailSettings/index.tsx | 2 +- .../Sections/LocationSettings/index.tsx | 2 +- .../src/components/routes/event/tickets.tsx | 77 -- .../AttendeeProductAndInformation}/index.tsx | 14 +- .../CollectInformation.module.scss | 0 .../CollectInformation/index.tsx | 50 +- .../OrderSummaryAndProducts.module.scss} | 0 .../OrderSummaryAndProducts}/index.tsx | 16 +- .../Payment/Payment.module.scss | 0 .../Payment/index.tsx | 0 .../PaymentReturn/PaymentReturn.module.scss | 0 .../PaymentReturn/index.tsx | 0 .../PrintOrder/index.tsx | 10 +- .../PrintProduct}/index.tsx | 14 +- .../SelectProducts}/Prices/Tiered/index.tsx | 50 +- .../SelectProducts}/index.tsx | 142 +-- frontend/src/locales/de.js | 2 +- frontend/src/locales/de.po | 800 +++++++------- frontend/src/locales/en.js | 2 +- frontend/src/locales/en.po | 974 +++++++++--------- frontend/src/locales/es.js | 2 +- frontend/src/locales/es.po | 636 ++++++------ frontend/src/locales/fr.js | 2 +- frontend/src/locales/fr.po | 636 ++++++------ frontend/src/locales/pt-br.js | 2 +- frontend/src/locales/pt-br.po | 610 +++++------ frontend/src/locales/pt.js | 2 +- frontend/src/locales/pt.po | 638 ++++++------ frontend/src/locales/ru.js | 2 +- frontend/src/locales/ru.po | 718 ++++++------- frontend/src/locales/zh-cn.js | 2 +- frontend/src/locales/zh-cn.po | 596 +++++------ .../src/mutations/useCreateOrderPublic.ts | 4 +- frontend/src/mutations/useDeleteTicket.ts | 19 - .../src/mutations/useResendAttendeeTicket.ts | 12 - frontend/src/mutations/useSortTickets.ts | 12 - frontend/src/mutations/useUpdateTicket.ts | 20 - frontend/src/queries/useGetTicket.ts | 16 - frontend/src/queries/useGetTickets.ts | 13 - frontend/src/queries/useGetTicketsPublic.ts | 13 - frontend/src/router.tsx | 34 +- frontend/src/styles/product/default.scss | 3 + frontend/src/styles/ticket/default.scss | 3 - frontend/src/styles/widget/default.scss | 20 +- frontend/src/types.ts | 66 +- frontend/src/utilites/helpers.ts | 4 +- frontend/src/utilites/tickets.ts | 20 - 364 files changed, 6553 insertions(+), 6627 deletions(-) rename backend/app/DomainObjects/Enums/{TicketType.php => ProductPriceType.php} (87%) create mode 100644 backend/app/DomainObjects/Enums/ProductType.php rename backend/app/DomainObjects/Generated/{TicketCapacityAssignmentDomainObjectAbstract.php => ProductCapacityAssignmentDomainObjectAbstract.php} (79%) rename backend/app/DomainObjects/Generated/{TicketsCheckInListDomainObjectAbstract.php => ProductCheckInListDomainObjectAbstract.php} (73%) rename backend/app/DomainObjects/Generated/{TicketDomainObjectAbstract.php => ProductDomainObjectAbstract.php} (93%) rename backend/app/DomainObjects/Generated/{TicketPriceDomainObjectAbstract.php => ProductPriceDomainObjectAbstract.php} (90%) rename backend/app/DomainObjects/Generated/{TicketQuestionDomainObjectAbstract.php => ProductQuestionDomainObjectAbstract.php} (71%) rename backend/app/DomainObjects/Generated/{TicketTaxAndFeesDomainObjectAbstract.php => ProductTaxAndFeesDomainObjectAbstract.php} (64%) delete mode 100644 backend/app/DomainObjects/Generated/TicketCheckInListDomainObjectAbstract.php delete mode 100644 backend/app/DomainObjects/Generated/TicketTaxDomainObjectAbstract.php create mode 100644 backend/app/DomainObjects/ProductCapacityAssignmentDomainObject.php rename backend/app/DomainObjects/{TicketsCheckInListDomainObject.php => ProductCheckInListDomainObject.php} (54%) rename backend/app/DomainObjects/{TicketDomainObject.php => ProductDomainObject.php} (66%) rename backend/app/DomainObjects/{TicketPriceDomainObject.php => ProductPriceDomainObject.php} (89%) create mode 100644 backend/app/DomainObjects/ProductQuestionDomainObject.php create mode 100644 backend/app/DomainObjects/ProductTaxAndFeesDomainObject.php create mode 100644 backend/app/DomainObjects/ProductsCheckInListDomainObject.php rename backend/app/DomainObjects/Status/{TicketStatus.php => ProductStatus.php} (88%) delete mode 100644 backend/app/DomainObjects/TicketAttributeDomainObject.php delete mode 100644 backend/app/DomainObjects/TicketCapacityAssignmentDomainObject.php delete mode 100644 backend/app/DomainObjects/TicketCheckInListDomainObject.php delete mode 100644 backend/app/DomainObjects/TicketQuestionDomainObject.php delete mode 100644 backend/app/DomainObjects/TicketTaxAndFeesDomainObject.php delete mode 100644 backend/app/DomainObjects/TicketTaxDomainObject.php rename backend/app/Http/Actions/{Tickets/CreateTicketAction.php => Products/CreateProductAction.php} (54%) rename backend/app/Http/Actions/{Tickets/DeleteTicketAction.php => Products/DeleteProductAction.php} (60%) rename backend/app/Http/Actions/{Tickets/EditTicketAction.php => Products/EditProductAction.php} (53%) create mode 100644 backend/app/Http/Actions/Products/GetProductAction.php rename backend/app/Http/Actions/{Tickets/GetTicketsAction.php => Products/GetProductsAction.php} (53%) rename backend/app/Http/Actions/{Tickets/SortTicketsAction.php => Products/SortProductsAction.php} (60%) delete mode 100644 backend/app/Http/Actions/Tickets/GetTicketAction.php rename backend/app/Http/Request/{Ticket/SortTicketsRequest.php => Product/SortProductsRequest.php} (71%) rename backend/app/Http/Request/{Ticket/UpsertTicketRequest.php => Product/UpsertProductRequest.php} (85%) rename backend/app/Models/{Ticket.php => Product.php} (52%) rename backend/app/Models/{TicketPrice.php => ProductPrice.php} (70%) rename backend/app/Models/{TicketQuestion.php => ProductQuestion.php} (55%) create mode 100644 backend/app/Repository/Eloquent/ProductPriceRepository.php create mode 100644 backend/app/Repository/Eloquent/ProductRepository.php delete mode 100644 backend/app/Repository/Eloquent/TicketPriceRepository.php delete mode 100644 backend/app/Repository/Eloquent/TicketRepository.php create mode 100644 backend/app/Repository/Interfaces/ProductPriceRepositoryInterface.php create mode 100644 backend/app/Repository/Interfaces/ProductRepositoryInterface.php delete mode 100644 backend/app/Repository/Interfaces/TicketPriceRepositoryInterface.php delete mode 100644 backend/app/Repository/Interfaces/TicketRepositoryInterface.php rename backend/app/Resources/{Ticket/TicketMinimalResourcePublic.php => Product/ProductMinimalResourcePublic.php} (56%) rename backend/app/Resources/{Ticket/TicketPriceResource.php => Product/ProductPriceResource.php} (85%) rename backend/app/Resources/{Ticket/TicketPriceResourcePublic.php => Product/ProductPriceResourcePublic.php} (87%) rename backend/app/Resources/{Ticket/TicketResource.php => Product/ProductResource.php} (75%) rename backend/app/Resources/{Ticket/TicketResourcePublic.php => Product/ProductResourcePublic.php} (71%) create mode 100644 backend/app/Services/Domain/Product/AvailableProductQuantitiesFetchService.php create mode 100644 backend/app/Services/Domain/Product/CreateProductService.php rename backend/app/Services/Domain/{Ticket/DTO/AvailableTicketQuantitiesDTO.php => Product/DTO/AvailableProductQuantitiesDTO.php} (74%) rename backend/app/Services/Domain/{Ticket/DTO/AvailableTicketQuantitiesResponseDTO.php => Product/DTO/AvailableProductQuantitiesResponseDTO.php} (58%) create mode 100644 backend/app/Services/Domain/Product/DTO/CreateProductDTO.php rename backend/app/Services/Domain/{Ticket/DTO/OrderTicketPriceDTO.php => Product/DTO/OrderProductPriceDTO.php} (71%) rename backend/app/Services/Domain/{Ticket => Product}/DTO/PriceDTO.php (82%) create mode 100644 backend/app/Services/Domain/Product/DTO/ProductPriceDTO.php create mode 100644 backend/app/Services/Domain/Product/EventProductValidationService.php create mode 100644 backend/app/Services/Domain/Product/Exception/UnrecognizedProductIdException.php create mode 100644 backend/app/Services/Domain/Product/ProductFilterService.php rename backend/app/Services/Domain/{Ticket/TicketPriceCreateService.php => Product/ProductPriceCreateService.php} (66%) create mode 100644 backend/app/Services/Domain/Product/ProductPriceService.php rename backend/app/Services/Domain/{Ticket/TicketPriceUpdateService.php => Product/ProductPriceUpdateService.php} (62%) rename backend/app/Services/Domain/{Ticket/TicketQuantityUpdateService.php => Product/ProductQuantityUpdateService.php} (80%) rename backend/app/Services/Domain/Tax/DTO/{TaxAndTicketAssociateParams.php => TaxAndProductAssociateParams.php} (71%) rename backend/app/Services/Domain/Tax/{TaxAndTicketAssociationService.php => TaxAndProductAssociationService.php} (66%) delete mode 100644 backend/app/Services/Domain/Ticket/AvailableTicketQuantitiesFetchService.php delete mode 100644 backend/app/Services/Domain/Ticket/CreateTicketService.php delete mode 100644 backend/app/Services/Domain/Ticket/DTO/CreateTicketDTO.php delete mode 100644 backend/app/Services/Domain/Ticket/DTO/TicketPriceDTO.php delete mode 100644 backend/app/Services/Domain/Ticket/EventTicketValidationService.php delete mode 100644 backend/app/Services/Domain/Ticket/Exception/UnrecognizedTicketIdException.php delete mode 100644 backend/app/Services/Domain/Ticket/TicketFilterService.php delete mode 100644 backend/app/Services/Domain/Ticket/TicketPriceService.php create mode 100644 backend/app/Services/Handlers/Product/CreateProductHandler.php create mode 100644 backend/app/Services/Handlers/Product/DTO/UpsertProductDTO.php create mode 100644 backend/app/Services/Handlers/Product/DeleteProductHandler.php create mode 100644 backend/app/Services/Handlers/Product/EditProductHandler.php create mode 100644 backend/app/Services/Handlers/Product/GetProductsHandler.php create mode 100644 backend/app/Services/Handlers/Product/SortProductsHandler.php delete mode 100644 backend/app/Services/Handlers/Ticket/CreateTicketHandler.php delete mode 100644 backend/app/Services/Handlers/Ticket/DTO/UpsertTicketDTO.php delete mode 100644 backend/app/Services/Handlers/Ticket/DeleteTicketHandler.php delete mode 100644 backend/app/Services/Handlers/Ticket/EditTicketHandler.php delete mode 100644 backend/app/Services/Handlers/Ticket/GetTicketsHandler.php delete mode 100644 backend/app/Services/Handlers/Ticket/SortTicketsHandler.php create mode 100644 backend/database/migrations/2024_09_20_032323_rename_tickets_to_products.php create mode 100644 backend/database/migrations/2024_09_20_032838_add_product_type_to_products.php create mode 100755 frontend/scripts/rename.sh delete mode 100644 frontend/src/api/ticket.client.ts rename frontend/src/components/common/{AttendeeTicket/AttendeeTicket.module.scss => AttendeeProduct/AttendeeProduct.module.scss} (96%) rename frontend/src/components/common/{AttendeeTicket => AttendeeProduct}/index.tsx (74%) rename frontend/src/components/common/{TicketPriceAvailability => ProductPriceAvailability}/index.tsx (51%) rename frontend/src/components/common/{TicketsTable/TicketsTable.module.scss => ProductsTable/ProductsTable.module.scss} (93%) rename frontend/src/components/common/{TicketsTable/SortableTicket => ProductsTable/SortableProduct}/index.tsx (68%) rename frontend/src/components/common/{TicketsTable => ProductsTable}/index.tsx (66%) rename frontend/src/components/forms/{TicketForm/TicketForm.module.scss => ProductForm/ProductForm.module.scss} (100%) rename frontend/src/components/forms/{TicketForm => ProductForm}/index.tsx (80%) rename frontend/src/components/layouts/EventHomepage/{TicketSelection/TicketSelection.module.scss => ProductSelection/ProductSelection.module.scss} (100%) create mode 100644 frontend/src/components/layouts/EventHomepage/ProductSelection/index.tsx delete mode 100644 frontend/src/components/layouts/EventHomepage/TicketSelection/index.tsx rename frontend/src/components/layouts/{TicketWidget/TicketWidget.module.scss => ProductWidget/ProductWidget.module.scss} (100%) rename frontend/src/components/layouts/{TicketWidget => ProductWidget}/index.tsx (92%) rename frontend/src/components/modals/{CreateTicketModal => CreateProductModal}/index.tsx (74%) rename frontend/src/components/modals/{EditTicketModal => EditProductModal}/index.tsx (54%) delete mode 100644 frontend/src/components/routes/event/tickets.tsx rename frontend/src/components/routes/{ticket-widget/AttendeeTicketAndInformation => product-widget/AttendeeProductAndInformation}/index.tsx (80%) rename frontend/src/components/routes/{ticket-widget => product-widget}/CollectInformation/CollectInformation.module.scss (100%) rename frontend/src/components/routes/{ticket-widget => product-widget}/CollectInformation/index.tsx (88%) rename frontend/src/components/routes/{ticket-widget/OrderSummaryAndTickets/OrderSummaryAndTickets.module.scss => product-widget/OrderSummaryAndProducts/OrderSummaryAndProducts.module.scss} (100%) rename frontend/src/components/routes/{ticket-widget/OrderSummaryAndTickets => product-widget/OrderSummaryAndProducts}/index.tsx (92%) rename frontend/src/components/routes/{ticket-widget => product-widget}/Payment/Payment.module.scss (100%) rename frontend/src/components/routes/{ticket-widget => product-widget}/Payment/index.tsx (100%) rename frontend/src/components/routes/{ticket-widget => product-widget}/PaymentReturn/PaymentReturn.module.scss (100%) rename frontend/src/components/routes/{ticket-widget => product-widget}/PaymentReturn/index.tsx (100%) rename frontend/src/components/routes/{ticket-widget => product-widget}/PrintOrder/index.tsx (86%) rename frontend/src/components/routes/{ticket-widget/PrintTicket => product-widget/PrintProduct}/index.tsx (84%) rename frontend/src/components/routes/{ticket-widget/SelectTickets => product-widget/SelectProducts}/Prices/Tiered/index.tsx (64%) rename frontend/src/components/routes/{ticket-widget/SelectTickets => product-widget/SelectProducts}/index.tsx (74%) delete mode 100644 frontend/src/mutations/useDeleteTicket.ts delete mode 100644 frontend/src/mutations/useResendAttendeeTicket.ts delete mode 100644 frontend/src/mutations/useSortTickets.ts delete mode 100644 frontend/src/mutations/useUpdateTicket.ts delete mode 100644 frontend/src/queries/useGetTicket.ts delete mode 100644 frontend/src/queries/useGetTickets.ts delete mode 100644 frontend/src/queries/useGetTicketsPublic.ts create mode 100644 frontend/src/styles/product/default.scss delete mode 100644 frontend/src/styles/ticket/default.scss delete mode 100644 frontend/src/utilites/tickets.ts diff --git a/backend/app/DomainObjects/AttendeeDomainObject.php b/backend/app/DomainObjects/AttendeeDomainObject.php index c8101dd736..ee9ceeb994 100644 --- a/backend/app/DomainObjects/AttendeeDomainObject.php +++ b/backend/app/DomainObjects/AttendeeDomainObject.php @@ -11,7 +11,7 @@ class AttendeeDomainObject extends Generated\AttendeeDomainObjectAbstract implem { private ?OrderDomainObject $order = null; - private ?TicketDomainObject $ticket = null; + private ?ProductDomainObject $product = null; /** @var Collection|null */ public ?Collection $questionAndAnswerViews = null; @@ -60,7 +60,7 @@ public static function getAllowedFilterFields(): array { return [ self::STATUS, - self::TICKET_ID, + self::PRODUCT_ID, ]; } @@ -79,14 +79,14 @@ public function getFullName(): string return $this->first_name . ' ' . $this->last_name; } - public function getTicket(): ?TicketDomainObject + public function getProduct(): ?ProductDomainObject { - return $this->ticket; + return $this->product; } - public function setTicket(?TicketDomainObject $ticket): self + public function setProduct(?ProductDomainObject $product): self { - $this->ticket = $ticket; + $this->product = $product; return $this; } diff --git a/backend/app/DomainObjects/CapacityAssignmentDomainObject.php b/backend/app/DomainObjects/CapacityAssignmentDomainObject.php index 758847d251..642fadae10 100644 --- a/backend/app/DomainObjects/CapacityAssignmentDomainObject.php +++ b/backend/app/DomainObjects/CapacityAssignmentDomainObject.php @@ -9,7 +9,7 @@ class CapacityAssignmentDomainObject extends Generated\CapacityAssignmentDomainObjectAbstract implements IsSortable { - public ?Collection $tickets = null; + public ?Collection $products = null; public static function getDefaultSort(): string { @@ -58,14 +58,14 @@ public function getPercentageUsed(): float return round(($this->getUsedCapacity() / $this->getCapacity()) * 100, 2); } - public function getTickets(): ?Collection + public function getProducts(): ?Collection { - return $this->tickets; + return $this->products; } - public function setTickets(?Collection $tickets): static + public function setProducts(?Collection $products): static { - $this->tickets = $tickets; + $this->products = $products; return $this; } diff --git a/backend/app/DomainObjects/CheckInListDomainObject.php b/backend/app/DomainObjects/CheckInListDomainObject.php index 30364989e6..ae55f3bbcf 100644 --- a/backend/app/DomainObjects/CheckInListDomainObject.php +++ b/backend/app/DomainObjects/CheckInListDomainObject.php @@ -9,7 +9,7 @@ class CheckInListDomainObject extends Generated\CheckInListDomainObjectAbstract implements IsSortable { - private ?Collection $tickets = null; + private ?Collection $products = null; private ?EventDomainObject $event = null; @@ -53,14 +53,14 @@ public static function getAllowedSorts(): AllowedSorts ); } - public function getTickets(): ?Collection + public function getProducts(): ?Collection { - return $this->tickets; + return $this->products; } - public function setTickets(?Collection $tickets): static + public function setProducts(?Collection $products): static { - $this->tickets = $tickets; + $this->products = $products; return $this; } diff --git a/backend/app/DomainObjects/Enums/CapacityAssignmentAppliesTo.php b/backend/app/DomainObjects/Enums/CapacityAssignmentAppliesTo.php index 75d27759b4..250b77a355 100644 --- a/backend/app/DomainObjects/Enums/CapacityAssignmentAppliesTo.php +++ b/backend/app/DomainObjects/Enums/CapacityAssignmentAppliesTo.php @@ -6,6 +6,6 @@ enum CapacityAssignmentAppliesTo { use BaseEnum; - case TICKETS; + case PRODUCTS; case EVENT; } diff --git a/backend/app/DomainObjects/Enums/MessageTypeEnum.php b/backend/app/DomainObjects/Enums/MessageTypeEnum.php index 4246d750b0..fd2b44b171 100644 --- a/backend/app/DomainObjects/Enums/MessageTypeEnum.php +++ b/backend/app/DomainObjects/Enums/MessageTypeEnum.php @@ -7,7 +7,7 @@ enum MessageTypeEnum use BaseEnum; case ORDER; - case TICKET; + case PRODUCT; case ATTENDEE; case EVENT; } diff --git a/backend/app/DomainObjects/Enums/TicketType.php b/backend/app/DomainObjects/Enums/ProductPriceType.php similarity index 87% rename from backend/app/DomainObjects/Enums/TicketType.php rename to backend/app/DomainObjects/Enums/ProductPriceType.php index 942a45156d..a0d01c442c 100644 --- a/backend/app/DomainObjects/Enums/TicketType.php +++ b/backend/app/DomainObjects/Enums/ProductPriceType.php @@ -2,7 +2,7 @@ namespace HiEvents\DomainObjects\Enums; -enum TicketType +enum ProductPriceType { use BaseEnum; diff --git a/backend/app/DomainObjects/Enums/ProductType.php b/backend/app/DomainObjects/Enums/ProductType.php new file mode 100644 index 0000000000..04d096c787 --- /dev/null +++ b/backend/app/DomainObjects/Enums/ProductType.php @@ -0,0 +1,11 @@ +tickets = $tickets; + $this->products = $products; return $this; } - public function getTickets(): ?Collection + public function getProducts(): ?Collection { - return $this->tickets; + return $this->products; } public function setQuestions(?Collection $questions): EventDomainObject diff --git a/backend/app/DomainObjects/Generated/AttendeeCheckInDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/AttendeeCheckInDomainObjectAbstract.php index ffb56a8d57..1249d2714c 100644 --- a/backend/app/DomainObjects/Generated/AttendeeCheckInDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/AttendeeCheckInDomainObjectAbstract.php @@ -12,7 +12,7 @@ abstract class AttendeeCheckInDomainObjectAbstract extends \HiEvents\DomainObjec final public const PLURAL_NAME = 'attendee_check_ins'; final public const ID = 'id'; final public const CHECK_IN_LIST_ID = 'check_in_list_id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const ATTENDEE_ID = 'attendee_id'; final public const EVENT_ID = 'event_id'; final public const SHORT_ID = 'short_id'; @@ -23,7 +23,7 @@ abstract class AttendeeCheckInDomainObjectAbstract extends \HiEvents\DomainObjec protected int $id; protected int $check_in_list_id; - protected int $ticket_id; + protected int $product_id; protected int $attendee_id; protected int $event_id; protected string $short_id; @@ -37,7 +37,7 @@ public function toArray(): array return [ 'id' => $this->id ?? null, 'check_in_list_id' => $this->check_in_list_id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'attendee_id' => $this->attendee_id ?? null, 'event_id' => $this->event_id ?? null, 'short_id' => $this->short_id ?? null, @@ -70,15 +70,15 @@ public function getCheckInListId(): int return $this->check_in_list_id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setAttendeeId(int $attendee_id): self diff --git a/backend/app/DomainObjects/Generated/AttendeeDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/AttendeeDomainObjectAbstract.php index 59b052de07..6097de8955 100644 --- a/backend/app/DomainObjects/Generated/AttendeeDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/AttendeeDomainObjectAbstract.php @@ -12,11 +12,11 @@ abstract class AttendeeDomainObjectAbstract extends \HiEvents\DomainObjects\Abst final public const PLURAL_NAME = 'attendees'; final public const ID = 'id'; final public const ORDER_ID = 'order_id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const EVENT_ID = 'event_id'; final public const CHECKED_IN_BY = 'checked_in_by'; final public const CHECKED_OUT_BY = 'checked_out_by'; - final public const TICKET_PRICE_ID = 'ticket_price_id'; + final public const PRODUCT_PRICE_ID = 'product_price_id'; final public const SHORT_ID = 'short_id'; final public const FIRST_NAME = 'first_name'; final public const LAST_NAME = 'last_name'; @@ -31,11 +31,11 @@ abstract class AttendeeDomainObjectAbstract extends \HiEvents\DomainObjects\Abst protected int $id; protected int $order_id; - protected int $ticket_id; + protected int $product_id; protected int $event_id; protected ?int $checked_in_by = null; protected ?int $checked_out_by = null; - protected int $ticket_price_id; + protected int $product_price_id; protected string $short_id; protected string $first_name = ''; protected string $last_name = ''; @@ -53,11 +53,11 @@ public function toArray(): array return [ 'id' => $this->id ?? null, 'order_id' => $this->order_id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'event_id' => $this->event_id ?? null, 'checked_in_by' => $this->checked_in_by ?? null, 'checked_out_by' => $this->checked_out_by ?? null, - 'ticket_price_id' => $this->ticket_price_id ?? null, + 'product_price_id' => $this->product_price_id ?? null, 'short_id' => $this->short_id ?? null, 'first_name' => $this->first_name ?? null, 'last_name' => $this->last_name ?? null, @@ -94,15 +94,15 @@ public function getOrderId(): int return $this->order_id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setEventId(int $event_id): self @@ -138,15 +138,15 @@ public function getCheckedOutBy(): ?int return $this->checked_out_by; } - public function setTicketPriceId(int $ticket_price_id): self + public function setProductPriceId(int $product_price_id): self { - $this->ticket_price_id = $ticket_price_id; + $this->product_price_id = $product_price_id; return $this; } - public function getTicketPriceId(): int + public function getProductPriceId(): int { - return $this->ticket_price_id; + return $this->product_price_id; } public function setShortId(string $short_id): self diff --git a/backend/app/DomainObjects/Generated/EventDailyStatisticDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/EventDailyStatisticDomainObjectAbstract.php index beae7bfa03..667c860d23 100644 --- a/backend/app/DomainObjects/Generated/EventDailyStatisticDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/EventDailyStatisticDomainObjectAbstract.php @@ -15,7 +15,7 @@ abstract class EventDailyStatisticDomainObjectAbstract extends \HiEvents\DomainO final public const SALES_TOTAL_GROSS = 'sales_total_gross'; final public const TOTAL_TAX = 'total_tax'; final public const SALES_TOTAL_BEFORE_ADDITIONS = 'sales_total_before_additions'; - final public const TICKETS_SOLD = 'tickets_sold'; + final public const PRODUCTS_SOLD = 'products_sold'; final public const ORDERS_CREATED = 'orders_created'; final public const DATE = 'date'; final public const CREATED_AT = 'created_at'; @@ -31,7 +31,7 @@ abstract class EventDailyStatisticDomainObjectAbstract extends \HiEvents\DomainO protected float $sales_total_gross = 0.0; protected float $total_tax = 0.0; protected float $sales_total_before_additions = 0.0; - protected int $tickets_sold = 0; + protected int $products_sold = 0; protected int $orders_created = 0; protected string $date; protected string $created_at; @@ -50,7 +50,7 @@ public function toArray(): array 'sales_total_gross' => $this->sales_total_gross ?? null, 'total_tax' => $this->total_tax ?? null, 'sales_total_before_additions' => $this->sales_total_before_additions ?? null, - 'tickets_sold' => $this->tickets_sold ?? null, + 'products_sold' => $this->products_sold ?? null, 'orders_created' => $this->orders_created ?? null, 'date' => $this->date ?? null, 'created_at' => $this->created_at ?? null, @@ -118,15 +118,15 @@ public function getSalesTotalBeforeAdditions(): float return $this->sales_total_before_additions; } - public function setTicketsSold(int $tickets_sold): self + public function setProductsSold(int $products_sold): self { - $this->tickets_sold = $tickets_sold; + $this->products_sold = $products_sold; return $this; } - public function getTicketsSold(): int + public function getProductsSold(): int { - return $this->tickets_sold; + return $this->products_sold; } public function setOrdersCreated(int $orders_created): self diff --git a/backend/app/DomainObjects/Generated/EventDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/EventDomainObjectAbstract.php index bd097faa83..787c721e25 100644 --- a/backend/app/DomainObjects/Generated/EventDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/EventDomainObjectAbstract.php @@ -28,7 +28,7 @@ abstract class EventDomainObjectAbstract extends \HiEvents\DomainObjects\Abstrac final public const DELETED_AT = 'deleted_at'; final public const LOCATION = 'location'; final public const SHORT_ID = 'short_id'; - final public const TICKET_QUANTITY_AVAILABLE = 'ticket_quantity_available'; + final public const PRODUCT_QUANTITY_AVAILABLE = 'ticket_quantity_available'; protected int $id; protected int $account_id; diff --git a/backend/app/DomainObjects/Generated/EventSettingDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/EventSettingDomainObjectAbstract.php index a883a7e23b..03feec38a1 100644 --- a/backend/app/DomainObjects/Generated/EventSettingDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/EventSettingDomainObjectAbstract.php @@ -14,7 +14,7 @@ abstract class EventSettingDomainObjectAbstract extends \HiEvents\DomainObjects\ final public const EVENT_ID = 'event_id'; final public const PRE_CHECKOUT_MESSAGE = 'pre_checkout_message'; final public const POST_CHECKOUT_MESSAGE = 'post_checkout_message'; - final public const TICKET_PAGE_MESSAGE = 'ticket_page_message'; + final public const PRODUCT_PAGE_MESSAGE = 'product_page_message'; final public const CONTINUE_BUTTON_TEXT = 'continue_button_text'; final public const EMAIL_FOOTER_MESSAGE = 'email_footer_message'; final public const SUPPORT_EMAIL = 'support_email'; @@ -50,7 +50,7 @@ abstract class EventSettingDomainObjectAbstract extends \HiEvents\DomainObjects\ protected int $event_id; protected ?string $pre_checkout_message = null; protected ?string $post_checkout_message = null; - protected ?string $ticket_page_message = null; + protected ?string $product_page_message = null; protected ?string $continue_button_text = null; protected ?string $email_footer_message = null; protected ?string $support_email = null; @@ -89,7 +89,7 @@ public function toArray(): array 'event_id' => $this->event_id ?? null, 'pre_checkout_message' => $this->pre_checkout_message ?? null, 'post_checkout_message' => $this->post_checkout_message ?? null, - 'ticket_page_message' => $this->ticket_page_message ?? null, + 'product_page_message' => $this->product_page_message ?? null, 'continue_button_text' => $this->continue_button_text ?? null, 'email_footer_message' => $this->email_footer_message ?? null, 'support_email' => $this->support_email ?? null, @@ -167,15 +167,15 @@ public function getPostCheckoutMessage(): ?string return $this->post_checkout_message; } - public function setTicketPageMessage(?string $ticket_page_message): self + public function setProductPageMessage(?string $product_page_message): self { - $this->ticket_page_message = $ticket_page_message; + $this->product_page_message = $product_page_message; return $this; } - public function getTicketPageMessage(): ?string + public function getProductPageMessage(): ?string { - return $this->ticket_page_message; + return $this->product_page_message; } public function setContinueButtonText(?string $continue_button_text): self diff --git a/backend/app/DomainObjects/Generated/EventStatisticDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/EventStatisticDomainObjectAbstract.php index 1519b8fbe9..f345220584 100644 --- a/backend/app/DomainObjects/Generated/EventStatisticDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/EventStatisticDomainObjectAbstract.php @@ -21,7 +21,7 @@ abstract class EventStatisticDomainObjectAbstract extends \HiEvents\DomainObject final public const DELETED_AT = 'deleted_at'; final public const UPDATED_AT = 'updated_at'; final public const TOTAL_FEE = 'total_fee'; - final public const TICKETS_SOLD = 'tickets_sold'; + final public const PRODUCTS_SOLD = 'products_sold'; final public const VERSION = 'version'; final public const ORDERS_CREATED = 'orders_created'; final public const TOTAL_REFUNDED = 'total_refunded'; @@ -37,7 +37,7 @@ abstract class EventStatisticDomainObjectAbstract extends \HiEvents\DomainObject protected ?string $deleted_at = null; protected ?string $updated_at = null; protected float $total_fee = 0.0; - protected int $tickets_sold = 0; + protected int $products_sold = 0; protected int $version = 0; protected int $orders_created = 0; protected float $total_refunded = 0.0; @@ -56,7 +56,7 @@ public function toArray(): array 'deleted_at' => $this->deleted_at ?? null, 'updated_at' => $this->updated_at ?? null, 'total_fee' => $this->total_fee ?? null, - 'tickets_sold' => $this->tickets_sold ?? null, + 'products_sold' => $this->products_sold ?? null, 'version' => $this->version ?? null, 'orders_created' => $this->orders_created ?? null, 'total_refunded' => $this->total_refunded ?? null, @@ -184,15 +184,15 @@ public function getTotalFee(): float return $this->total_fee; } - public function setTicketsSold(int $tickets_sold): self + public function setProductsSold(int $products_sold): self { - $this->tickets_sold = $tickets_sold; + $this->products_sold = $products_sold; return $this; } - public function getTicketsSold(): int + public function getProductsSold(): int { - return $this->tickets_sold; + return $this->products_sold; } public function setVersion(int $version): self diff --git a/backend/app/DomainObjects/Generated/MessageDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/MessageDomainObjectAbstract.php index c496d4ac0a..acb847efb0 100644 --- a/backend/app/DomainObjects/Generated/MessageDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/MessageDomainObjectAbstract.php @@ -19,7 +19,7 @@ abstract class MessageDomainObjectAbstract extends \HiEvents\DomainObjects\Abstr final public const RECIPIENT_IDS = 'recipient_ids'; final public const SENT_AT = 'sent_at'; final public const ATTENDEE_IDS = 'attendee_ids'; - final public const TICKET_IDS = 'ticket_ids'; + final public const PRODUCT_IDS = 'product_ids'; final public const ORDER_ID = 'order_id'; final public const STATUS = 'status'; final public const SEND_DATA = 'send_data'; @@ -36,7 +36,7 @@ abstract class MessageDomainObjectAbstract extends \HiEvents\DomainObjects\Abstr protected array|string|null $recipient_ids = null; protected ?string $sent_at = null; protected array|string|null $attendee_ids = null; - protected array|string|null $ticket_ids = null; + protected array|string|null $product_ids = null; protected ?int $order_id = null; protected string $status; protected array|string|null $send_data = null; @@ -56,7 +56,7 @@ public function toArray(): array 'recipient_ids' => $this->recipient_ids ?? null, 'sent_at' => $this->sent_at ?? null, 'attendee_ids' => $this->attendee_ids ?? null, - 'ticket_ids' => $this->ticket_ids ?? null, + 'product_ids' => $this->product_ids ?? null, 'order_id' => $this->order_id ?? null, 'status' => $this->status ?? null, 'send_data' => $this->send_data ?? null, @@ -165,15 +165,15 @@ public function getAttendeeIds(): array|string|null return $this->attendee_ids; } - public function setTicketIds(array|string|null $ticket_ids): self + public function setProductIds(array|string|null $product_ids): self { - $this->ticket_ids = $ticket_ids; + $this->product_ids = $product_ids; return $this; } - public function getTicketIds(): array|string|null + public function getProductIds(): array|string|null { - return $this->ticket_ids; + return $this->product_ids; } public function setOrderId(?int $order_id): self diff --git a/backend/app/DomainObjects/Generated/OrderItemDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/OrderItemDomainObjectAbstract.php index 71936c1922..b47797b1d0 100644 --- a/backend/app/DomainObjects/Generated/OrderItemDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/OrderItemDomainObjectAbstract.php @@ -12,8 +12,8 @@ abstract class OrderItemDomainObjectAbstract extends \HiEvents\DomainObjects\Abs final public const PLURAL_NAME = 'order_items'; final public const ID = 'id'; final public const ORDER_ID = 'order_id'; - final public const TICKET_ID = 'ticket_id'; - final public const TICKET_PRICE_ID = 'ticket_price_id'; + final public const PRODUCT_ID = 'product_id'; + final public const PRODUCT_PRICE_ID = 'product_price_id'; final public const TOTAL_BEFORE_ADDITIONS = 'total_before_additions'; final public const QUANTITY = 'quantity'; final public const ITEM_NAME = 'item_name'; @@ -27,8 +27,8 @@ abstract class OrderItemDomainObjectAbstract extends \HiEvents\DomainObjects\Abs protected int $id; protected int $order_id; - protected int $ticket_id; - protected int $ticket_price_id; + protected int $product_id; + protected int $product_price_id; protected float $total_before_additions; protected int $quantity; protected ?string $item_name = null; @@ -45,8 +45,8 @@ public function toArray(): array return [ 'id' => $this->id ?? null, 'order_id' => $this->order_id ?? null, - 'ticket_id' => $this->ticket_id ?? null, - 'ticket_price_id' => $this->ticket_price_id ?? null, + 'product_id' => $this->product_id ?? null, + 'product_price_id' => $this->product_price_id ?? null, 'total_before_additions' => $this->total_before_additions ?? null, 'quantity' => $this->quantity ?? null, 'item_name' => $this->item_name ?? null, @@ -82,26 +82,26 @@ public function getOrderId(): int return $this->order_id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } - public function setTicketPriceId(int $ticket_price_id): self + public function setProductPriceId(int $product_price_id): self { - $this->ticket_price_id = $ticket_price_id; + $this->product_price_id = $product_price_id; return $this; } - public function getTicketPriceId(): int + public function getProductPriceId(): int { - return $this->ticket_price_id; + return $this->product_price_id; } public function setTotalBeforeAdditions(float $total_before_additions): self diff --git a/backend/app/DomainObjects/Generated/OrganizerDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/OrganizerDomainObjectAbstract.php index 11dbffee90..1eea9f6408 100644 --- a/backend/app/DomainObjects/Generated/OrganizerDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/OrganizerDomainObjectAbstract.php @@ -22,6 +22,8 @@ abstract class OrganizerDomainObjectAbstract extends \HiEvents\DomainObjects\Abs final public const DELETED_AT = 'deleted_at'; final public const CURRENCY = 'currency'; final public const TIMEZONE = 'timezone'; + final public const SOCIAL_HANDLES = 'social_handles'; + final public const IS_PUBLIC_PROFILE_ENABLED = 'is_public_profile_enabled'; protected int $id; protected int $account_id; @@ -35,6 +37,8 @@ abstract class OrganizerDomainObjectAbstract extends \HiEvents\DomainObjects\Abs protected ?string $deleted_at = null; protected string $currency = 'USD'; protected string $timezone; + protected array|string|null $social_handles = null; + protected ?string $is_public_profile_enabled = null; public function toArray(): array { @@ -51,6 +55,8 @@ public function toArray(): array 'deleted_at' => $this->deleted_at ?? null, 'currency' => $this->currency ?? null, 'timezone' => $this->timezone ?? null, + 'social_handles' => $this->social_handles ?? null, + 'is_public_profile_enabled' => $this->is_public_profile_enabled ?? null, ]; } @@ -185,4 +191,26 @@ public function getTimezone(): string { return $this->timezone; } + + public function setSocialHandles(array|string|null $social_handles): self + { + $this->social_handles = $social_handles; + return $this; + } + + public function getSocialHandles(): array|string|null + { + return $this->social_handles; + } + + public function setIsPublicProfileEnabled(?string $is_public_profile_enabled): self + { + $this->is_public_profile_enabled = $is_public_profile_enabled; + return $this; + } + + public function getIsPublicProfileEnabled(): ?string + { + return $this->is_public_profile_enabled; + } } diff --git a/backend/app/DomainObjects/Generated/TicketCapacityAssignmentDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/ProductCapacityAssignmentDomainObjectAbstract.php similarity index 79% rename from backend/app/DomainObjects/Generated/TicketCapacityAssignmentDomainObjectAbstract.php rename to backend/app/DomainObjects/Generated/ProductCapacityAssignmentDomainObjectAbstract.php index 40ed859361..048264f454 100644 --- a/backend/app/DomainObjects/Generated/TicketCapacityAssignmentDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/ProductCapacityAssignmentDomainObjectAbstract.php @@ -6,19 +6,19 @@ * THIS FILE IS AUTOGENERATED - DO NOT EDIT IT DIRECTLY. * @package HiEvents\DomainObjects\Generated */ -abstract class TicketCapacityAssignmentDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject +abstract class ProductCapacityAssignmentDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject { - final public const SINGULAR_NAME = 'ticket_capacity_assignment'; - final public const PLURAL_NAME = 'ticket_capacity_assignments'; + final public const SINGULAR_NAME = 'product_capacity_assignment'; + final public const PLURAL_NAME = 'product_capacity_assignments'; final public const ID = 'id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const CAPACITY_ASSIGNMENT_ID = 'capacity_assignment_id'; final public const CREATED_AT = 'created_at'; final public const UPDATED_AT = 'updated_at'; final public const DELETED_AT = 'deleted_at'; protected int $id; - protected int $ticket_id; + protected int $product_id; protected int $capacity_assignment_id; protected ?string $created_at = null; protected ?string $updated_at = null; @@ -28,7 +28,7 @@ public function toArray(): array { return [ 'id' => $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'capacity_assignment_id' => $this->capacity_assignment_id ?? null, 'created_at' => $this->created_at ?? null, 'updated_at' => $this->updated_at ?? null, @@ -47,15 +47,15 @@ public function getId(): int return $this->id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setCapacityAssignmentId(int $capacity_assignment_id): self diff --git a/backend/app/DomainObjects/Generated/TicketsCheckInListDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/ProductCheckInListDomainObjectAbstract.php similarity index 73% rename from backend/app/DomainObjects/Generated/TicketsCheckInListDomainObjectAbstract.php rename to backend/app/DomainObjects/Generated/ProductCheckInListDomainObjectAbstract.php index da5209eae2..427251d755 100644 --- a/backend/app/DomainObjects/Generated/TicketsCheckInListDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/ProductCheckInListDomainObjectAbstract.php @@ -6,17 +6,17 @@ * THIS FILE IS AUTOGENERATED - DO NOT EDIT IT DIRECTLY. * @package HiEvents\DomainObjects\Generated */ -abstract class TicketsCheckInListDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject +abstract class ProductCheckInListDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject { - final public const SINGULAR_NAME = 'tickets_check_in_list'; - final public const PLURAL_NAME = 'tickets_check_in_lists'; + final public const SINGULAR_NAME = 'product_check_in_list'; + final public const PLURAL_NAME = 'product_check_in_lists'; final public const ID = 'id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const CHECK_IN_LIST_ID = 'check_in_list_id'; final public const DELETED_AT = 'deleted_at'; protected int $id; - protected int $ticket_id; + protected int $product_id; protected int $check_in_list_id; protected ?string $deleted_at = null; @@ -24,7 +24,7 @@ public function toArray(): array { return [ 'id' => $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'check_in_list_id' => $this->check_in_list_id ?? null, 'deleted_at' => $this->deleted_at ?? null, ]; @@ -41,15 +41,15 @@ public function getId(): int return $this->id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setCheckInListId(int $check_in_list_id): self diff --git a/backend/app/DomainObjects/Generated/TicketDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/ProductDomainObjectAbstract.php similarity index 93% rename from backend/app/DomainObjects/Generated/TicketDomainObjectAbstract.php rename to backend/app/DomainObjects/Generated/ProductDomainObjectAbstract.php index 88e1f07b69..e1bda8ed7b 100644 --- a/backend/app/DomainObjects/Generated/TicketDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/ProductDomainObjectAbstract.php @@ -6,10 +6,10 @@ * THIS FILE IS AUTOGENERATED - DO NOT EDIT IT DIRECTLY. * @package HiEvents\DomainObjects\Generated */ -abstract class TicketDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject +abstract class ProductDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject { - final public const SINGULAR_NAME = 'ticket'; - final public const PLURAL_NAME = 'tickets'; + final public const SINGULAR_NAME = 'product'; + final public const PLURAL_NAME = 'products'; final public const ID = 'id'; final public const EVENT_ID = 'event_id'; final public const TITLE = 'title'; @@ -31,6 +31,7 @@ abstract class TicketDomainObjectAbstract extends \HiEvents\DomainObjects\Abstra final public const DELETED_AT = 'deleted_at'; final public const TYPE = 'type'; final public const IS_HIDDEN = 'is_hidden'; + final public const PRODUCT_TYPE = 'product_type'; protected int $id; protected int $event_id; @@ -53,6 +54,7 @@ abstract class TicketDomainObjectAbstract extends \HiEvents\DomainObjects\Abstra protected ?string $deleted_at = null; protected string $type = 'PAID'; protected ?bool $is_hidden = false; + protected string $product_type = 'PRODUCT'; public function toArray(): array { @@ -78,6 +80,7 @@ public function toArray(): array 'deleted_at' => $this->deleted_at ?? null, 'type' => $this->type ?? null, 'is_hidden' => $this->is_hidden ?? null, + 'product_type' => $this->product_type ?? null, ]; } @@ -311,4 +314,15 @@ public function getIsHidden(): ?bool { return $this->is_hidden; } + + public function setProductType(string $product_type): self + { + $this->product_type = $product_type; + return $this; + } + + public function getProductType(): string + { + return $this->product_type; + } } diff --git a/backend/app/DomainObjects/Generated/TicketPriceDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/ProductPriceDomainObjectAbstract.php similarity index 90% rename from backend/app/DomainObjects/Generated/TicketPriceDomainObjectAbstract.php rename to backend/app/DomainObjects/Generated/ProductPriceDomainObjectAbstract.php index c6e47272c4..ee1f14577a 100644 --- a/backend/app/DomainObjects/Generated/TicketPriceDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/ProductPriceDomainObjectAbstract.php @@ -6,12 +6,12 @@ * THIS FILE IS AUTOGENERATED - DO NOT EDIT IT DIRECTLY. * @package HiEvents\DomainObjects\Generated */ -abstract class TicketPriceDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject +abstract class ProductPriceDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject { - final public const SINGULAR_NAME = 'ticket_price'; - final public const PLURAL_NAME = 'ticket_prices'; + final public const SINGULAR_NAME = 'product_price'; + final public const PLURAL_NAME = 'product_prices'; final public const ID = 'id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const PRICE = 'price'; final public const LABEL = 'label'; final public const SALE_START_DATE = 'sale_start_date'; @@ -26,7 +26,7 @@ abstract class TicketPriceDomainObjectAbstract extends \HiEvents\DomainObjects\A final public const QUANTITY_AVAILABLE = 'quantity_available'; protected int $id; - protected int $ticket_id; + protected int $product_id; protected float $price; protected ?string $label = null; protected ?string $sale_start_date = null; @@ -44,7 +44,7 @@ public function toArray(): array { return [ 'id' => $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'price' => $this->price ?? null, 'label' => $this->label ?? null, 'sale_start_date' => $this->sale_start_date ?? null, @@ -71,15 +71,15 @@ public function getId(): int return $this->id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setPrice(float $price): self diff --git a/backend/app/DomainObjects/Generated/TicketQuestionDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/ProductQuestionDomainObjectAbstract.php similarity index 71% rename from backend/app/DomainObjects/Generated/TicketQuestionDomainObjectAbstract.php rename to backend/app/DomainObjects/Generated/ProductQuestionDomainObjectAbstract.php index 7aaba00d1c..7b3cbebfb4 100644 --- a/backend/app/DomainObjects/Generated/TicketQuestionDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/ProductQuestionDomainObjectAbstract.php @@ -6,17 +6,17 @@ * THIS FILE IS AUTOGENERATED - DO NOT EDIT IT DIRECTLY. * @package HiEvents\DomainObjects\Generated */ -abstract class TicketQuestionDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject +abstract class ProductQuestionDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject { - final public const SINGULAR_NAME = 'ticket_question'; - final public const PLURAL_NAME = 'ticket_questions'; + final public const SINGULAR_NAME = 'product_question'; + final public const PLURAL_NAME = 'product_questions'; final public const ID = 'id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const QUESTION_ID = 'question_id'; final public const DELETED_AT = 'deleted_at'; protected int $id; - protected int $ticket_id; + protected int $product_id; protected int $question_id; protected ?string $deleted_at = null; @@ -24,7 +24,7 @@ public function toArray(): array { return [ 'id' => $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'question_id' => $this->question_id ?? null, 'deleted_at' => $this->deleted_at ?? null, ]; @@ -41,15 +41,15 @@ public function getId(): int return $this->id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setQuestionId(int $question_id): self diff --git a/backend/app/DomainObjects/Generated/TicketTaxAndFeesDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/ProductTaxAndFeesDomainObjectAbstract.php similarity index 64% rename from backend/app/DomainObjects/Generated/TicketTaxAndFeesDomainObjectAbstract.php rename to backend/app/DomainObjects/Generated/ProductTaxAndFeesDomainObjectAbstract.php index f4f59d977d..6c1b7840a8 100644 --- a/backend/app/DomainObjects/Generated/TicketTaxAndFeesDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/ProductTaxAndFeesDomainObjectAbstract.php @@ -6,23 +6,23 @@ * THIS FILE IS AUTOGENERATED - DO NOT EDIT IT DIRECTLY. * @package HiEvents\DomainObjects\Generated */ -abstract class TicketTaxAndFeesDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject +abstract class ProductTaxAndFeesDomainObjectAbstract extends \HiEvents\DomainObjects\AbstractDomainObject { - final public const SINGULAR_NAME = 'ticket_tax_and_fees'; - final public const PLURAL_NAME = 'ticket_tax_and_fees'; + final public const SINGULAR_NAME = 'product_tax_and_fees'; + final public const PLURAL_NAME = 'product_tax_and_fees'; final public const ID = 'id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const TAX_AND_FEE_ID = 'tax_and_fee_id'; protected int $id; - protected int $ticket_id; + protected int $product_id; protected int $tax_and_fee_id; public function toArray(): array { return [ 'id' => $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'tax_and_fee_id' => $this->tax_and_fee_id ?? null, ]; } @@ -38,15 +38,15 @@ public function getId(): int return $this->id; } - public function setTicketId(int $ticket_id): self + public function setProductId(int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): int + public function getProductId(): int { - return $this->ticket_id; + return $this->product_id; } public function setTaxAndFeeId(int $tax_and_fee_id): self diff --git a/backend/app/DomainObjects/Generated/PromoCodeDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/PromoCodeDomainObjectAbstract.php index f4bda2b518..c1e7174e18 100644 --- a/backend/app/DomainObjects/Generated/PromoCodeDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/PromoCodeDomainObjectAbstract.php @@ -14,7 +14,7 @@ abstract class PromoCodeDomainObjectAbstract extends \HiEvents\DomainObjects\Abs final public const EVENT_ID = 'event_id'; final public const CODE = 'code'; final public const DISCOUNT = 'discount'; - final public const APPLICABLE_TICKET_IDS = 'applicable_ticket_ids'; + final public const APPLICABLE_PRODUCT_IDS = 'applicable_product_ids'; final public const EXPIRY_DATE = 'expiry_date'; final public const DISCOUNT_TYPE = 'discount_type'; final public const ATTENDEE_USAGE_COUNT = 'attendee_usage_count'; @@ -28,7 +28,7 @@ abstract class PromoCodeDomainObjectAbstract extends \HiEvents\DomainObjects\Abs protected int $event_id; protected string $code; protected float $discount = 0.0; - protected array|string|null $applicable_ticket_ids = null; + protected array|string|null $applicable_product_ids = null; protected ?string $expiry_date = null; protected ?string $discount_type = null; protected int $attendee_usage_count = 0; @@ -45,7 +45,7 @@ public function toArray(): array 'event_id' => $this->event_id ?? null, 'code' => $this->code ?? null, 'discount' => $this->discount ?? null, - 'applicable_ticket_ids' => $this->applicable_ticket_ids ?? null, + 'applicable_product_ids' => $this->applicable_product_ids ?? null, 'expiry_date' => $this->expiry_date ?? null, 'discount_type' => $this->discount_type ?? null, 'attendee_usage_count' => $this->attendee_usage_count ?? null, @@ -101,15 +101,15 @@ public function getDiscount(): float return $this->discount; } - public function setApplicableTicketIds(array|string|null $applicable_ticket_ids): self + public function setApplicableProductIds(array|string|null $applicable_product_ids): self { - $this->applicable_ticket_ids = $applicable_ticket_ids; + $this->applicable_product_ids = $applicable_product_ids; return $this; } - public function getApplicableTicketIds(): array|string|null + public function getApplicableProductIds(): array|string|null { - return $this->applicable_ticket_ids; + return $this->applicable_product_ids; } public function setExpiryDate(?string $expiry_date): self diff --git a/backend/app/DomainObjects/Generated/QuestionAnswerDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/QuestionAnswerDomainObjectAbstract.php index f71fc5ae13..0505f94c3e 100644 --- a/backend/app/DomainObjects/Generated/QuestionAnswerDomainObjectAbstract.php +++ b/backend/app/DomainObjects/Generated/QuestionAnswerDomainObjectAbstract.php @@ -14,7 +14,7 @@ abstract class QuestionAnswerDomainObjectAbstract extends \HiEvents\DomainObject final public const QUESTION_ID = 'question_id'; final public const ORDER_ID = 'order_id'; final public const ATTENDEE_ID = 'attendee_id'; - final public const TICKET_ID = 'ticket_id'; + final public const PRODUCT_ID = 'product_id'; final public const CREATED_AT = 'created_at'; final public const UPDATED_AT = 'updated_at'; final public const DELETED_AT = 'deleted_at'; @@ -24,7 +24,7 @@ abstract class QuestionAnswerDomainObjectAbstract extends \HiEvents\DomainObject protected int $question_id; protected int $order_id; protected ?int $attendee_id = null; - protected ?int $ticket_id = null; + protected ?int $product_id = null; protected string $created_at; protected string $updated_at; protected ?string $deleted_at = null; @@ -37,7 +37,7 @@ public function toArray(): array 'question_id' => $this->question_id ?? null, 'order_id' => $this->order_id ?? null, 'attendee_id' => $this->attendee_id ?? null, - 'ticket_id' => $this->ticket_id ?? null, + 'product_id' => $this->product_id ?? null, 'created_at' => $this->created_at ?? null, 'updated_at' => $this->updated_at ?? null, 'deleted_at' => $this->deleted_at ?? null, @@ -89,15 +89,15 @@ public function getAttendeeId(): ?int return $this->attendee_id; } - public function setTicketId(?int $ticket_id): self + public function setProductId(?int $product_id): self { - $this->ticket_id = $ticket_id; + $this->product_id = $product_id; return $this; } - public function getTicketId(): ?int + public function getProductId(): ?int { - return $this->ticket_id; + return $this->product_id; } public function setCreatedAt(string $created_at): self diff --git a/backend/app/DomainObjects/Generated/TicketCheckInListDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/TicketCheckInListDomainObjectAbstract.php deleted file mode 100644 index 6b4722ada8..0000000000 --- a/backend/app/DomainObjects/Generated/TicketCheckInListDomainObjectAbstract.php +++ /dev/null @@ -1,76 +0,0 @@ - $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, - 'check_in_list_id' => $this->check_in_list_id ?? null, - 'deleted_at' => $this->deleted_at ?? null, - ]; - } - - public function setId(int $id): self - { - $this->id = $id; - return $this; - } - - public function getId(): int - { - return $this->id; - } - - public function setTicketId(int $ticket_id): self - { - $this->ticket_id = $ticket_id; - return $this; - } - - public function getTicketId(): int - { - return $this->ticket_id; - } - - public function setCheckInListId(int $check_in_list_id): self - { - $this->check_in_list_id = $check_in_list_id; - return $this; - } - - public function getCheckInListId(): int - { - return $this->check_in_list_id; - } - - public function setDeletedAt(?string $deleted_at): self - { - $this->deleted_at = $deleted_at; - return $this; - } - - public function getDeletedAt(): ?string - { - return $this->deleted_at; - } -} diff --git a/backend/app/DomainObjects/Generated/TicketTaxDomainObjectAbstract.php b/backend/app/DomainObjects/Generated/TicketTaxDomainObjectAbstract.php deleted file mode 100644 index 66295f559e..0000000000 --- a/backend/app/DomainObjects/Generated/TicketTaxDomainObjectAbstract.php +++ /dev/null @@ -1,62 +0,0 @@ - $this->id ?? null, - 'ticket_id' => $this->ticket_id ?? null, - 'tax_id' => $this->tax_id ?? null, - ]; - } - - public function setId(int $id): self - { - $this->id = $id; - return $this; - } - - public function getId(): int - { - return $this->id; - } - - public function setTicketId(int $ticket_id): self - { - $this->ticket_id = $ticket_id; - return $this; - } - - public function getTicketId(): int - { - return $this->ticket_id; - } - - public function setTaxId(int $tax_id): self - { - $this->tax_id = $tax_id; - return $this; - } - - public function getTaxId(): int - { - return $this->tax_id; - } -} diff --git a/backend/app/DomainObjects/OrderItemDomainObject.php b/backend/app/DomainObjects/OrderItemDomainObject.php index 1a93d71e59..a1df31f778 100644 --- a/backend/app/DomainObjects/OrderItemDomainObject.php +++ b/backend/app/DomainObjects/OrderItemDomainObject.php @@ -6,35 +6,35 @@ class OrderItemDomainObject extends Generated\OrderItemDomainObjectAbstract { - private ?TicketPriceDomainObject $ticketPrice = null; + private ?ProductPriceDomainObject $productPrice = null; - public ?TicketDomainObject $ticket = null; + public ?ProductDomainObject $product = null; public function getTotalBeforeDiscount(): float { return Currency::round($this->getPriceBeforeDiscount() * $this->getQuantity()); } - public function getTicketPrice(): ?TicketPriceDomainObject + public function getProductPrice(): ?ProductPriceDomainObject { - return $this->ticketPrice; + return $this->productPrice; } - public function setTicketPrice(?TicketPriceDomainObject $tier): self + public function setProductPrice(?ProductPriceDomainObject $tier): self { - $this->ticketPrice = $tier; + $this->productPrice = $tier; return $this; } - public function getTicket(): ?TicketDomainObject + public function getProduct(): ?ProductDomainObject { - return $this->ticket; + return $this->product; } - public function setTicket(?TicketDomainObject $ticket): self + public function setProduct(?ProductDomainObject $product): self { - $this->ticket = $ticket; + $this->product = $product; return $this; } diff --git a/backend/app/DomainObjects/ProductCapacityAssignmentDomainObject.php b/backend/app/DomainObjects/ProductCapacityAssignmentDomainObject.php new file mode 100644 index 0000000000..6560231b64 --- /dev/null +++ b/backend/app/DomainObjects/ProductCapacityAssignmentDomainObject.php @@ -0,0 +1,7 @@ +taxAndFees = $taxes; return $this; @@ -79,29 +79,29 @@ public function getFees(): ?Collection public function isSoldOut(): bool { - if (!$this->getTicketPrices() || $this->getTicketPrices()->isEmpty()) { + if (!$this->getProductPrices() || $this->getProductPrices()->isEmpty()) { return true; } - return $this->getTicketPrices()->every(fn(TicketPriceDomainObject $price) => $price->isSoldOut()); + return $this->getProductPrices()->every(fn(ProductPriceDomainObject $price) => $price->isSoldOut()); } public function getQuantityAvailable(): int { - $availableCount = $this->getTicketPrices()->sum(fn(TicketPriceDomainObject $price) => $price->getQuantityAvailable()); + $availableCount = $this->getProductPrices()->sum(fn(ProductPriceDomainObject $price) => $price->getQuantityAvailable()); if ($this->quantityAvailable !== null) { return min($availableCount, $this->quantityAvailable); } - if (!$this->getTicketPrices() || $this->getTicketPrices()->isEmpty()) { + if (!$this->getProductPrices() || $this->getProductPrices()->isEmpty()) { return 0; } return $availableCount; } - public function setQuantityAvailable(int $quantityAvailable): TicketDomainObject + public function setQuantityAvailable(int $quantityAvailable): ProductDomainObject { $this->quantityAvailable = $quantityAvailable; @@ -125,7 +125,7 @@ public function isAfterSaleEndDate(): bool public function isAvailable(): bool { // If all prices are hidden, it's not available - if ($this->getType() === TicketType::TIERED->name && $this->getTicketPrices()?->isEmpty()) { + if ($this->getType() === ProductPriceType::TIERED->name && $this->getProductPrices()?->isEmpty()) { return false; } @@ -136,14 +136,14 @@ public function isAvailable(): bool } /** - * @return Collection|null + * @return Collection|null */ - public function getTicketPrices(): ?Collection + public function getProductPrices(): ?Collection { return $this->prices; } - public function setTicketPrices(?Collection $prices): self + public function setProductPrices(?Collection $prices): self { $this->prices = $prices; @@ -151,59 +151,59 @@ public function setTicketPrices(?Collection $prices): self } /** - * All ticket types except TIERED have a single price, so we can just return the first price. + * All product types except TIERED have a single price, so we can just return the first price. * * @return float|null */ public function getPrice(): ?float { - if ($this->getType() === TicketType::TIERED->name) { - throw new LogicException('You cannot get a single price for a tiered ticket. Use getPrices() instead.'); + if ($this->getType() === ProductPriceType::TIERED->name) { + throw new LogicException('You cannot get a single price for a tiered product. Use getPrices() instead.'); } - return $this->getTicketPrices()?->first()->getPrice(); + return $this->getProductPrices()?->first()->getPrice(); } - public function getPriceById(int $priceId): ?TicketPriceDomainObject + public function getPriceById(int $priceId): ?ProductPriceDomainObject { - return $this->getTicketPrices()?->first(fn(TicketPriceDomainObject $price) => $price->getId() === $priceId); + return $this->getProductPrices()?->first(fn(ProductPriceDomainObject $price) => $price->getId() === $priceId); } public function isTieredType(): bool { - return $this->getType() === TicketType::TIERED->name; + return $this->getType() === ProductPriceType::TIERED->name; } public function isDonationType(): bool { - return $this->getType() === TicketType::DONATION->name; + return $this->getType() === ProductPriceType::DONATION->name; } public function isPaidType(): bool { - return $this->getType() === TicketType::PAID->name; + return $this->getType() === ProductPriceType::PAID->name; } public function isFreeType(): bool { - return $this->getType() === TicketType::FREE->name; + return $this->getType() === ProductPriceType::FREE->name; } public function getInitialQuantityAvailable(): ?int { - if ($this->getType() === TicketType::TIERED->name) { - return $this->getTicketPrices()?->sum(fn(TicketPriceDomainObject $price) => $price->getInitialQuantityAvailable()); + if ($this->getType() === ProductPriceType::TIERED->name) { + return $this->getProductPrices()?->sum(fn(ProductPriceDomainObject $price) => $price->getInitialQuantityAvailable()); } - return $this->getTicketPrices()?->first()?->getInitialQuantityAvailable(); + return $this->getProductPrices()?->first()?->getInitialQuantityAvailable(); } public function getQuantitySold(): int { - return $this->getTicketPrices()?->sum(fn(TicketPriceDomainObject $price) => $price->getQuantitySold()) ?? 0; + return $this->getProductPrices()?->sum(fn(ProductPriceDomainObject $price) => $price->getQuantitySold()) ?? 0; } - public function setOffSaleReason(?string $offSaleReason): TicketDomainObject + public function setOffSaleReason(?string $offSaleReason): ProductDomainObject { $this->offSaleReason = $offSaleReason; diff --git a/backend/app/DomainObjects/TicketPriceDomainObject.php b/backend/app/DomainObjects/ProductPriceDomainObject.php similarity index 89% rename from backend/app/DomainObjects/TicketPriceDomainObject.php rename to backend/app/DomainObjects/ProductPriceDomainObject.php index d2d312e96f..0919781ec1 100644 --- a/backend/app/DomainObjects/TicketPriceDomainObject.php +++ b/backend/app/DomainObjects/ProductPriceDomainObject.php @@ -6,7 +6,7 @@ use HiEvents\Helper\Currency; use LogicException; -class TicketPriceDomainObject extends Generated\TicketPriceDomainObjectAbstract +class ProductPriceDomainObject extends Generated\ProductPriceDomainObjectAbstract { private ?float $priceBeforeDiscount = null; @@ -23,7 +23,7 @@ public function getPriceBeforeDiscount(): ?float return $this->priceBeforeDiscount; } - public function setPriceBeforeDiscount(?float $originalPrice): TicketPriceDomainObject + public function setPriceBeforeDiscount(?float $originalPrice): ProductPriceDomainObject { $this->priceBeforeDiscount = $originalPrice; @@ -96,13 +96,13 @@ public function isAvailable(): ?bool return $this->isAvailable; } - public function setIsAvailable(?bool $isAvailable): TicketPriceDomainObject + public function setIsAvailable(?bool $isAvailable): ProductPriceDomainObject { $this->isAvailable = $isAvailable; return $this; } - public function setOffSaleReason(?string $offSaleReason): TicketPriceDomainObject + public function setOffSaleReason(?string $offSaleReason): ProductPriceDomainObject { $this->offSaleReason = $offSaleReason; diff --git a/backend/app/DomainObjects/ProductQuestionDomainObject.php b/backend/app/DomainObjects/ProductQuestionDomainObject.php new file mode 100644 index 0000000000..866ff446c3 --- /dev/null +++ b/backend/app/DomainObjects/ProductQuestionDomainObject.php @@ -0,0 +1,7 @@ +getApplicableTicketIds()) { + // If there's no product IDs we apply the promo to all products + if (!$this->getApplicableProductIds()) { return true; } - return in_array($ticket->getId(), array_map('intval', $this->getApplicableTicketIds()), true); + return in_array($product->getId(), array_map('intval', $this->getApplicableProductIds()), true); } public function isFixedDiscount(): bool diff --git a/backend/app/DomainObjects/QuestionDomainObject.php b/backend/app/DomainObjects/QuestionDomainObject.php index 6ddd5a1331..5f1c011f63 100644 --- a/backend/app/DomainObjects/QuestionDomainObject.php +++ b/backend/app/DomainObjects/QuestionDomainObject.php @@ -7,17 +7,17 @@ class QuestionDomainObject extends Generated\QuestionDomainObjectAbstract { - public ?Collection $tickets = null; + public ?Collection $products = null; - public function setTickets(?Collection $tickets): QuestionDomainObject + public function setProducts(?Collection $products): QuestionDomainObject { - $this->tickets = $tickets; + $this->products = $products; return $this; } - public function getTickets(): ?Collection + public function getProducts(): ?Collection { - return $this->tickets; + return $this->products; } public function isMultipleChoice(): bool diff --git a/backend/app/DomainObjects/Status/TicketStatus.php b/backend/app/DomainObjects/Status/ProductStatus.php similarity index 88% rename from backend/app/DomainObjects/Status/TicketStatus.php rename to backend/app/DomainObjects/Status/ProductStatus.php index 2f6e2bfa8c..fe55a34752 100644 --- a/backend/app/DomainObjects/Status/TicketStatus.php +++ b/backend/app/DomainObjects/Status/ProductStatus.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\Enums\BaseEnum; -enum TicketStatus +enum ProductStatus { use BaseEnum; diff --git a/backend/app/DomainObjects/TicketAttributeDomainObject.php b/backend/app/DomainObjects/TicketAttributeDomainObject.php deleted file mode 100644 index 00aeeee035..0000000000 --- a/backend/app/DomainObjects/TicketAttributeDomainObject.php +++ /dev/null @@ -1,7 +0,0 @@ -getCheckedInAt() ? Carbon::parse($attendee->getCheckedInAt())->format('Y-m-d H:i:s') : '', - $attendee->getTicketId(), + $attendee->getProductId(), $attendee->getEventId(), $attendee->getPublicId(), $attendee->getShortId(), diff --git a/backend/app/Helper/Url.php b/backend/app/Helper/Url.php index 4eb96a6e5a..e52777fddf 100644 --- a/backend/app/Helper/Url.php +++ b/backend/app/Helper/Url.php @@ -11,7 +11,7 @@ class Url public const ACCEPT_INVITATION = 'app.frontend_urls.accept_invitation'; public const CONFIRM_EMAIL_ADDRESS = 'app.frontend_urls.confirm_email_address'; public const EVENT_HOMEPAGE = 'app.frontend_urls.event_homepage'; - public const ATTENDEE_TICKET = 'app.frontend_urls.attendee_ticket'; + public const ATTENDEE_PRODUCT = 'app.frontend_urls.attendee_product'; public const ORDER_SUMMARY = 'app.frontend_urls.order_summary'; public const ORGANIZER_ORDER_SUMMARY = 'app.frontend_urls.organizer_order_summary'; diff --git a/backend/app/Http/Actions/Attendees/CreateAttendeeAction.php b/backend/app/Http/Actions/Attendees/CreateAttendeeAction.php index e43a97761b..34808b3661 100644 --- a/backend/app/Http/Actions/Attendees/CreateAttendeeAction.php +++ b/backend/app/Http/Actions/Attendees/CreateAttendeeAction.php @@ -3,8 +3,8 @@ namespace HiEvents\Http\Actions\Attendees; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\Exceptions\InvalidTicketPriceId; -use HiEvents\Exceptions\NoTicketsAvailableException; +use HiEvents\Exceptions\InvalidProductPriceId; +use HiEvents\Exceptions\NoProductsAvailableException; use HiEvents\Http\Actions\BaseAction; use HiEvents\Http\Request\Attendee\CreateAttendeeRequest; use HiEvents\Http\ResponseCodes; @@ -37,13 +37,13 @@ public function __invoke(CreateAttendeeRequest $request, int $eventId): JsonResp 'event_id' => $eventId, ]) )); - } catch (NoTicketsAvailableException $exception) { + } catch (NoProductsAvailableException $exception) { throw ValidationException::withMessages([ - 'ticket_id' => $exception->getMessage(), + 'product_id' => $exception->getMessage(), ]); - } catch (InvalidTicketPriceId $exception) { + } catch (InvalidProductPriceId $exception) { throw ValidationException::withMessages([ - 'ticket_price_id' => $exception->getMessage(), + 'product_price_id' => $exception->getMessage(), ]); } diff --git a/backend/app/Http/Actions/Attendees/EditAttendeeAction.php b/backend/app/Http/Actions/Attendees/EditAttendeeAction.php index c9f11f6d40..85eaf7d659 100644 --- a/backend/app/Http/Actions/Attendees/EditAttendeeAction.php +++ b/backend/app/Http/Actions/Attendees/EditAttendeeAction.php @@ -3,7 +3,7 @@ namespace HiEvents\Http\Actions\Attendees; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\Exceptions\NoTicketsAvailableException; +use HiEvents\Exceptions\NoProductsAvailableException; use HiEvents\Http\Actions\BaseAction; use HiEvents\Http\Request\Attendee\EditAttendeeRequest; use HiEvents\Resources\Attendee\AttendeeResource; @@ -33,14 +33,14 @@ public function __invoke(EditAttendeeRequest $request, int $eventId, int $attend 'first_name' => $request->input('first_name'), 'last_name' => $request->input('last_name'), 'email' => $request->input('email'), - 'ticket_id' => $request->input('ticket_id'), - 'ticket_price_id' => $request->input('ticket_price_id'), + 'product_id' => $request->input('product_id'), + 'product_price_id' => $request->input('product_price_id'), 'event_id' => $eventId, 'attendee_id' => $attendeeId, ])); - } catch (NoTicketsAvailableException $exception) { + } catch (NoProductsAvailableException $exception) { throw ValidationException::withMessages([ - 'ticket_id' => $exception->getMessage(), + 'product_id' => $exception->getMessage(), ]); } diff --git a/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php b/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php index 3d36e38c42..990c263aaa 100644 --- a/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php +++ b/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php @@ -35,7 +35,7 @@ public function __invoke(int $eventId): BinaryFileResponse $questions = $this->questionRepository->findWhere([ 'event_id' => $eventId, - 'belongs_to' => QuestionBelongsTo::TICKET->name, + 'belongs_to' => QuestionBelongsTo::PRODUCT->name, ]); return Excel::download( diff --git a/backend/app/Http/Actions/Attendees/GetAttendeeAction.php b/backend/app/Http/Actions/Attendees/GetAttendeeAction.php index 6d82bbbd21..8bb96d2740 100644 --- a/backend/app/Http/Actions/Attendees/GetAttendeeAction.php +++ b/backend/app/Http/Actions/Attendees/GetAttendeeAction.php @@ -4,8 +4,8 @@ use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\QuestionAndAnswerViewDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Http\Actions\BaseAction; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; @@ -29,12 +29,12 @@ public function __invoke(int $eventId, int $attendeeId): Response|JsonResponse $attendee = $this->attendeeRepository ->loadRelation(QuestionAndAnswerViewDomainObject::class) ->loadRelation(new Relationship( - domainObject: TicketDomainObject::class, + domainObject: ProductDomainObject::class, nested: [ new Relationship( - domainObject: TicketPriceDomainObject::class, + domainObject: ProductPriceDomainObject::class, ), - ], name: 'ticket')) + ], name: 'product')) ->findFirstWhere([ 'id' => $attendeeId, 'event_id' => $eventId, diff --git a/backend/app/Http/Actions/Attendees/GetAttendeeActionPublic.php b/backend/app/Http/Actions/Attendees/GetAttendeeActionPublic.php index 87d0b2f88c..5be7ddf396 100644 --- a/backend/app/Http/Actions/Attendees/GetAttendeeActionPublic.php +++ b/backend/app/Http/Actions/Attendees/GetAttendeeActionPublic.php @@ -3,8 +3,8 @@ namespace HiEvents\Http\Actions\Attendees; use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Http\Actions\BaseAction; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; @@ -28,12 +28,12 @@ public function __invoke(int $eventId, string $attendeeShortId): JsonResponse|Re { $attendee = $this->attendeeRepository ->loadRelation(new Relationship( - domainObject: TicketDomainObject::class, + domainObject: ProductDomainObject::class, nested: [ new Relationship( - domainObject: TicketPriceDomainObject::class, + domainObject: ProductPriceDomainObject::class, ), - ], name: 'ticket')) + ], name: 'product')) ->findFirstWhere([ AttendeeDomainObjectAbstract::SHORT_ID => $attendeeShortId ]); diff --git a/backend/app/Http/Actions/CapacityAssignments/CreateCapacityAssignmentAction.php b/backend/app/Http/Actions/CapacityAssignments/CreateCapacityAssignmentAction.php index 0eae8c0ded..8450d86aba 100644 --- a/backend/app/Http/Actions/CapacityAssignments/CreateCapacityAssignmentAction.php +++ b/backend/app/Http/Actions/CapacityAssignments/CreateCapacityAssignmentAction.php @@ -6,7 +6,7 @@ use HiEvents\Http\Actions\BaseAction; use HiEvents\Http\Request\CapacityAssigment\UpsertCapacityAssignmentRequest; use HiEvents\Resources\CapacityAssignment\CapacityAssignmentResource; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CapacityAssignment\CreateCapacityAssignmentHandler; use HiEvents\Services\Handlers\CapacityAssignment\DTO\UpsertCapacityAssignmentDTO; use Illuminate\Http\JsonResponse; @@ -31,10 +31,10 @@ public function __invoke(int $eventId, UpsertCapacityAssignmentRequest $request) 'event_id' => $eventId, 'capacity' => $request->validated('capacity'), 'status' => $request->validated('status'), - 'ticket_ids' => $request->validated('ticket_ids'), + 'product_ids' => $request->validated('product_ids'), ]), ); - } catch (UnrecognizedTicketIdException $exception) { + } catch (UnrecognizedProductIdException $exception) { return $this->errorResponse( message: $exception->getMessage(), statusCode: Response::HTTP_UNPROCESSABLE_ENTITY, diff --git a/backend/app/Http/Actions/CapacityAssignments/UpdateCapacityAssignmentAction.php b/backend/app/Http/Actions/CapacityAssignments/UpdateCapacityAssignmentAction.php index 0deb55fb3b..3af2d794a1 100644 --- a/backend/app/Http/Actions/CapacityAssignments/UpdateCapacityAssignmentAction.php +++ b/backend/app/Http/Actions/CapacityAssignments/UpdateCapacityAssignmentAction.php @@ -6,7 +6,7 @@ use HiEvents\Http\Actions\BaseAction; use HiEvents\Http\Request\CapacityAssigment\UpsertCapacityAssignmentRequest; use HiEvents\Resources\CapacityAssignment\CapacityAssignmentResource; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CapacityAssignment\DTO\UpsertCapacityAssignmentDTO; use HiEvents\Services\Handlers\CapacityAssignment\UpdateCapacityAssignmentHandler; use Illuminate\Http\JsonResponse; @@ -33,10 +33,10 @@ public function __invoke(int $eventId, int $capacityAssignmentId, UpsertCapacity 'capacity' => $request->validated('capacity'), 'applies_to' => $request->validated('applies_to'), 'status' => $request->validated('status'), - 'ticket_ids' => $request->validated('ticket_ids'), + 'product_ids' => $request->validated('product_ids'), ]), ); - } catch (UnrecognizedTicketIdException $exception) { + } catch (UnrecognizedProductIdException $exception) { return $this->errorResponse( message: $exception->getMessage(), statusCode: Response::HTTP_UNPROCESSABLE_ENTITY, diff --git a/backend/app/Http/Actions/CheckInLists/CreateCheckInListAction.php b/backend/app/Http/Actions/CheckInLists/CreateCheckInListAction.php index c10ada722e..80f9c55bb4 100644 --- a/backend/app/Http/Actions/CheckInLists/CreateCheckInListAction.php +++ b/backend/app/Http/Actions/CheckInLists/CreateCheckInListAction.php @@ -5,7 +5,7 @@ use HiEvents\Http\Actions\BaseAction; use HiEvents\Http\Request\CheckInList\UpsertCheckInListRequest; use HiEvents\Resources\CheckInList\CheckInListResource; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CheckInList\CreateCheckInListHandler; use HiEvents\Services\Handlers\CheckInList\DTO\UpsertCheckInListDTO; use Illuminate\Http\JsonResponse; @@ -27,12 +27,12 @@ public function __invoke(UpsertCheckInListRequest $request, int $eventId): JsonR name: $request->validated('name'), description: $request->validated('description'), eventId: $eventId, - ticketIds: $request->validated('ticket_ids'), + productIds: $request->validated('product_ids'), expiresAt: $request->validated('expires_at'), activatesAt: $request->validated('activates_at'), ) ); - } catch (UnrecognizedTicketIdException $exception) { + } catch (UnrecognizedProductIdException $exception) { return $this->errorResponse( message: $exception->getMessage(), statusCode: Response::HTTP_UNPROCESSABLE_ENTITY, diff --git a/backend/app/Http/Actions/CheckInLists/UpdateCheckInListAction.php b/backend/app/Http/Actions/CheckInLists/UpdateCheckInListAction.php index 5599617dae..f401d784c4 100644 --- a/backend/app/Http/Actions/CheckInLists/UpdateCheckInListAction.php +++ b/backend/app/Http/Actions/CheckInLists/UpdateCheckInListAction.php @@ -6,7 +6,7 @@ use HiEvents\Http\Actions\BaseAction; use HiEvents\Http\Request\CheckInList\UpsertCheckInListRequest; use HiEvents\Resources\CheckInList\CheckInListResource; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CheckInList\DTO\UpsertCheckInListDTO; use HiEvents\Services\Handlers\CheckInList\UpdateCheckInlistHandler; use Illuminate\Http\JsonResponse; @@ -30,13 +30,13 @@ public function __invoke(UpsertCheckInListRequest $request, int $eventId, int $c name: $request->validated('name'), description: $request->validated('description'), eventId: $eventId, - ticketIds: $request->validated('ticket_ids'), + productIds: $request->validated('product_ids'), expiresAt: $request->validated('expires_at'), activatesAt: $request->validated('activates_at'), id: $checkInListId, ) ); - } catch (UnrecognizedTicketIdException $exception) { + } catch (UnrecognizedProductIdException $exception) { return $this->errorResponse( message: $exception->getMessage(), statusCode: Response::HTTP_UNPROCESSABLE_ENTITY, diff --git a/backend/app/Http/Actions/Events/DuplicateEventAction.php b/backend/app/Http/Actions/Events/DuplicateEventAction.php index 2f6063da0c..2d69e876d8 100644 --- a/backend/app/Http/Actions/Events/DuplicateEventAction.php +++ b/backend/app/Http/Actions/Events/DuplicateEventAction.php @@ -29,7 +29,7 @@ public function __invoke(int $eventId, DuplicateEventRequest $request): JsonResp accountId: $this->getAuthenticatedAccountId(), title: $request->validated('title'), startDate: $request->validated('start_date'), - duplicateTickets: $request->validated('duplicate_tickets'), + duplicateProducts: $request->validated('duplicate_products'), duplicateQuestions: $request->validated('duplicate_questions'), duplicateSettings: $request->validated('duplicate_settings'), duplicatePromoCodes: $request->validated('duplicate_promo_codes'), diff --git a/backend/app/Http/Actions/Events/GetEventAction.php b/backend/app/Http/Actions/Events/GetEventAction.php index d2ac6f9c50..5eea274d0b 100644 --- a/backend/app/Http/Actions/Events/GetEventAction.php +++ b/backend/app/Http/Actions/Events/GetEventAction.php @@ -7,8 +7,8 @@ use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\OrganizerDomainObject; use HiEvents\DomainObjects\TaxAndFeesDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Http\Actions\BaseAction; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\EventRepositoryInterface; @@ -31,8 +31,8 @@ public function __invoke(int $eventId): JsonResponse $event = $this->eventRepository ->loadRelation(new Relationship(domainObject: OrganizerDomainObject::class, name: 'organizer')) ->loadRelation( - new Relationship(TicketDomainObject::class, [ - new Relationship(TicketPriceDomainObject::class), + new Relationship(ProductDomainObject::class, [ + new Relationship(ProductPriceDomainObject::class), new Relationship(TaxAndFeesDomainObject::class), ]), ) diff --git a/backend/app/Http/Actions/Messages/SendMessageAction.php b/backend/app/Http/Actions/Messages/SendMessageAction.php index 5bdb1910ec..bf0220da03 100644 --- a/backend/app/Http/Actions/Messages/SendMessageAction.php +++ b/backend/app/Http/Actions/Messages/SendMessageAction.php @@ -36,7 +36,7 @@ public function __invoke(SendMessageRequest $request, int $eventId): JsonRespons 'is_test' => $request->input('is_test'), 'order_id' => $request->input('order_id'), 'attendee_ids' => $request->input('attendee_ids'), - 'ticket_ids' => $request->input('ticket_ids'), + 'product_ids' => $request->input('product_ids'), 'send_copy_to_current_user' => $request->boolean('send_copy_to_current_user'), 'sent_by_user_id' => $user->getId(), 'account_id' => $this->getAuthenticatedAccountId(), diff --git a/backend/app/Http/Actions/Orders/CreateOrderActionPublic.php b/backend/app/Http/Actions/Orders/CreateOrderActionPublic.php index c789ae6cf1..9c7db9d16b 100644 --- a/backend/app/Http/Actions/Orders/CreateOrderActionPublic.php +++ b/backend/app/Http/Actions/Orders/CreateOrderActionPublic.php @@ -12,7 +12,7 @@ use HiEvents\Services\Domain\Order\OrderCreateRequestValidationService; use HiEvents\Services\Handlers\Order\CreateOrderHandler; use HiEvents\Services\Handlers\Order\DTO\CreateOrderPublicDTO; -use HiEvents\Services\Handlers\Order\DTO\TicketOrderDetailsDTO; +use HiEvents\Services\Handlers\Order\DTO\ProductOrderDetailsDTO; use HiEvents\Services\Infrastructure\Session\CheckoutSessionManagementService; use Illuminate\Http\JsonResponse; use Throwable; @@ -42,7 +42,7 @@ public function __invoke(CreateOrderRequest $request, int $eventId): JsonRespons CreateOrderPublicDTO::fromArray([ 'is_user_authenticated' => $this->isUserAuthenticated(), 'promo_code' => $request->input('promo_code'), - 'tickets' => TicketOrderDetailsDTO::collectionFromArray($request->input('tickets')), + 'products' => ProductOrderDetailsDTO::collectionFromArray($request->input('products')), 'session_identifier' => $sessionId, 'order_locale' => $this->localeService->getLocaleOrDefault($request->getPreferredLanguage()), ]) diff --git a/backend/app/Http/Actions/Tickets/CreateTicketAction.php b/backend/app/Http/Actions/Products/CreateProductAction.php similarity index 54% rename from backend/app/Http/Actions/Tickets/CreateTicketAction.php rename to backend/app/Http/Actions/Products/CreateProductAction.php index 7340b99b6a..ac46cf7fde 100644 --- a/backend/app/Http/Actions/Tickets/CreateTicketAction.php +++ b/backend/app/Http/Actions/Products/CreateProductAction.php @@ -2,33 +2,33 @@ declare(strict_types=1); -namespace HiEvents\Http\Actions\Tickets; +namespace HiEvents\Http\Actions\Products; use HiEvents\DomainObjects\EventDomainObject; use HiEvents\Exceptions\InvalidTaxOrFeeIdException; use HiEvents\Http\Actions\BaseAction; -use HiEvents\Http\Request\Ticket\UpsertTicketRequest; +use HiEvents\Http\Request\Product\UpsertProductRequest; use HiEvents\Http\ResponseCodes; -use HiEvents\Resources\Ticket\TicketResource; -use HiEvents\Services\Handlers\Ticket\CreateTicketHandler; -use HiEvents\Services\Handlers\Ticket\DTO\UpsertTicketDTO; +use HiEvents\Resources\Product\ProductResource; +use HiEvents\Services\Handlers\Product\CreateProductHandler; +use HiEvents\Services\Handlers\Product\DTO\UpsertProductDTO; use Illuminate\Http\JsonResponse; use Illuminate\Validation\ValidationException; use Throwable; -class CreateTicketAction extends BaseAction +class CreateProductAction extends BaseAction { - private CreateTicketHandler $createTicketHandler; + private CreateProductHandler $createProductHandler; - public function __construct(CreateTicketHandler $handler) + public function __construct(CreateProductHandler $handler) { - $this->createTicketHandler = $handler; + $this->createProductHandler = $handler; } /** * @throws Throwable */ - public function __invoke(int $eventId, UpsertTicketRequest $request): JsonResponse + public function __invoke(int $eventId, UpsertProductRequest $request): JsonResponse { $this->isActionAuthorized($eventId, EventDomainObject::class); @@ -38,7 +38,7 @@ public function __invoke(int $eventId, UpsertTicketRequest $request): JsonRespon ]); try { - $ticket = $this->createTicketHandler->handle(UpsertTicketDTO::fromArray($request->all())); + $product = $this->createProductHandler->handle(UpsertProductDTO::fromArray($request->all())); } catch (InvalidTaxOrFeeIdException $e) { throw ValidationException::withMessages([ 'tax_and_fee_ids' => $e->getMessage(), @@ -46,8 +46,8 @@ public function __invoke(int $eventId, UpsertTicketRequest $request): JsonRespon } return $this->resourceResponse( - resource: TicketResource::class, - data: $ticket, + resource: ProductResource::class, + data: $product, statusCode: ResponseCodes::HTTP_CREATED, ); } diff --git a/backend/app/Http/Actions/Tickets/DeleteTicketAction.php b/backend/app/Http/Actions/Products/DeleteProductAction.php similarity index 60% rename from backend/app/Http/Actions/Tickets/DeleteTicketAction.php rename to backend/app/Http/Actions/Products/DeleteProductAction.php index 7c51cad3f1..3979c48690 100644 --- a/backend/app/Http/Actions/Tickets/DeleteTicketAction.php +++ b/backend/app/Http/Actions/Products/DeleteProductAction.php @@ -2,32 +2,32 @@ declare(strict_types=1); -namespace HiEvents\Http\Actions\Tickets; +namespace HiEvents\Http\Actions\Products; use HiEvents\DomainObjects\EventDomainObject; use HiEvents\Exceptions\CannotDeleteEntityException; use HiEvents\Http\Actions\BaseAction; -use HiEvents\Services\Handlers\Ticket\DeleteTicketHandler; +use HiEvents\Services\Handlers\Product\DeleteProductHandler; use Illuminate\Http\JsonResponse; use Illuminate\Http\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse; -class DeleteTicketAction extends BaseAction +class DeleteProductAction extends BaseAction { - private DeleteTicketHandler $deleteTicketHandler; + private DeleteProductHandler $deleteProductHandler; - public function __construct(DeleteTicketHandler $handler) + public function __construct(DeleteProductHandler $handler) { - $this->deleteTicketHandler = $handler; + $this->deleteProductHandler = $handler; } - public function __invoke(int $eventId, int $ticketId): Response|JsonResponse + public function __invoke(int $eventId, int $productId): Response|JsonResponse { $this->isActionAuthorized($eventId, EventDomainObject::class); try { - $this->deleteTicketHandler->handle( - ticketId: $ticketId, + $this->deleteProductHandler->handle( + productId: $productId, eventId: $eventId, ); } catch (CannotDeleteEntityException $exception) { diff --git a/backend/app/Http/Actions/Tickets/EditTicketAction.php b/backend/app/Http/Actions/Products/EditProductAction.php similarity index 53% rename from backend/app/Http/Actions/Tickets/EditTicketAction.php rename to backend/app/Http/Actions/Products/EditProductAction.php index d03111e83a..79d6fe94bc 100644 --- a/backend/app/Http/Actions/Tickets/EditTicketAction.php +++ b/backend/app/Http/Actions/Products/EditProductAction.php @@ -2,24 +2,24 @@ declare(strict_types=1); -namespace HiEvents\Http\Actions\Tickets; +namespace HiEvents\Http\Actions\Products; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\Exceptions\CannotChangeTicketTypeException; +use HiEvents\Exceptions\CannotChangeProductTypeException; use HiEvents\Exceptions\InvalidTaxOrFeeIdException; use HiEvents\Http\Actions\BaseAction; -use HiEvents\Http\Request\Ticket\UpsertTicketRequest; -use HiEvents\Resources\Ticket\TicketResource; -use HiEvents\Services\Handlers\Ticket\DTO\UpsertTicketDTO; -use HiEvents\Services\Handlers\Ticket\EditTicketHandler; +use HiEvents\Http\Request\Product\UpsertProductRequest; +use HiEvents\Resources\Product\ProductResource; +use HiEvents\Services\Handlers\Product\DTO\UpsertProductDTO; +use HiEvents\Services\Handlers\Product\EditProductHandler; use Illuminate\Http\JsonResponse; use Illuminate\Validation\ValidationException; use Throwable; -class EditTicketAction extends BaseAction +class EditProductAction extends BaseAction { public function __construct( - private readonly EditTicketHandler $editTicketHandler, + private readonly EditProductHandler $editProductHandler, ) { } @@ -28,28 +28,28 @@ public function __construct( * @throws Throwable * @throws ValidationException */ - public function __invoke(UpsertTicketRequest $request, int $eventId, int $ticketId): JsonResponse + public function __invoke(UpsertProductRequest $request, int $eventId, int $productId): JsonResponse { $this->isActionAuthorized($eventId, EventDomainObject::class); $request->merge([ 'event_id' => $eventId, 'account_id' => $this->getAuthenticatedAccountId(), - 'ticket_id' => $ticketId, + 'product_id' => $productId, ]); try { - $ticket = $this->editTicketHandler->handle(UpsertTicketDTO::fromArray($request->all())); + $product = $this->editProductHandler->handle(UpsertProductDTO::fromArray($request->all())); } catch (InvalidTaxOrFeeIdException $e) { throw ValidationException::withMessages([ 'tax_and_fee_ids' => $e->getMessage(), ]); - } catch (CannotChangeTicketTypeException $e) { + } catch (CannotChangeProductTypeException $e) { throw ValidationException::withMessages([ 'type' => $e->getMessage(), ]); } - return $this->resourceResponse(TicketResource::class, $ticket); + return $this->resourceResponse(ProductResource::class, $product); } } diff --git a/backend/app/Http/Actions/Products/GetProductAction.php b/backend/app/Http/Actions/Products/GetProductAction.php new file mode 100644 index 0000000000..f73ec84749 --- /dev/null +++ b/backend/app/Http/Actions/Products/GetProductAction.php @@ -0,0 +1,37 @@ +productRepository = $productRepository; + } + + public function __invoke(int $eventId, int $productId): JsonResponse + { + $this->isActionAuthorized($eventId, EventDomainObject::class); + + return $this->resourceResponse(ProductResource::class, $this->productRepository + ->loadRelation(TaxAndFeesDomainObject::class) + ->loadRelation(ProductPriceDomainObject::class) + ->findFirstWhere([ + ProductDomainObjectAbstract::EVENT_ID => $eventId, + ProductDomainObjectAbstract::ID => $productId, + ])); + } +} diff --git a/backend/app/Http/Actions/Tickets/GetTicketsAction.php b/backend/app/Http/Actions/Products/GetProductsAction.php similarity index 53% rename from backend/app/Http/Actions/Tickets/GetTicketsAction.php rename to backend/app/Http/Actions/Products/GetProductsAction.php index e962de2692..b7f03a2dea 100644 --- a/backend/app/Http/Actions/Tickets/GetTicketsAction.php +++ b/backend/app/Http/Actions/Products/GetProductsAction.php @@ -2,20 +2,20 @@ declare(strict_types=1); -namespace HiEvents\Http\Actions\Tickets; +namespace HiEvents\Http\Actions\Products; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Http\Actions\BaseAction; -use HiEvents\Resources\Ticket\TicketResource; -use HiEvents\Services\Handlers\Ticket\GetTicketsHandler; +use HiEvents\Resources\Product\ProductResource; +use HiEvents\Services\Handlers\Product\GetProductsHandler; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -class GetTicketsAction extends BaseAction +class GetProductsAction extends BaseAction { public function __construct( - private readonly GetTicketsHandler $getTicketsHandler, + private readonly GetProductsHandler $getProductsHandler, ) { } @@ -24,15 +24,15 @@ public function __invoke(int $eventId, Request $request): JsonResponse { $this->isActionAuthorized($eventId, EventDomainObject::class); - $tickets = $this->getTicketsHandler->handle( + $products = $this->getProductsHandler->handle( eventId: $eventId, queryParamsDTO: $this->getPaginationQueryParams($request), ); return $this->filterableResourceResponse( - resource: TicketResource::class, - data: $tickets, - domainObject: TicketDomainObject::class + resource: ProductResource::class, + data: $products, + domainObject: ProductDomainObject::class ); } } diff --git a/backend/app/Http/Actions/Tickets/SortTicketsAction.php b/backend/app/Http/Actions/Products/SortProductsAction.php similarity index 60% rename from backend/app/Http/Actions/Tickets/SortTicketsAction.php rename to backend/app/Http/Actions/Products/SortProductsAction.php index 1e17775722..82a1cddd0b 100644 --- a/backend/app/Http/Actions/Tickets/SortTicketsAction.php +++ b/backend/app/Http/Actions/Products/SortProductsAction.php @@ -1,29 +1,29 @@ isActionAuthorized($eventId, EventDomainObject::class); try { - $this->sortTicketsHandler->handle( + $this->sortProductsHandler->handle( $eventId, $request->validated(), ); diff --git a/backend/app/Http/Actions/PromoCodes/CreatePromoCodeAction.php b/backend/app/Http/Actions/PromoCodes/CreatePromoCodeAction.php index f1c8b8b74e..4631e88cf8 100644 --- a/backend/app/Http/Actions/PromoCodes/CreatePromoCodeAction.php +++ b/backend/app/Http/Actions/PromoCodes/CreatePromoCodeAction.php @@ -9,7 +9,7 @@ use HiEvents\Http\Request\PromoCode\CreateUpdatePromoCodeRequest; use HiEvents\Http\ResponseCodes; use HiEvents\Resources\PromoCode\PromoCodeResource; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\PromoCode\CreatePromoCodeHandler; use HiEvents\Services\Handlers\PromoCode\DTO\UpsertPromoCodeDTO; use Illuminate\Http\JsonResponse; @@ -35,7 +35,7 @@ public function __invoke(CreateUpdatePromoCodeRequest $request, int $eventId): J $promoCode = $this->createPromoCodeHandler->handle($eventId, new UpsertPromoCodeDTO( code: strtolower($request->input('code')), event_id: $eventId, - applicable_ticket_ids: $request->input('applicable_ticket_ids'), + applicable_product_ids: $request->input('applicable_product_ids'), discount_type: PromoCodeDiscountTypeEnum::fromName($request->input('discount_type')), discount: $request->float('discount'), expiry_date: $request->input('expiry_date'), @@ -45,9 +45,9 @@ public function __invoke(CreateUpdatePromoCodeRequest $request, int $eventId): J throw ValidationException::withMessages([ 'code' => $e->getMessage(), ]); - } catch (UnrecognizedTicketIdException $e) { + } catch (UnrecognizedProductIdException $e) { throw ValidationException::withMessages([ - 'applicable_ticket_ids' => $e->getMessage(), + 'applicable_product_ids' => $e->getMessage(), ]); } diff --git a/backend/app/Http/Actions/PromoCodes/UpdatePromoCodeAction.php b/backend/app/Http/Actions/PromoCodes/UpdatePromoCodeAction.php index 0416ed8073..b3c4c5c467 100644 --- a/backend/app/Http/Actions/PromoCodes/UpdatePromoCodeAction.php +++ b/backend/app/Http/Actions/PromoCodes/UpdatePromoCodeAction.php @@ -9,7 +9,7 @@ use HiEvents\Http\Request\PromoCode\CreateUpdatePromoCodeRequest; use HiEvents\Http\ResponseCodes; use HiEvents\Resources\PromoCode\PromoCodeResource; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\PromoCode\DTO\UpsertPromoCodeDTO; use HiEvents\Services\Handlers\PromoCode\UpdatePromoCodeHandler; use Illuminate\Http\JsonResponse; @@ -35,7 +35,7 @@ public function __invoke(CreateUpdatePromoCodeRequest $request, int $eventId, in $promoCode = $this->updatePromoCodeHandler->handle($promoCodeId, new UpsertPromoCodeDTO( code: strtolower($request->input('code')), event_id: $eventId, - applicable_ticket_ids: $request->input('applicable_ticket_ids'), + applicable_product_ids: $request->input('applicable_product_ids'), discount_type: PromoCodeDiscountTypeEnum::fromName($request->input('discount_type')), discount: $request->float('discount'), expiry_date: $request->input('expiry_date'), @@ -45,9 +45,9 @@ public function __invoke(CreateUpdatePromoCodeRequest $request, int $eventId, in throw ValidationException::withMessages([ 'code' => $e->getMessage(), ]); - } catch (UnrecognizedTicketIdException $e) { + } catch (UnrecognizedProductIdException $e) { throw ValidationException::withMessages([ - 'applicable_ticket_ids' => $e->getMessage(), + 'applicable_product_ids' => $e->getMessage(), ]); } diff --git a/backend/app/Http/Actions/Questions/CreateQuestionAction.php b/backend/app/Http/Actions/Questions/CreateQuestionAction.php index a5a9ca71dd..d13b8b0d89 100644 --- a/backend/app/Http/Actions/Questions/CreateQuestionAction.php +++ b/backend/app/Http/Actions/Questions/CreateQuestionAction.php @@ -30,7 +30,7 @@ public function __invoke(UpsertQuestionRequest $request, int $eventId): JsonResp 'required' => $request->boolean('required'), 'options' => $request->input('options'), 'event_id' => $eventId, - 'ticket_ids' => $request->input('ticket_ids'), + 'product_ids' => $request->input('product_ids'), 'belongs_to' => $request->input('belongs_to'), 'is_hidden' => $request->boolean('is_hidden'), 'description' => $request->input('description'), diff --git a/backend/app/Http/Actions/Questions/EditQuestionAction.php b/backend/app/Http/Actions/Questions/EditQuestionAction.php index 616584e972..189bdec41a 100644 --- a/backend/app/Http/Actions/Questions/EditQuestionAction.php +++ b/backend/app/Http/Actions/Questions/EditQuestionAction.php @@ -37,7 +37,7 @@ public function __invoke(UpsertQuestionRequest $request, int $eventId, int $ques 'required' => $request->boolean('required'), 'options' => $request->input('options'), 'event_id' => $eventId, - 'ticket_ids' => $request->input('ticket_ids'), + 'product_ids' => $request->input('product_ids'), 'is_hidden' => $request->boolean('is_hidden'), 'belongs_to' => QuestionBelongsTo::fromName($request->input('belongs_to')), 'description' => $request->input('description'), diff --git a/backend/app/Http/Actions/Questions/GetQuestionAction.php b/backend/app/Http/Actions/Questions/GetQuestionAction.php index 6759fd3d4c..6701e7ce3f 100644 --- a/backend/app/Http/Actions/Questions/GetQuestionAction.php +++ b/backend/app/Http/Actions/Questions/GetQuestionAction.php @@ -3,7 +3,7 @@ namespace HiEvents\Http\Actions\Questions; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Http\Actions\BaseAction; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; use HiEvents\Resources\Question\QuestionResource; @@ -24,7 +24,7 @@ public function __invoke(Request $request, int $eventId, int $questionId): JsonR $this->isActionAuthorized($eventId, EventDomainObject::class); $questions = $this->questionRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findById($questionId); return $this->resourceResponse(QuestionResource::class, $questions); diff --git a/backend/app/Http/Actions/Questions/GetQuestionsAction.php b/backend/app/Http/Actions/Questions/GetQuestionsAction.php index fa9fe3a4ae..4a234442c0 100644 --- a/backend/app/Http/Actions/Questions/GetQuestionsAction.php +++ b/backend/app/Http/Actions/Questions/GetQuestionsAction.php @@ -3,8 +3,8 @@ namespace HiEvents\Http\Actions\Questions; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Http\Actions\BaseAction; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; @@ -27,8 +27,8 @@ public function __invoke(Request $request, int $eventId): JsonResponse $questions = $this->questionRepository ->loadRelation( - new Relationship(TicketDomainObject::class, [ - new Relationship(TicketPriceDomainObject::class) + new Relationship(ProductDomainObject::class, [ + new Relationship(ProductPriceDomainObject::class) ]) ) ->findByEventId($eventId); diff --git a/backend/app/Http/Actions/Questions/GetQuestionsPublicAction.php b/backend/app/Http/Actions/Questions/GetQuestionsPublicAction.php index 8fe8250251..8e718f2846 100644 --- a/backend/app/Http/Actions/Questions/GetQuestionsPublicAction.php +++ b/backend/app/Http/Actions/Questions/GetQuestionsPublicAction.php @@ -3,7 +3,7 @@ namespace HiEvents\Http\Actions\Questions; use HiEvents\DomainObjects\Generated\QuestionDomainObjectAbstract; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Http\Actions\BaseAction; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; use HiEvents\Resources\Question\QuestionResourcePublic; @@ -22,7 +22,7 @@ public function __construct(QuestionRepositoryInterface $questionRepository) public function __invoke(Request $request, int $eventId): JsonResponse { $questions = $this->questionRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findWhere([ QuestionDomainObjectAbstract::EVENT_ID => $eventId, QuestionDomainObjectAbstract::IS_HIDDEN => false, diff --git a/backend/app/Http/Actions/Tickets/GetTicketAction.php b/backend/app/Http/Actions/Tickets/GetTicketAction.php deleted file mode 100644 index be28a9f4bc..0000000000 --- a/backend/app/Http/Actions/Tickets/GetTicketAction.php +++ /dev/null @@ -1,37 +0,0 @@ -ticketRepository = $ticketRepository; - } - - public function __invoke(int $eventId, int $ticketId): JsonResponse - { - $this->isActionAuthorized($eventId, EventDomainObject::class); - - return $this->resourceResponse(TicketResource::class, $this->ticketRepository - ->loadRelation(TaxAndFeesDomainObject::class) - ->loadRelation(TicketPriceDomainObject::class) - ->findFirstWhere([ - TicketDomainObjectAbstract::EVENT_ID => $eventId, - TicketDomainObjectAbstract::ID => $ticketId, - ])); - } -} diff --git a/backend/app/Http/Request/Attendee/CreateAttendeeRequest.php b/backend/app/Http/Request/Attendee/CreateAttendeeRequest.php index d5a6faee6e..0bbd3eed75 100644 --- a/backend/app/Http/Request/Attendee/CreateAttendeeRequest.php +++ b/backend/app/Http/Request/Attendee/CreateAttendeeRequest.php @@ -12,8 +12,8 @@ class CreateAttendeeRequest extends BaseRequest public function rules(): array { return [ - 'ticket_id' => ['int', 'required'], - 'ticket_price_id' => ['int', 'nullable'], + 'product_id' => ['int', 'required'], + 'product_price_id' => ['int', 'nullable'], 'email' => ['required', 'email'], 'first_name' => 'string|required', 'last_name' => 'string', diff --git a/backend/app/Http/Request/Attendee/EditAttendeeRequest.php b/backend/app/Http/Request/Attendee/EditAttendeeRequest.php index 10cf0ae4ac..0badf3043d 100644 --- a/backend/app/Http/Request/Attendee/EditAttendeeRequest.php +++ b/backend/app/Http/Request/Attendee/EditAttendeeRequest.php @@ -13,8 +13,8 @@ public function rules(): array 'email' => RulesHelper::REQUIRED_EMAIL, 'first_name' => RulesHelper::REQUIRED_STRING, 'last_name' => RulesHelper::REQUIRED_STRING, - 'ticket_id' => RulesHelper::REQUIRED_NUMERIC, - 'ticket_price_id' => RulesHelper::REQUIRED_NUMERIC, + 'product_id' => RulesHelper::REQUIRED_NUMERIC, + 'product_price_id' => RulesHelper::REQUIRED_NUMERIC, ]; } @@ -25,10 +25,10 @@ public function messages(): array 'email.email' => __('Email must be a valid email address'), 'first_name.required' => __('First name is required'), 'last_name.required' => __('Last name is required'), - 'ticket_id.required' => __('Ticket is required'), - 'ticket_price_id.required' => __('Ticket price is required'), - 'ticket_id.numeric' => '', - 'ticket_price_id.numeric' => '', + 'product_id.required' => __('Product is required'), + 'product_price_id.required' => __('Product price is required'), + 'product_id.numeric' => '', + 'product_price_id.numeric' => '', ]; } } diff --git a/backend/app/Http/Request/CapacityAssigment/UpsertCapacityAssignmentRequest.php b/backend/app/Http/Request/CapacityAssigment/UpsertCapacityAssignmentRequest.php index 0873bfac27..008000d444 100644 --- a/backend/app/Http/Request/CapacityAssigment/UpsertCapacityAssignmentRequest.php +++ b/backend/app/Http/Request/CapacityAssigment/UpsertCapacityAssignmentRequest.php @@ -15,14 +15,14 @@ public function rules(): array 'name' => RulesHelper::REQUIRED_STRING, 'capacity' => ['nullable', 'numeric', 'min:1'], 'status' => ['required', Rule::in(CapacityAssignmentStatus::valuesArray())], - 'ticket_ids' => ['required', 'array'], + 'product_ids' => ['required', 'array'], ]; } public function messages(): array { return [ - 'ticket_ids.required' => __('Please select at least one ticket.'), + 'product_ids.required' => __('Please select at least one product.'), ]; } } diff --git a/backend/app/Http/Request/CheckInList/UpsertCheckInListRequest.php b/backend/app/Http/Request/CheckInList/UpsertCheckInListRequest.php index 8405521d2a..06372e6760 100644 --- a/backend/app/Http/Request/CheckInList/UpsertCheckInListRequest.php +++ b/backend/app/Http/Request/CheckInList/UpsertCheckInListRequest.php @@ -14,7 +14,7 @@ public function rules(): array 'description' => ['nullable', 'string', 'max:255'], 'expires_at' => ['nullable', 'date'], 'activates_at' => ['nullable', 'date'], - 'ticket_ids' => ['required', 'array', 'min:1'], + 'product_ids' => ['required', 'array', 'min:1'], ]; } @@ -32,7 +32,7 @@ public function withValidator($validator): void public function messages(): array { return [ - 'ticket_ids.required' => __('Please select at least one ticket.'), + 'product_ids.required' => __('Please select at least one product.'), 'expires_at.after' => __('The expiration date must be after the activation date.'), 'activates_at.before' => __('The activation date must be before the expiration date.'), ]; diff --git a/backend/app/Http/Request/Event/DuplicateEventRequest.php b/backend/app/Http/Request/Event/DuplicateEventRequest.php index 70e0420abd..c172c118e1 100644 --- a/backend/app/Http/Request/Event/DuplicateEventRequest.php +++ b/backend/app/Http/Request/Event/DuplicateEventRequest.php @@ -14,7 +14,7 @@ public function rules(): array $eventValidations = $this->minimalRules(); $duplicateValidations = [ - 'duplicate_tickets' => ['boolean', 'required'], + 'duplicate_products' => ['boolean', 'required'], 'duplicate_questions' => ['boolean', 'required'], 'duplicate_settings' => ['boolean', 'required'], 'duplicate_promo_codes' => ['boolean', 'required'], diff --git a/backend/app/Http/Request/EventSettings/UpdateEventSettingsRequest.php b/backend/app/Http/Request/EventSettings/UpdateEventSettingsRequest.php index 4b1ad92264..5942b7c74c 100644 --- a/backend/app/Http/Request/EventSettings/UpdateEventSettingsRequest.php +++ b/backend/app/Http/Request/EventSettings/UpdateEventSettingsRequest.php @@ -69,8 +69,8 @@ public function messages(): array 'homepage_text_color' => $colorMessage, 'homepage_button_color' => $colorMessage, 'homepage_link_color' => $colorMessage, - 'homepage_ticket_widget_background_color' => $colorMessage, - 'homepage_ticket_widget_text_color' => $colorMessage, + 'homepage_product_widget_background_color' => $colorMessage, + 'homepage_product_widget_text_color' => $colorMessage, 'location_details.address_line_1.required_with' => __('The address line 1 field is required'), 'location_details.city.required_with' => __('The city field is required'), 'location_details.zip_or_postal_code.required_with' => __('The zip or postal code field is required'), diff --git a/backend/app/Http/Request/Message/SendMessageRequest.php b/backend/app/Http/Request/Message/SendMessageRequest.php index 35f69c7b71..5eb7851d50 100644 --- a/backend/app/Http/Request/Message/SendMessageRequest.php +++ b/backend/app/Http/Request/Message/SendMessageRequest.php @@ -17,9 +17,9 @@ public function rules(): array 'is_test' => 'boolean', 'attendee_ids' => 'max:50,array|required_if:message_type,' . MessageTypeEnum::ATTENDEE->name, 'attendee_ids.*' => 'integer', - 'ticket_ids' => ['array', 'required_if:message_type,' . MessageTypeEnum::TICKET->name], + 'product_ids' => ['array', 'required_if:message_type,' . MessageTypeEnum::PRODUCT->name], 'order_id' => 'integer|required_if:message_type,' . MessageTypeEnum::ORDER->name, - 'ticket_ids.*' => 'integer', + 'product_ids.*' => 'integer', ]; } @@ -28,7 +28,7 @@ public function messages(): array return [ 'subject.max' => 'The subject must be less than 100 characters.', 'attendee_ids.max' => 'You can only send a message to a maximum of 50 individual attendees at a time. ' . - 'To message more attendees, you can send to attendees with a specific ticket, or to all event attendees.' + 'To message more attendees, you can send to attendees with a specific product, or to all event attendees.' ]; } } diff --git a/backend/app/Http/Request/Ticket/SortTicketsRequest.php b/backend/app/Http/Request/Product/SortProductsRequest.php similarity index 71% rename from backend/app/Http/Request/Ticket/SortTicketsRequest.php rename to backend/app/Http/Request/Product/SortProductsRequest.php index 5ca72804f9..df1844589a 100644 --- a/backend/app/Http/Request/Ticket/SortTicketsRequest.php +++ b/backend/app/Http/Request/Product/SortProductsRequest.php @@ -1,10 +1,10 @@ 'integer|nullable', 'prices' => ['required', 'array'], 'prices.*.price' => [...RulesHelper::MONEY, 'required'], - 'prices.*.label' => ['nullable', ...RulesHelper::STRING, 'required_if:type,' . TicketType::TIERED->name], + 'prices.*.label' => ['nullable', ...RulesHelper::STRING, 'required_if:type,' . ProductPriceType::TIERED->name], 'prices.*.sale_start_date' => ['date', 'nullable', 'after:sale_start_date'], 'prices.*.sale_end_date' => 'date|nullable|after:prices.*.sale_start_date', 'prices.*.initial_quantity_available' => ['integer', 'nullable', 'min:0'], @@ -35,7 +35,7 @@ public function rules(): array 'hide_when_sold_out' => 'boolean', 'show_quantity_remaining' => 'boolean', 'is_hidden_without_promo_code' => 'boolean', - 'type' => ['required', Rule::in(TicketType::valuesArray())], + 'type' => ['required', Rule::in(ProductPriceType::valuesArray())], 'tax_and_fee_ids' => 'array', ]; } @@ -46,7 +46,7 @@ public function messages(): array 'sale_end_date.after' => __('The sale end date must be after the sale start date.'), 'prices.*.sale_end_date.after' => __('The sale end date must be after the sale start date.'), 'prices.*.sale_end_date.date' => __('The sale end date must be a valid date.'), - 'prices.*.sale_start_date.after' => __('The sale start date must be after the ticket sale start date.'), + 'prices.*.sale_start_date.after' => __('The sale start date must be after the product sale start date.'), ]; } } diff --git a/backend/app/Http/Request/PromoCode/CreateUpdatePromoCodeRequest.php b/backend/app/Http/Request/PromoCode/CreateUpdatePromoCodeRequest.php index 328fd0cbd1..5e381f6120 100644 --- a/backend/app/Http/Request/PromoCode/CreateUpdatePromoCodeRequest.php +++ b/backend/app/Http/Request/PromoCode/CreateUpdatePromoCodeRequest.php @@ -12,7 +12,7 @@ public function rules(): array { return [ 'code' => 'min:2|string|required|max:50', - 'applicable_ticket_ids' => 'array', + 'applicable_product_ids' => 'array', 'discount' => [ 'required_if:discount_type,PERCENTAGE,FIXED', 'numeric', diff --git a/backend/app/Http/Request/Questions/UpsertQuestionRequest.php b/backend/app/Http/Request/Questions/UpsertQuestionRequest.php index e696d15e3e..a383a0036d 100644 --- a/backend/app/Http/Request/Questions/UpsertQuestionRequest.php +++ b/backend/app/Http/Request/Questions/UpsertQuestionRequest.php @@ -15,9 +15,9 @@ public function rules(): array 'title' => ['string', 'required'], 'description' => ['string', 'nullable', 'max:10000'], 'type' => ['required', Rule::in(QuestionTypeEnum::valuesArray())], - 'ticket_ids' => ['array', 'required_if:belongs_to,TICKET'], + 'product_ids' => ['array', 'required_if:belongs_to,PRODUCT'], 'belongs_to' => [ - ['required', Rule::in([QuestionBelongsTo::TICKET->name, QuestionBelongsTo::ORDER->name])], + ['required', Rule::in([QuestionBelongsTo::PRODUCT->name, QuestionBelongsTo::ORDER->name])], ], 'options' => 'max:2000|required_if:type,CHECKBOX,RADIO', 'required' => 'required|boolean', @@ -28,7 +28,7 @@ public function rules(): array public function messages(): array { return [ - 'ticket_ids.required_if' => __('Please select at least one ticket.'), + 'product_ids.required_if' => __('Please select at least one product.'), ]; } } diff --git a/backend/app/Jobs/Order/SendOrderDetailsEmailJob.php b/backend/app/Jobs/Order/SendOrderDetailsEmailJob.php index c6fb63f948..4ca2fcf193 100644 --- a/backend/app/Jobs/Order/SendOrderDetailsEmailJob.php +++ b/backend/app/Jobs/Order/SendOrderDetailsEmailJob.php @@ -22,6 +22,6 @@ public function __construct(private readonly OrderDomainObject $order) public function handle(SendOrderDetailsService $service): void { - $service->sendOrderSummaryAndTicketEmails($this->order); + $service->sendOrderSummaryAndProductEmails($this->order); } } diff --git a/backend/app/Mail/Attendee/AttendeeTicketMail.php b/backend/app/Mail/Attendee/AttendeeTicketMail.php index 722d91ac8a..90d90a5fff 100644 --- a/backend/app/Mail/Attendee/AttendeeTicketMail.php +++ b/backend/app/Mail/Attendee/AttendeeTicketMail.php @@ -45,14 +45,14 @@ public function envelope(): Envelope public function content(): Content { return new Content( - markdown: 'emails.orders.attendee-ticket', + markdown: 'emails.orders.attendee-product', with: [ 'event' => $this->event, 'attendee' => $this->attendee, 'eventSettings' => $this->eventSettings, 'organizer' => $this->organizer, - 'ticketUrl' => sprintf( - Url::getFrontEndUrlFromConfig(Url::ATTENDEE_TICKET), + 'productUrl' => sprintf( + Url::getFrontEndUrlFromConfig(Url::ATTENDEE_PRODUCT), $this->event->getId(), $this->attendee->getShortId(), ) diff --git a/backend/app/Models/Attendee.php b/backend/app/Models/Attendee.php index 9164ce2361..476a6d1609 100644 --- a/backend/app/Models/Attendee.php +++ b/backend/app/Models/Attendee.php @@ -30,9 +30,9 @@ public function order(): BelongsTo return $this->belongsTo(Order::class); } - public function ticket(): BelongsTo + public function product(): BelongsTo { - return $this->belongsTo(Ticket::class); + return $this->belongsTo(Product::class); } public function check_in(): HasOne diff --git a/backend/app/Models/AttendeeCheckIn.php b/backend/app/Models/AttendeeCheckIn.php index be8f6f681f..50eff85135 100644 --- a/backend/app/Models/AttendeeCheckIn.php +++ b/backend/app/Models/AttendeeCheckIn.php @@ -16,10 +16,10 @@ protected function getFillableFields(): array return []; } - public function tickets(): BelongsTo + public function products(): BelongsTo { return $this->belongsTo( - related: Ticket::class, + related: Product::class, ); } diff --git a/backend/app/Models/CapacityAssignment.php b/backend/app/Models/CapacityAssignment.php index 7359f9455e..87518f133b 100644 --- a/backend/app/Models/CapacityAssignment.php +++ b/backend/app/Models/CapacityAssignment.php @@ -22,11 +22,11 @@ public function event(): BelongsTo return $this->belongsTo(Event::class); } - public function tickets(): BelongsToMany + public function products(): BelongsToMany { return $this->belongsToMany( - related: Ticket::class, - table: 'ticket_capacity_assignments', + related: Product::class, + table: 'product_capacity_assignments', ); } } diff --git a/backend/app/Models/CheckInList.php b/backend/app/Models/CheckInList.php index d61c62b989..0e918372e1 100644 --- a/backend/app/Models/CheckInList.php +++ b/backend/app/Models/CheckInList.php @@ -17,11 +17,11 @@ protected function getFillableFields(): array return []; } - public function tickets(): BelongsToMany + public function products(): BelongsToMany { return $this->belongsToMany( - related: Ticket::class, - table: 'ticket_check_in_lists', + related: Product::class, + table: 'product_check_in_lists', ); } diff --git a/backend/app/Models/Event.php b/backend/app/Models/Event.php index d7be878910..2c092fd1b7 100644 --- a/backend/app/Models/Event.php +++ b/backend/app/Models/Event.php @@ -19,9 +19,9 @@ public function organizer(): BelongsTo return $this->belongsTo(Organizer::class); } - public function tickets(): HasMany + public function products(): HasMany { - return $this->hasMany(Ticket::class)->orderBy('order'); + return $this->hasMany(Product::class)->orderBy('order'); } public function attendees(): HasMany diff --git a/backend/app/Models/Message.php b/backend/app/Models/Message.php index 7e08f7aed7..1cac8fae37 100644 --- a/backend/app/Models/Message.php +++ b/backend/app/Models/Message.php @@ -15,7 +15,7 @@ protected function getCastMap(): array { return [ 'attendee_ids' => 'array', - 'ticket_ids' => 'array', + 'product_ids' => 'array', 'send_data' => 'array', ]; } diff --git a/backend/app/Models/OrderItem.php b/backend/app/Models/OrderItem.php index b1b87bd985..cd5796707f 100644 --- a/backend/app/Models/OrderItem.php +++ b/backend/app/Models/OrderItem.php @@ -36,13 +36,13 @@ protected function getFillableFields(): array return []; } - public function ticket_price(): HasOne + public function product_price(): HasOne { - return $this->hasOne(TicketPrice::class); + return $this->hasOne(ProductPrice::class); } - public function ticket(): BelongsTo + public function product(): BelongsTo { - return $this->belongsTo(Ticket::class); + return $this->belongsTo(Product::class); } } diff --git a/backend/app/Models/Ticket.php b/backend/app/Models/Product.php similarity index 52% rename from backend/app/Models/Ticket.php rename to backend/app/Models/Product.php index dea99092f3..46bb9d6a88 100644 --- a/backend/app/Models/Ticket.php +++ b/backend/app/Models/Product.php @@ -4,17 +4,17 @@ namespace HiEvents\Models; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -class Ticket extends BaseModel +class Product extends BaseModel { protected function getCastMap(): array { return [ - TicketDomainObjectAbstract::SALES_VOLUME => 'float', - TicketDomainObjectAbstract::SALES_TAX_VOLUME => 'float', + ProductDomainObjectAbstract::SALES_VOLUME => 'float', + ProductDomainObjectAbstract::SALES_TAX_VOLUME => 'float', ]; } @@ -25,26 +25,26 @@ protected function getFillableFields(): array public function questions(): BelongsToMany { - return $this->belongsToMany(Question::class, 'ticket_questions'); + return $this->belongsToMany(Question::class, 'product_questions'); } - public function ticket_prices(): HasMany + public function product_prices(): HasMany { - return $this->hasMany(TicketPrice::class)->orderBy('order'); + return $this->hasMany(ProductPrice::class)->orderBy('order'); } public function tax_and_fees(): BelongsToMany { - return $this->belongsToMany(TaxAndFee::class, 'ticket_taxes_and_fees'); + return $this->belongsToMany(TaxAndFee::class, 'product_taxes_and_fees'); } public function capacity_assignments(): BelongsToMany { - return $this->belongsToMany(CapacityAssignment::class, 'ticket_capacity_assignments'); + return $this->belongsToMany(CapacityAssignment::class, 'product_capacity_assignments'); } public function check_in_lists(): BelongsToMany { - return $this->belongsToMany(CheckInList::class, 'ticket_check_in_lists'); + return $this->belongsToMany(CheckInList::class, 'product_check_in_lists'); } } diff --git a/backend/app/Models/TicketPrice.php b/backend/app/Models/ProductPrice.php similarity index 70% rename from backend/app/Models/TicketPrice.php rename to backend/app/Models/ProductPrice.php index 1ac6e299e1..23ab15a389 100644 --- a/backend/app/Models/TicketPrice.php +++ b/backend/app/Models/ProductPrice.php @@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; -class TicketPrice extends BaseModel +class ProductPrice extends BaseModel { protected function getCastMap(): array { @@ -18,8 +18,8 @@ protected function getFillableFields(): array return []; } - public function ticket(): BelongsTo + public function product(): BelongsTo { - return $this->belongsTo(Ticket::class); + return $this->belongsTo(Product::class); } } diff --git a/backend/app/Models/TicketQuestion.php b/backend/app/Models/ProductQuestion.php similarity index 55% rename from backend/app/Models/TicketQuestion.php rename to backend/app/Models/ProductQuestion.php index 4c85d75c10..fee35fc522 100644 --- a/backend/app/Models/TicketQuestion.php +++ b/backend/app/Models/ProductQuestion.php @@ -2,9 +2,9 @@ namespace HiEvents\Models; -use HiEvents\DomainObjects\Generated\TicketQuestionDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductQuestionDomainObjectAbstract; -class TicketQuestion extends BaseModel +class ProductQuestion extends BaseModel { protected function getTimestampsEnabled(): bool { @@ -19,8 +19,8 @@ protected function getCastMap(): array protected function getFillableFields(): array { return [ - TicketQuestionDomainObjectAbstract::QUESTION_ID, - TicketQuestionDomainObjectAbstract::TICKET_ID, + ProductQuestionDomainObjectAbstract::QUESTION_ID, + ProductQuestionDomainObjectAbstract::PRODUCT_ID, ]; } } diff --git a/backend/app/Models/PromoCode.php b/backend/app/Models/PromoCode.php index d6d7545afb..1e0ad4f0c5 100644 --- a/backend/app/Models/PromoCode.php +++ b/backend/app/Models/PromoCode.php @@ -11,7 +11,7 @@ protected function getCastMap(): array return [ PromoCodeDomainObjectAbstract::DISCOUNT => 'float', PromoCodeDomainObjectAbstract::EXPIRY_DATE => 'datetime', - PromoCodeDomainObjectAbstract::APPLICABLE_TICKET_IDS => 'array', + PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS => 'array', ]; } @@ -21,7 +21,7 @@ protected function getFillableFields(): array PromoCodeDomainObjectAbstract::CODE, PromoCodeDomainObjectAbstract::DISCOUNT, PromoCodeDomainObjectAbstract::DISCOUNT_TYPE, - PromoCodeDomainObjectAbstract::APPLICABLE_TICKET_IDS, + PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS, PromoCodeDomainObjectAbstract::EXPIRY_DATE, PromoCodeDomainObjectAbstract::EVENT_ID, PromoCodeDomainObjectAbstract::MAX_ALLOWED_USAGES, diff --git a/backend/app/Models/Question.php b/backend/app/Models/Question.php index 1fa2744f8d..f540466f5a 100644 --- a/backend/app/Models/Question.php +++ b/backend/app/Models/Question.php @@ -19,10 +19,10 @@ protected function getFillableFields(): array return []; } - public function tickets(): BelongsToMany + public function products(): BelongsToMany { return $this - ->belongsToMany(Ticket::class, 'ticket_questions') - ->whereNull('ticket_questions.deleted_at'); + ->belongsToMany(Product::class, 'product_questions') + ->whereNull('product_questions.deleted_at'); } } diff --git a/backend/app/Models/QuestionAnswer.php b/backend/app/Models/QuestionAnswer.php index 3564d22ce5..c9c4de1313 100644 --- a/backend/app/Models/QuestionAnswer.php +++ b/backend/app/Models/QuestionAnswer.php @@ -17,7 +17,7 @@ protected function getFillableFields(): array { return [ QuestionAnswerDomainObjectAbstract::QUESTION_ID, - QuestionAnswerDomainObjectAbstract::TICKET_ID, + QuestionAnswerDomainObjectAbstract::PRODUCT_ID, QuestionAnswerDomainObjectAbstract::ORDER_ID, QuestionAnswerDomainObjectAbstract::ATTENDEE_ID, QuestionAnswerDomainObjectAbstract::ANSWER, diff --git a/backend/app/Models/TaxAndFee.php b/backend/app/Models/TaxAndFee.php index 32a656b9a5..1d36edbe80 100644 --- a/backend/app/Models/TaxAndFee.php +++ b/backend/app/Models/TaxAndFee.php @@ -8,9 +8,9 @@ class TaxAndFee extends BaseModel { protected $table = 'taxes_and_fees'; - public function tickets(): BelongsToMany + public function products(): BelongsToMany { - return $this->belongsToMany(Ticket::class, 'ticket_taxes_and_fees'); + return $this->belongsToMany(Product::class, 'product_taxes_and_fees'); } protected function getCastMap(): array diff --git a/backend/app/Providers/RepositoryServiceProvider.php b/backend/app/Providers/RepositoryServiceProvider.php index 2ae3adc901..7ff4caf5f5 100644 --- a/backend/app/Providers/RepositoryServiceProvider.php +++ b/backend/app/Providers/RepositoryServiceProvider.php @@ -27,8 +27,8 @@ use HiEvents\Repository\Eloquent\StripeCustomerRepository; use HiEvents\Repository\Eloquent\StripePaymentsRepository; use HiEvents\Repository\Eloquent\TaxAndFeeRepository; -use HiEvents\Repository\Eloquent\TicketPriceRepository; -use HiEvents\Repository\Eloquent\TicketRepository; +use HiEvents\Repository\Eloquent\ProductPriceRepository; +use HiEvents\Repository\Eloquent\ProductRepository; use HiEvents\Repository\Eloquent\UserRepository; use HiEvents\Repository\Interfaces\AccountRepositoryInterface; use HiEvents\Repository\Interfaces\AccountUserRepositoryInterface; @@ -53,8 +53,8 @@ use HiEvents\Repository\Interfaces\StripeCustomerRepositoryInterface; use HiEvents\Repository\Interfaces\StripePaymentsRepositoryInterface; use HiEvents\Repository\Interfaces\TaxAndFeeRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketPriceRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductPriceRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Repository\Interfaces\UserRepositoryInterface; use Illuminate\Support\ServiceProvider; @@ -67,7 +67,7 @@ class RepositoryServiceProvider extends ServiceProvider UserRepositoryInterface::class => UserRepository::class, AccountRepositoryInterface::class => AccountRepository::class, EventRepositoryInterface::class => EventRepository::class, - TicketRepositoryInterface::class => TicketRepository::class, + ProductRepositoryInterface::class => ProductRepository::class, OrderRepositoryInterface::class => OrderRepository::class, AttendeeRepositoryInterface::class => AttendeeRepository::class, OrderItemRepositoryInterface::class => OrderItemRepository::class, @@ -80,7 +80,7 @@ class RepositoryServiceProvider extends ServiceProvider PasswordResetRepositoryInterface::class => PasswordResetRepository::class, TaxAndFeeRepositoryInterface::class => TaxAndFeeRepository::class, ImageRepositoryInterface::class => ImageRepository::class, - TicketPriceRepositoryInterface::class => TicketPriceRepository::class, + ProductPriceRepositoryInterface::class => ProductPriceRepository::class, EventStatisticRepositoryInterface::class => EventStatisticRepository::class, EventDailyStatisticRepositoryInterface::class => EventDailyStatisticRepository::class, EventSettingsRepositoryInterface::class => EventSettingsRepository::class, diff --git a/backend/app/Repository/Eloquent/AttendeeRepository.php b/backend/app/Repository/Eloquent/AttendeeRepository.php index a60f3439f9..e0e16e381e 100644 --- a/backend/app/Repository/Eloquent/AttendeeRepository.php +++ b/backend/app/Repository/Eloquent/AttendeeRepository.php @@ -108,8 +108,8 @@ public function getAttendeesByCheckInShortId(string $shortId, QueryParamsDTO $pa $this->model = $this->model->select('attendees.*') ->join('orders', 'orders.id', '=', 'attendees.order_id') - ->join('ticket_check_in_lists', 'ticket_check_in_lists.ticket_id', '=', 'attendees.ticket_id') - ->join('check_in_lists', 'check_in_lists.id', '=', 'ticket_check_in_lists.check_in_list_id') + ->join('product_check_in_lists', 'product_check_in_lists.product_id', '=', 'attendees.product_id') + ->join('check_in_lists', 'check_in_lists.id', '=', 'product_check_in_lists.check_in_list_id') ->where('check_in_lists.short_id', $shortId) ->where('attendees.status', AttendeeStatus::ACTIVE->name) ->whereIn('orders.status', [OrderStatus::COMPLETED->name]); diff --git a/backend/app/Repository/Eloquent/CheckInListRepository.php b/backend/app/Repository/Eloquent/CheckInListRepository.php index 45d804461c..47baa4e7fa 100644 --- a/backend/app/Repository/Eloquent/CheckInListRepository.php +++ b/backend/app/Repository/Eloquent/CheckInListRepository.php @@ -38,7 +38,7 @@ public function getCheckedInAttendeeCountById(int $checkInListId): CheckedInAtte valid_attendees AS ( SELECT a.id, tcil.check_in_list_id FROM attendees a - JOIN ticket_check_in_lists tcil ON a.ticket_id = tcil.ticket_id + JOIN product_check_in_lists tcil ON a.product_id = tcil.product_id WHERE a.deleted_at IS NULL AND tcil.deleted_at IS NULL ) @@ -78,7 +78,7 @@ public function getCheckedInAttendeeCountByIds(array $checkInListIds): Collectio valid_attendees AS ( SELECT a.id, tcil.check_in_list_id FROM attendees a - JOIN ticket_check_in_lists tcil ON a.ticket_id = tcil.ticket_id + JOIN product_check_in_lists tcil ON a.product_id = tcil.product_id WHERE a.deleted_at IS NULL AND tcil.deleted_at IS NULL AND a.status = '$attendeeActiveStatus' diff --git a/backend/app/Repository/Eloquent/ProductPriceRepository.php b/backend/app/Repository/Eloquent/ProductPriceRepository.php new file mode 100644 index 0000000000..7114d6f3bc --- /dev/null +++ b/backend/app/Repository/Eloquent/ProductPriceRepository.php @@ -0,0 +1,20 @@ +query)) { + $where[] = static function (Builder $builder) use ($params) { + $builder + ->where(ProductDomainObjectAbstract::TITLE, 'ilike', '%' . $params->query . '%'); + }; + } + + $this->model = $this->model->orderBy( + $params->sort_by ?? ProductDomainObject::getDefaultSort(), + $params->sort_direction ?? ProductDomainObject::getDefaultSortDirection(), + ); + + return $this->paginateWhere( + where: $where, + limit: $params->per_page, + page: $params->page, + ); + } + + /** + * @param int $productId + * @param int $productPriceId + * @return int + */ + public function getQuantityRemainingForProductPrice(int $productId, int $productPriceId): int + { + $query = <<db->selectOne($query, [ + 'productPriceId' => $productPriceId, + 'productId' => $productId + ]); + + if ($result === null) { + throw new RuntimeException('Product price not found'); + } + + if ($result->unlimited_products_available) { + return Constants::INFINITE; + } + + return (int)$result->quantity_remaining; + } + + public function getTaxesByProductId(int $productId): Collection + { + $query = <<db->select($query, [ + 'productId' => $productId + ]); + + return $this->handleResults($taxAndFees, TaxAndFeesDomainObject::class); + } + + public function getProductsByTaxId(int $taxId): Collection + { + $query = <<model->select($query, [ + 'taxAndFeeId' => $taxId + ]); + + return $this->handleResults($products, ProductDomainObject::class); + } + + public function getCapacityAssignmentsByProductId(int $productId): Collection + { + $capacityAssignments = CapacityAssignment::whereHas('products', static function ($query) use ($productId) { + $query->where('product_id', $productId); + })->get(); + + return $this->handleResults($capacityAssignments, CapacityAssignmentDomainObject::class); + } + + public function addTaxesAndFeesToProduct(int $productId, array $taxIds): void + { + Product::findOrFail($productId)?->tax_and_fees()->sync($taxIds); + } + + public function addCapacityAssignmentToProducts(int $capacityAssignmentId, array $productIds): void + { + $productIds = array_unique($productIds); + + Product::whereNotIn('id', $productIds) + ->whereHas('capacity_assignments', function ($query) use ($capacityAssignmentId) { + $query->where('capacity_assignment_id', $capacityAssignmentId); + }) + ->each(function (Product $product) use ($capacityAssignmentId) { + $product->capacity_assignments()->detach($capacityAssignmentId); + }); + + Product::whereIn('id', $productIds) + ->each(function (Product $product) use ($capacityAssignmentId) { + $product->capacity_assignments()->syncWithoutDetaching([$capacityAssignmentId]); + }); + } + + public function addCheckInListToProducts(int $checkInListId, array $productIds): void + { + $productIds = array_unique($productIds); + + Product::whereNotIn('id', $productIds) + ->whereHas('check_in_lists', function ($query) use ($checkInListId) { + $query->where('check_in_list_id', $checkInListId); + }) + ->each(function (Product $product) use ($checkInListId) { + $product->check_in_lists()->detach($checkInListId); + }); + + Product::whereIn('id', $productIds) + ->each(function (Product $product) use ($checkInListId) { + $product->check_in_lists()->syncWithoutDetaching([$checkInListId]); + }); + } + + public function removeCheckInListFromProducts(int $checkInListId): void + { + $checkInList = CheckInList::find($checkInListId); + + $checkInList?->products()->detach(); + } + + public function removeCapacityAssignmentFromProducts(int $capacityAssignmentId): void + { + $capacityAssignment = CapacityAssignment::find($capacityAssignmentId); + + $capacityAssignment?->products()->detach(); + } + + public function sortProducts(int $eventId, array $orderedProductIds): void + { + $parameters = [ + 'eventId' => $eventId, + 'productIds' => '{' . implode(',', $orderedProductIds) . '}', + 'orders' => '{' . implode(',', range(1, count($orderedProductIds))) . '}', + ]; + + $query = "WITH new_order AS ( + SELECT unnest(:productIds::bigint[]) AS product_id, + unnest(:orders::int[]) AS order + ) + UPDATE products + SET \"order\" = new_order.order + FROM new_order + WHERE products.id = new_order.product_id AND products.event_id = :eventId"; + + $this->db->update($query, $parameters); + } + + public function getModel(): string + { + return Product::class; + } + + public function getDomainObject(): string + { + return ProductDomainObject::class; + } +} diff --git a/backend/app/Repository/Eloquent/QuestionRepository.php b/backend/app/Repository/Eloquent/QuestionRepository.php index 8c2e1e9a0d..1e0dcdbb60 100644 --- a/backend/app/Repository/Eloquent/QuestionRepository.php +++ b/backend/app/Repository/Eloquent/QuestionRepository.php @@ -5,21 +5,21 @@ use HiEvents\DomainObjects\Generated\QuestionDomainObjectAbstract; use HiEvents\DomainObjects\QuestionDomainObject; use HiEvents\Models\Question; -use HiEvents\Models\TicketQuestion; +use HiEvents\Models\ProductQuestion; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use Illuminate\Database\DatabaseManager; use Illuminate\Foundation\Application; use Illuminate\Support\Collection; class QuestionRepository extends BaseRepository implements QuestionRepositoryInterface { - private TicketRepositoryInterface $ticketRepository; + private ProductRepositoryInterface $productRepository; - public function __construct(Application $application, DatabaseManager $db, TicketRepositoryInterface $ticketRepository) + public function __construct(Application $application, DatabaseManager $db, ProductRepositoryInterface $productRepository) { parent::__construct($application, $db); - $this->ticketRepository = $ticketRepository; + $this->productRepository = $productRepository; } protected function getModel(): string @@ -32,37 +32,37 @@ public function getDomainObject(): string return QuestionDomainObject::class; } - public function create(array $attributes, array $ticketIds = []): QuestionDomainObject + public function create(array $attributes, array $productIds = []): QuestionDomainObject { /** @var QuestionDomainObject $question */ $question = parent::create($attributes); - foreach ($ticketIds as $ticketId) { - $ticketQuestion = new TicketQuestion(); - $ticketQuestion->create([ - 'ticket_id' => $ticketId, + foreach ($productIds as $productId) { + $productQuestion = new ProductQuestion(); + $productQuestion->create([ + 'product_id' => $productId, 'question_id' => $question->getId(), ]); } - $question->setTickets($this->ticketRepository->findWhereIn('id', $ticketIds)); + $question->setProducts($this->productRepository->findWhereIn('id', $productIds)); return $question; } - public function updateQuestion(int $questionId, int $eventId, array $attributes, array $ticketIds = []): void + public function updateQuestion(int $questionId, int $eventId, array $attributes, array $productIds = []): void { $this->updateWhere($attributes, [ 'id' => $questionId, 'event_id' => $eventId, ]); - TicketQuestion::where('question_id', $questionId)->delete(); + ProductQuestion::where('question_id', $questionId)->delete(); - foreach ($ticketIds as $ticketId) { - $ticketQuestion = new TicketQuestion(); - $ticketQuestion->create([ - 'ticket_id' => $ticketId, + foreach ($productIds as $productId) { + $productQuestion = new ProductQuestion(); + $productQuestion->create([ + 'product_id' => $productId, 'question_id' => $questionId, ]); } diff --git a/backend/app/Repository/Eloquent/TicketPriceRepository.php b/backend/app/Repository/Eloquent/TicketPriceRepository.php deleted file mode 100644 index 9f40580138..0000000000 --- a/backend/app/Repository/Eloquent/TicketPriceRepository.php +++ /dev/null @@ -1,20 +0,0 @@ -query)) { - $where[] = static function (Builder $builder) use ($params) { - $builder - ->where(TicketDomainObjectAbstract::TITLE, 'ilike', '%' . $params->query . '%'); - }; - } - - $this->model = $this->model->orderBy( - $params->sort_by ?? TicketDomainObject::getDefaultSort(), - $params->sort_direction ?? TicketDomainObject::getDefaultSortDirection(), - ); - - return $this->paginateWhere( - where: $where, - limit: $params->per_page, - page: $params->page, - ); - } - - /** - * @param int $ticketId - * @param int $ticketPriceId - * @return int - */ - public function getQuantityRemainingForTicketPrice(int $ticketId, int $ticketPriceId): int - { - $query = <<db->selectOne($query, [ - 'ticketPriceId' => $ticketPriceId, - 'ticketId' => $ticketId - ]); - - if ($result === null) { - throw new RuntimeException('Ticket price not found'); - } - - if ($result->unlimited_tickets_available) { - return Constants::INFINITE; - } - - return (int)$result->quantity_remaining; - } - - public function getTaxesByTicketId(int $ticketId): Collection - { - $query = <<db->select($query, [ - 'ticketId' => $ticketId - ]); - - return $this->handleResults($taxAndFees, TaxAndFeesDomainObject::class); - } - - public function getTicketsByTaxId(int $taxId): Collection - { - $query = <<model->select($query, [ - 'taxAndFeeId' => $taxId - ]); - - return $this->handleResults($tickets, TicketDomainObject::class); - } - - public function getCapacityAssignmentsByTicketId(int $ticketId): Collection - { - $capacityAssignments = CapacityAssignment::whereHas('tickets', static function ($query) use ($ticketId) { - $query->where('ticket_id', $ticketId); - })->get(); - - return $this->handleResults($capacityAssignments, CapacityAssignmentDomainObject::class); - } - - public function addTaxesAndFeesToTicket(int $ticketId, array $taxIds): void - { - Ticket::findOrFail($ticketId)?->tax_and_fees()->sync($taxIds); - } - - public function addCapacityAssignmentToTickets(int $capacityAssignmentId, array $ticketIds): void - { - $ticketIds = array_unique($ticketIds); - - Ticket::whereNotIn('id', $ticketIds) - ->whereHas('capacity_assignments', function ($query) use ($capacityAssignmentId) { - $query->where('capacity_assignment_id', $capacityAssignmentId); - }) - ->each(function (Ticket $ticket) use ($capacityAssignmentId) { - $ticket->capacity_assignments()->detach($capacityAssignmentId); - }); - - Ticket::whereIn('id', $ticketIds) - ->each(function (Ticket $ticket) use ($capacityAssignmentId) { - $ticket->capacity_assignments()->syncWithoutDetaching([$capacityAssignmentId]); - }); - } - - public function addCheckInListToTickets(int $checkInListId, array $ticketIds): void - { - $ticketIds = array_unique($ticketIds); - - Ticket::whereNotIn('id', $ticketIds) - ->whereHas('check_in_lists', function ($query) use ($checkInListId) { - $query->where('check_in_list_id', $checkInListId); - }) - ->each(function (Ticket $ticket) use ($checkInListId) { - $ticket->check_in_lists()->detach($checkInListId); - }); - - Ticket::whereIn('id', $ticketIds) - ->each(function (Ticket $ticket) use ($checkInListId) { - $ticket->check_in_lists()->syncWithoutDetaching([$checkInListId]); - }); - } - - public function removeCheckInListFromTickets(int $checkInListId): void - { - $checkInList = CheckInList::find($checkInListId); - - $checkInList?->tickets()->detach(); - } - - public function removeCapacityAssignmentFromTickets(int $capacityAssignmentId): void - { - $capacityAssignment = CapacityAssignment::find($capacityAssignmentId); - - $capacityAssignment?->tickets()->detach(); - } - - public function sortTickets(int $eventId, array $orderedTicketIds): void - { - $parameters = [ - 'eventId' => $eventId, - 'ticketIds' => '{' . implode(',', $orderedTicketIds) . '}', - 'orders' => '{' . implode(',', range(1, count($orderedTicketIds))) . '}', - ]; - - $query = "WITH new_order AS ( - SELECT unnest(:ticketIds::bigint[]) AS ticket_id, - unnest(:orders::int[]) AS order - ) - UPDATE tickets - SET \"order\" = new_order.order - FROM new_order - WHERE tickets.id = new_order.ticket_id AND tickets.event_id = :eventId"; - - $this->db->update($query, $parameters); - } - - public function getModel(): string - { - return Ticket::class; - } - - public function getDomainObject(): string - { - return TicketDomainObject::class; - } -} diff --git a/backend/app/Repository/Interfaces/ProductPriceRepositoryInterface.php b/backend/app/Repository/Interfaces/ProductPriceRepositoryInterface.php new file mode 100644 index 0000000000..23b538f1e4 --- /dev/null +++ b/backend/app/Repository/Interfaces/ProductPriceRepositoryInterface.php @@ -0,0 +1,15 @@ + + */ +interface ProductPriceRepositoryInterface extends RepositoryInterface +{ +} diff --git a/backend/app/Repository/Interfaces/ProductRepositoryInterface.php b/backend/app/Repository/Interfaces/ProductRepositoryInterface.php new file mode 100644 index 0000000000..f7c3e08587 --- /dev/null +++ b/backend/app/Repository/Interfaces/ProductRepositoryInterface.php @@ -0,0 +1,89 @@ + + */ +interface ProductRepositoryInterface extends RepositoryInterface +{ + /** + * @param int $eventId + * @param QueryParamsDTO $params + * @return LengthAwarePaginator + */ + public function findByEventId(int $eventId, QueryParamsDTO $params): LengthAwarePaginator; + + /** + * @param int $productId + * @param int $productPriceId + * @return int + */ + public function getQuantityRemainingForProductPrice(int $productId, int $productPriceId): int; + + /** + * @param int $productId + * @return Collection + */ + public function getTaxesByProductId(int $productId): Collection; + + /** + * @param int $taxId + * @return Collection + */ + public function getProductsByTaxId(int $taxId): Collection; + + /** + * @param int $productId + * @return Collection + */ + public function getCapacityAssignmentsByProductId(int $productId): Collection; + + /** + * @param int $productId + * @param array $taxIds + * @return void + */ + public function addTaxesAndFeesToProduct(int $productId, array $taxIds): void; + + /** + * @param array $productIds + * @param int $capacityAssignmentId + * @return void + */ + public function addCapacityAssignmentToProducts(int $capacityAssignmentId, array $productIds): void; + + /** + * @param int $checkInListId + * @param array $productIds + * @return void + */ + public function addCheckInListToProducts(int $checkInListId, array $productIds): void; + + /** + * @param int $checkInListId + * @return void + */ + public function removeCheckInListFromProducts(int $checkInListId): void; + + /** + * @param int $capacityAssignmentId + * @return void + */ + public function removeCapacityAssignmentFromProducts(int $capacityAssignmentId): void; + + /** + * @param int $eventId + * @param array $orderedProductIds + * @return void + */ + public function sortProducts(int $eventId, array $orderedProductIds): void; +} diff --git a/backend/app/Repository/Interfaces/QuestionRepositoryInterface.php b/backend/app/Repository/Interfaces/QuestionRepositoryInterface.php index 6a5438de12..61c303e64e 100644 --- a/backend/app/Repository/Interfaces/QuestionRepositoryInterface.php +++ b/backend/app/Repository/Interfaces/QuestionRepositoryInterface.php @@ -13,9 +13,9 @@ interface QuestionRepositoryInterface extends RepositoryInterface { public function findByEventId(int $eventId): Collection; - public function create(array $attributes, array $ticketIds = []): QuestionDomainObject; + public function create(array $attributes, array $productIds = []): QuestionDomainObject; - public function updateQuestion(int $questionId, int $eventId, array $attributes, array $ticketIds = []): void; + public function updateQuestion(int $questionId, int $eventId, array $attributes, array $productIds = []): void; public function sortQuestions(int $eventId, array $orderedQuestionIds): void; } diff --git a/backend/app/Repository/Interfaces/TicketPriceRepositoryInterface.php b/backend/app/Repository/Interfaces/TicketPriceRepositoryInterface.php deleted file mode 100644 index 83df3a0bc7..0000000000 --- a/backend/app/Repository/Interfaces/TicketPriceRepositoryInterface.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -interface TicketPriceRepositoryInterface extends RepositoryInterface -{ -} diff --git a/backend/app/Repository/Interfaces/TicketRepositoryInterface.php b/backend/app/Repository/Interfaces/TicketRepositoryInterface.php deleted file mode 100644 index 4143956a51..0000000000 --- a/backend/app/Repository/Interfaces/TicketRepositoryInterface.php +++ /dev/null @@ -1,89 +0,0 @@ - - */ -interface TicketRepositoryInterface extends RepositoryInterface -{ - /** - * @param int $eventId - * @param QueryParamsDTO $params - * @return LengthAwarePaginator - */ - public function findByEventId(int $eventId, QueryParamsDTO $params): LengthAwarePaginator; - - /** - * @param int $ticketId - * @param int $ticketPriceId - * @return int - */ - public function getQuantityRemainingForTicketPrice(int $ticketId, int $ticketPriceId): int; - - /** - * @param int $ticketId - * @return Collection - */ - public function getTaxesByTicketId(int $ticketId): Collection; - - /** - * @param int $taxId - * @return Collection - */ - public function getTicketsByTaxId(int $taxId): Collection; - - /** - * @param int $ticketId - * @return Collection - */ - public function getCapacityAssignmentsByTicketId(int $ticketId): Collection; - - /** - * @param int $ticketId - * @param array $taxIds - * @return void - */ - public function addTaxesAndFeesToTicket(int $ticketId, array $taxIds): void; - - /** - * @param array $ticketIds - * @param int $capacityAssignmentId - * @return void - */ - public function addCapacityAssignmentToTickets(int $capacityAssignmentId, array $ticketIds): void; - - /** - * @param int $checkInListId - * @param array $ticketIds - * @return void - */ - public function addCheckInListToTickets(int $checkInListId, array $ticketIds): void; - - /** - * @param int $checkInListId - * @return void - */ - public function removeCheckInListFromTickets(int $checkInListId): void; - - /** - * @param int $capacityAssignmentId - * @return void - */ - public function removeCapacityAssignmentFromTickets(int $capacityAssignmentId): void; - - /** - * @param int $eventId - * @param array $orderedTicketIds - * @return void - */ - public function sortTickets(int $eventId, array $orderedTicketIds): void; -} diff --git a/backend/app/Resources/Attendee/AttendeeResource.php b/backend/app/Resources/Attendee/AttendeeResource.php index 77a7f924f3..a4b79c84a5 100644 --- a/backend/app/Resources/Attendee/AttendeeResource.php +++ b/backend/app/Resources/Attendee/AttendeeResource.php @@ -6,7 +6,7 @@ use HiEvents\DomainObjects\Enums\QuestionBelongsTo; use HiEvents\Resources\Order\OrderResource; use HiEvents\Resources\Question\QuestionAnswerViewResource; -use HiEvents\Resources\Ticket\TicketResource; +use HiEvents\Resources\Product\ProductResource; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; @@ -20,8 +20,8 @@ public function toArray(Request $request): array return [ 'id' => $this->getId(), 'order_id' => $this->getOrderId(), - 'ticket_id' => $this->getTicketId(), - 'ticket_price_id' => $this->getTicketPriceId(), + 'product_id' => $this->getProductId(), + 'product_price_id' => $this->getProductPriceId(), 'event_id' => $this->getEventId(), 'email' => $this->getEmail(), 'status' => $this->getStatus(), @@ -30,9 +30,9 @@ public function toArray(Request $request): array 'public_id' => $this->getPublicId(), 'short_id' => $this->getShortId(), 'locale' => $this->getLocale(), - 'ticket' => $this->when( - !is_null($this->getTicket()), - fn() => new TicketResource($this->getTicket()), + 'product' => $this->when( + !is_null($this->getProduct()), + fn() => new ProductResource($this->getProduct()), ), 'order' => $this->when( !is_null($this->getOrder()), @@ -42,7 +42,7 @@ public function toArray(Request $request): array $this->getQuestionAndAnswerViews() !== null, fn() => QuestionAnswerViewResource::collection( $this->getQuestionAndAnswerViews() - ?->filter(fn($qav) => $qav->getBelongsTo() === QuestionBelongsTo::TICKET->name) + ?->filter(fn($qav) => $qav->getBelongsTo() === QuestionBelongsTo::PRODUCT->name) ) ), diff --git a/backend/app/Resources/Attendee/AttendeeResourcePublic.php b/backend/app/Resources/Attendee/AttendeeResourcePublic.php index 8c40ffcd16..1f4ffc1a36 100644 --- a/backend/app/Resources/Attendee/AttendeeResourcePublic.php +++ b/backend/app/Resources/Attendee/AttendeeResourcePublic.php @@ -3,7 +3,7 @@ namespace HiEvents\Resources\Attendee; use HiEvents\DomainObjects\AttendeeDomainObject; -use HiEvents\Resources\Ticket\TicketMinimalResourcePublic; +use HiEvents\Resources\Product\ProductMinimalResourcePublic; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; @@ -22,9 +22,9 @@ public function toArray(Request $request): array 'last_name' => $this->getLastName(), 'public_id' => $this->getPublicId(), 'short_id' => $this->getShortId(), - 'ticket_id' => $this->getTicketId(), - 'ticket_price_id' => $this->getTicketPriceId(), - 'ticket' => $this->when((bool)$this->getTicket(), fn() => new TicketMinimalResourcePublic($this->getTicket())), + 'product_id' => $this->getProductId(), + 'product_price_id' => $this->getProductPriceId(), + 'product' => $this->when((bool)$this->getProduct(), fn() => new ProductMinimalResourcePublic($this->getProduct())), 'locale' => $this->getLocale(), ]; } diff --git a/backend/app/Resources/Attendee/AttendeeWithCheckInPublicResource.php b/backend/app/Resources/Attendee/AttendeeWithCheckInPublicResource.php index 9750f8c1f9..ee8b888e92 100644 --- a/backend/app/Resources/Attendee/AttendeeWithCheckInPublicResource.php +++ b/backend/app/Resources/Attendee/AttendeeWithCheckInPublicResource.php @@ -20,8 +20,8 @@ public function toArray(Request $request): array 'first_name' => $this->getFirstName(), 'last_name' => $this->getLastName(), 'public_id' => $this->getPublicId(), - 'ticket_id' => $this->getTicketId(), - 'ticket_price_id' => $this->getTicketPriceId(), + 'product_id' => $this->getProductId(), + 'product_price_id' => $this->getProductPriceId(), 'locale' => $this->getLocale(), $this->mergeWhen($this->getCheckIn() !== null, [ 'check_in' => new AttendeeCheckInPublicResource($this->getCheckIn()), diff --git a/backend/app/Resources/CapacityAssignment/CapacityAssignmentResource.php b/backend/app/Resources/CapacityAssignment/CapacityAssignmentResource.php index af6af46267..c35c6ef3b3 100644 --- a/backend/app/Resources/CapacityAssignment/CapacityAssignmentResource.php +++ b/backend/app/Resources/CapacityAssignment/CapacityAssignmentResource.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CapacityAssignmentDomainObject; use HiEvents\DomainObjects\Enums\CapacityAssignmentAppliesTo; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Resources\BaseResource; use Illuminate\Http\Request; @@ -25,11 +25,11 @@ public function toArray(Request $request): array 'status' => $this->getStatus(), 'event_id' => $this->getEventId(), $this->mergeWhen( - condition: $this->getTickets() !== null && $this->getAppliesTo() === CapacityAssignmentAppliesTo::TICKETS->name, + condition: $this->getProducts() !== null && $this->getAppliesTo() === CapacityAssignmentAppliesTo::PRODUCTS->name, value: [ - 'tickets' => $this->getTickets()?->map(fn(TicketDomainObject $ticket) => [ - 'id' => $ticket->getId(), - 'title' => $ticket->getTitle(), + 'products' => $this->getProducts()?->map(fn(ProductDomainObject $product) => [ + 'id' => $product->getId(), + 'title' => $product->getTitle(), ]), ]), ]; diff --git a/backend/app/Resources/CheckInList/CheckInListResource.php b/backend/app/Resources/CheckInList/CheckInListResource.php index 51d9690216..744f947c70 100644 --- a/backend/app/Resources/CheckInList/CheckInListResource.php +++ b/backend/app/Resources/CheckInList/CheckInListResource.php @@ -3,7 +3,7 @@ namespace HiEvents\Resources\CheckInList; use HiEvents\DomainObjects\CheckInListDomainObject; -use HiEvents\Resources\Ticket\TicketResource; +use HiEvents\Resources\Product\ProductResource; use Illuminate\Http\Resources\Json\JsonResource; /** @@ -26,8 +26,8 @@ public function toArray($request): array 'is_expired' => $this->isExpired($this->getEvent()->getTimezone()), 'is_active' => $this->isActivated($this->getEvent()->getTimezone()), ]), - $this->mergeWhen($this->getTickets() !== null, fn() => [ - 'tickets' => TicketResource::collection($this->getTickets()), + $this->mergeWhen($this->getProducts() !== null, fn() => [ + 'products' => ProductResource::collection($this->getProducts()), ]), ]; } diff --git a/backend/app/Resources/CheckInList/CheckInListResourcePublic.php b/backend/app/Resources/CheckInList/CheckInListResourcePublic.php index 0466870033..7135da87e4 100644 --- a/backend/app/Resources/CheckInList/CheckInListResourcePublic.php +++ b/backend/app/Resources/CheckInList/CheckInListResourcePublic.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\Resources\Event\EventResourcePublic; -use HiEvents\Resources\Ticket\TicketMinimalResourcePublic; +use HiEvents\Resources\Product\ProductMinimalResourcePublic; use Illuminate\Http\Resources\Json\JsonResource; /** @@ -28,8 +28,8 @@ public function toArray($request): array 'is_active' => $this->isActivated($this->getEvent()->getTimezone()), 'event' => EventResourcePublic::make($this->getEvent()), ]), - $this->mergeWhen($this->getTickets() !== null, fn() => [ - 'tickets' => TicketMinimalResourcePublic::collection($this->getTickets()), + $this->mergeWhen($this->getProducts() !== null, fn() => [ + 'products' => ProductMinimalResourcePublic::collection($this->getProducts()), ]), ]; } diff --git a/backend/app/Resources/Event/EventResource.php b/backend/app/Resources/Event/EventResource.php index 7fcb425fd9..ef581326f7 100644 --- a/backend/app/Resources/Event/EventResource.php +++ b/backend/app/Resources/Event/EventResource.php @@ -6,7 +6,7 @@ use HiEvents\Resources\BaseResource; use HiEvents\Resources\Image\ImageResource; use HiEvents\Resources\Organizer\OrganizerResource; -use HiEvents\Resources\Ticket\TicketResource; +use HiEvents\Resources\Product\ProductResource; use Illuminate\Http\Request; /** @@ -27,7 +27,7 @@ public function toArray(Request $request): array 'currency' => $this->getCurrency(), 'timezone' => $this->getTimezone(), 'slug' => $this->getSlug(), - 'tickets' => $this->when((bool)$this->getTickets(), fn() => TicketResource::collection($this->getTickets())), + 'products' => $this->when((bool)$this->getProducts(), fn() => ProductResource::collection($this->getProducts())), 'attributes' => $this->when((bool)$this->getAttributes(), fn() => $this->getAttributes()), 'images' => $this->when((bool)$this->getImages(), fn() => ImageResource::collection($this->getImages())), 'location_details' => $this->when((bool)$this->getLocationDetails(), fn() => $this->getLocationDetails()), diff --git a/backend/app/Resources/Event/EventResourcePublic.php b/backend/app/Resources/Event/EventResourcePublic.php index d70dc0a103..aeeb4bdfe9 100644 --- a/backend/app/Resources/Event/EventResourcePublic.php +++ b/backend/app/Resources/Event/EventResourcePublic.php @@ -7,7 +7,7 @@ use HiEvents\Resources\Image\ImageResource; use HiEvents\Resources\Organizer\OrganizerResourcePublic; use HiEvents\Resources\Question\QuestionResource; -use HiEvents\Resources\Ticket\TicketResourcePublic; +use HiEvents\Resources\Product\ProductResourcePublic; use Illuminate\Http\Request; /** @@ -39,9 +39,9 @@ public function toArray(Request $request): array 'timezone' => $this->getTimezone(), 'location_details' => $this->when((bool)$this->getLocationDetails(), fn() => $this->getLocationDetails()), - 'tickets' => $this->when( - !is_null($this->getTickets()), - fn() => TicketResourcePublic::collection($this->getTickets()) + 'products' => $this->when( + !is_null($this->getProducts()), + fn() => ProductResourcePublic::collection($this->getProducts()) ), 'settings' => $this->when( !is_null($this->getEventSettings()), diff --git a/backend/app/Resources/Event/EventSettingsResource.php b/backend/app/Resources/Event/EventSettingsResource.php index 1886a27a22..b1a62b97c3 100644 --- a/backend/app/Resources/Event/EventSettingsResource.php +++ b/backend/app/Resources/Event/EventSettingsResource.php @@ -15,7 +15,7 @@ public function toArray($request): array return [ 'pre_checkout_message' => $this->getPreCheckoutMessage(), 'post_checkout_message' => $this->getPostCheckoutMessage(), - 'ticket_page_message' => $this->getTicketPageMessage(), + 'product_page_message' => $this->getProductPageMessage(), 'continue_button_text' => $this->getContinueButtonText(), 'required_attendee_details' => $this->getRequireAttendeeDetails(), 'email_footer_message' => $this->getEmailFooterMessage(), diff --git a/backend/app/Resources/Event/EventSettingsResourcePublic.php b/backend/app/Resources/Event/EventSettingsResourcePublic.php index 397a1d2a29..26bfca3fa8 100644 --- a/backend/app/Resources/Event/EventSettingsResourcePublic.php +++ b/backend/app/Resources/Event/EventSettingsResourcePublic.php @@ -28,7 +28,7 @@ public function toArray($request): array 'online_event_connection_details' => $this->getOnlineEventConnectionDetails(), ]), - 'ticket_page_message' => $this->getTicketPageMessage(), + 'product_page_message' => $this->getProductPageMessage(), 'continue_button_text' => $this->getContinueButtonText(), 'required_attendee_details' => $this->getRequireAttendeeDetails(), 'email_footer_message' => $this->getEmailFooterMessage(), diff --git a/backend/app/Resources/Event/EventStatisticsResource.php b/backend/app/Resources/Event/EventStatisticsResource.php index 91bd7d374f..2305583465 100644 --- a/backend/app/Resources/Event/EventStatisticsResource.php +++ b/backend/app/Resources/Event/EventStatisticsResource.php @@ -20,7 +20,7 @@ public function toArray(Request $request): array 'total_tax' => $this->getTotalTax(), 'sales_total_before_additions' => $this->getSalesTotalBeforeAdditions(), 'total_fee' => $this->getTotalFee(), - 'tickets_sold' => $this->getTicketsSold(), + 'products_sold' => $this->getProductsSold(), 'total_refunded' => $this->getTotalRefunded(), ]; } diff --git a/backend/app/Resources/Message/MessageResource.php b/backend/app/Resources/Message/MessageResource.php index 75dc30d676..b4b5ce81ef 100644 --- a/backend/app/Resources/Message/MessageResource.php +++ b/backend/app/Resources/Message/MessageResource.php @@ -22,7 +22,7 @@ public function toArray(Request $request): array 'type' => $this->getType(), 'attendee_ids' => $this->getAttendeeIds(), 'order_id' => $this->getOrderId(), - 'ticket_ids' => $this->getTicketIds(), + 'product_ids' => $this->getProductIds(), 'sent_at' => $this->getCreatedAt(), 'status' => $this->getStatus(), 'message_preview' => $this->getMessagePreview(), diff --git a/backend/app/Resources/Order/OrderItemResource.php b/backend/app/Resources/Order/OrderItemResource.php index 18e4155943..3199ca498b 100644 --- a/backend/app/Resources/Order/OrderItemResource.php +++ b/backend/app/Resources/Order/OrderItemResource.php @@ -19,7 +19,7 @@ public function toArray(Request $request): array 'total_before_additions' => $this->getTotalBeforeAdditions(), 'price' => $this->getPrice(), 'quantity' => $this->getQuantity(), - 'ticket_id' => $this->getTicketId(), + 'product_id' => $this->getProductId(), 'item_name' => $this->getItemName(), 'price_before_discount' => $this->getPriceBeforeDiscount(), 'taxes_and_fees_rollup' => $this->getTaxesAndFeesRollup(), diff --git a/backend/app/Resources/Order/OrderItemResourcePublic.php b/backend/app/Resources/Order/OrderItemResourcePublic.php index 471f762f05..18ff026120 100644 --- a/backend/app/Resources/Order/OrderItemResourcePublic.php +++ b/backend/app/Resources/Order/OrderItemResourcePublic.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\OrderItemDomainObject; use HiEvents\Resources\BaseResource; -use HiEvents\Resources\Ticket\TicketResourcePublic; +use HiEvents\Resources\Product\ProductResourcePublic; use Illuminate\Http\Request; /** @@ -22,14 +22,14 @@ public function toArray(Request $request): array 'price' => $this->getPrice(), 'price_before_discount' => $this->getPriceBeforeDiscount(), 'quantity' => $this->getQuantity(), - 'ticket_id' => $this->getTicketId(), - 'ticket_price_id' => $this->getTicketPriceId(), + 'product_id' => $this->getProductId(), + 'product_price_id' => $this->getProductPriceId(), 'item_name' => $this->getItemName(), 'total_service_fee' => $this->getTotalServiceFee(), 'total_tax' => $this->getTotalTax(), 'total_gross' => $this->getTotalGross(), 'taxes_and_fees_rollup' => $this->getTaxesAndFeesRollup(), - 'ticket' => $this->when((bool)$this->getTicket(), fn() => new TicketResourcePublic($this->getTicket())), + 'product' => $this->when((bool)$this->getProduct(), fn() => new ProductResourcePublic($this->getProduct())), ]; } } diff --git a/backend/app/Resources/Ticket/TicketMinimalResourcePublic.php b/backend/app/Resources/Product/ProductMinimalResourcePublic.php similarity index 56% rename from backend/app/Resources/Ticket/TicketMinimalResourcePublic.php rename to backend/app/Resources/Product/ProductMinimalResourcePublic.php index 10979bbd8b..c71e7918f5 100644 --- a/backend/app/Resources/Ticket/TicketMinimalResourcePublic.php +++ b/backend/app/Resources/Product/ProductMinimalResourcePublic.php @@ -1,15 +1,15 @@ $this->getType(), 'event_id' => $this->getEventId(), 'prices' => $this->when( - (bool)$this->getTicketPrices(), - fn() => TicketPriceResourcePublic::collection($this->getTicketPrices()), + (bool)$this->getProductPrices(), + fn() => ProductPriceResourcePublic::collection($this->getProductPrices()), ), ]; } diff --git a/backend/app/Resources/Ticket/TicketPriceResource.php b/backend/app/Resources/Product/ProductPriceResource.php similarity index 85% rename from backend/app/Resources/Ticket/TicketPriceResource.php rename to backend/app/Resources/Product/ProductPriceResource.php index 7a4844e394..3cbd945dc5 100644 --- a/backend/app/Resources/Ticket/TicketPriceResource.php +++ b/backend/app/Resources/Product/ProductPriceResource.php @@ -1,15 +1,15 @@ $this->getOrder(), 'description' => $this->getDescription(), 'price' => $this->when( - $this->getType() !== TicketType::TIERED->name, + $this->getType() !== ProductPriceType::TIERED->name, fn() => $this->getPrice() ), - 'max_per_order' => $this->getMaxPerOrder() ?? self::DEFAULT_MAX_TICKETS, - 'min_per_order' => $this->getMinPerOrder() ?? self::DEFAULT_MIN_TICKETS, + 'max_per_order' => $this->getMaxPerOrder() ?? self::DEFAULT_MAX_PRODUCTS, + 'min_per_order' => $this->getMinPerOrder() ?? self::DEFAULT_MIN_PRODUCTS, 'quantity_sold' => $this->getQuantitySold(), 'sale_start_date' => $this->getSaleStartDate(), 'sale_end_date' => $this->getSaleEndDate(), @@ -45,7 +45,7 @@ public function toArray(Request $request): array 'is_before_sale_start_date' => $this->isBeforeSaleStartDate(), 'is_after_sale_end_date' => $this->isAfterSaleEndDate(), 'is_available' => $this->isAvailable(), - $this->mergeWhen((bool)$this->getTicketPrices(), fn() => [ + $this->mergeWhen((bool)$this->getProductPrices(), fn() => [ 'is_sold_out' => $this->isSoldOut(), ]), 'taxes_and_fees' => $this->when( @@ -53,8 +53,8 @@ public function toArray(Request $request): array fn() => TaxAndFeeResource::collection($this->getTaxAndFees()) ), 'prices' => $this->when( - (bool)$this->getTicketPrices(), - fn() => TicketPriceResource::collection($this->getTicketPrices()) + (bool)$this->getProductPrices(), + fn() => ProductPriceResource::collection($this->getProductPrices()) ), ]; } diff --git a/backend/app/Resources/Ticket/TicketResourcePublic.php b/backend/app/Resources/Product/ProductResourcePublic.php similarity index 71% rename from backend/app/Resources/Ticket/TicketResourcePublic.php rename to backend/app/Resources/Product/ProductResourcePublic.php index 2bc93cbace..9239fa4da6 100644 --- a/backend/app/Resources/Ticket/TicketResourcePublic.php +++ b/backend/app/Resources/Product/ProductResourcePublic.php @@ -1,16 +1,16 @@ $this->getQuantityAvailable(), ]), 'price' => $this->when( - $this->getTicketPrices() && !$this->isTieredType(), + $this->getProductPrices() && !$this->isTieredType(), fn() => $this->getPrice(), ), 'prices' => $this->when( - (bool)$this->getTicketPrices(), - fn() => TicketPriceResourcePublic::collectionWithAdditionalData($this->getTicketPrices(), [ - TicketPriceResourcePublic::SHOW_QUANTITY_AVAILABLE => $this->getShowQuantityRemaining(), + (bool)$this->getProductPrices(), + fn() => ProductPriceResourcePublic::collectionWithAdditionalData($this->getProductPrices(), [ + ProductPriceResourcePublic::SHOW_QUANTITY_AVAILABLE => $this->getShowQuantityRemaining(), ]), ), 'taxes' => $this->when( (bool)$this->getTaxAndFees(), fn() => TaxAndFeeResource::collection($this->getTaxAndFees()) ), - $this->mergeWhen((bool)$this->getTicketPrices(), fn() => [ + $this->mergeWhen((bool)$this->getProductPrices(), fn() => [ 'is_available' => $this->isAvailable(), 'is_sold_out' => $this->isSoldOut(), ]), diff --git a/backend/app/Resources/PromoCode/PromoCodeResource.php b/backend/app/Resources/PromoCode/PromoCodeResource.php index 0aa9d80a54..ec713886ad 100644 --- a/backend/app/Resources/PromoCode/PromoCodeResource.php +++ b/backend/app/Resources/PromoCode/PromoCodeResource.php @@ -16,7 +16,7 @@ public function toArray(Request $request): array return [ 'id' => $this->getId(), 'code' => $this->getCode(), - 'applicable_ticket_ids' => $this->getApplicableTicketIds(), + 'applicable_product_ids' => $this->getApplicableProductIds(), 'discount' => $this->getDiscount(), 'discount_type' => $this->getDiscountType(), 'created_at' => $this->getCreatedAt(), diff --git a/backend/app/Resources/Question/QuestionResource.php b/backend/app/Resources/Question/QuestionResource.php index 7f0591dde2..96bdf29651 100644 --- a/backend/app/Resources/Question/QuestionResource.php +++ b/backend/app/Resources/Question/QuestionResource.php @@ -23,9 +23,9 @@ public function toArray(Request $request): array 'event_id' => $this->getEventId(), 'belongs_to' => $this->getBelongsTo(), 'is_hidden' => $this->getIsHidden(), - 'ticket_ids' => $this->when( - !is_null($this->getTickets()), - fn() => $this->getTickets()->map(fn($ticket) => $ticket->getId()) + 'product_ids' => $this->when( + !is_null($this->getProducts()), + fn() => $this->getProducts()->map(fn($product) => $product->getId()) ), ]; } diff --git a/backend/app/Resources/Question/QuestionResourcePublic.php b/backend/app/Resources/Question/QuestionResourcePublic.php index 5820cb8ba8..d4e1288de4 100644 --- a/backend/app/Resources/Question/QuestionResourcePublic.php +++ b/backend/app/Resources/Question/QuestionResourcePublic.php @@ -22,9 +22,9 @@ public function toArray(Request $request): array 'required' => $this->getRequired(), 'event_id' => $this->getEventId(), 'belongs_to' => $this->getBelongsTo(), - 'ticket_ids' => $this->when( - !is_null($this->getTickets()), - fn() => $this->getTickets()->map(fn($ticket) => $ticket->getId()) + 'product_ids' => $this->when( + !is_null($this->getProducts()), + fn() => $this->getProducts()->map(fn($product) => $product->getId()) ), ]; } diff --git a/backend/app/Services/Domain/Attendee/SendAttendeeTicketService.php b/backend/app/Services/Domain/Attendee/SendAttendeeTicketService.php index 27da9f08ae..49d89bac54 100644 --- a/backend/app/Services/Domain/Attendee/SendAttendeeTicketService.php +++ b/backend/app/Services/Domain/Attendee/SendAttendeeTicketService.php @@ -6,10 +6,10 @@ use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\EventSettingDomainObject; use HiEvents\DomainObjects\OrganizerDomainObject; -use HiEvents\Mail\Attendee\AttendeeTicketMail; +use HiEvents\Mail\Attendee\AttendeeProductMail; use Illuminate\Contracts\Mail\Mailer; -readonly class SendAttendeeTicketService +readonly class SendAttendeeProductService { public function __construct( private Mailer $mailer @@ -27,7 +27,7 @@ public function send( $this->mailer ->to($attendee->getEmail()) ->locale($attendee->getLocale()) - ->send(new AttendeeTicketMail( + ->send(new AttendeeProductMail( attendee: $attendee, event: $event, eventSettings: $eventSettings, diff --git a/backend/app/Services/Domain/CapacityAssignment/CapacityAssignmentTicketAssociationService.php b/backend/app/Services/Domain/CapacityAssignment/CapacityAssignmentTicketAssociationService.php index c5a0f5a2f8..e7f1b68de7 100644 --- a/backend/app/Services/Domain/CapacityAssignment/CapacityAssignmentTicketAssociationService.php +++ b/backend/app/Services/Domain/CapacityAssignment/CapacityAssignmentTicketAssociationService.php @@ -2,52 +2,52 @@ namespace HiEvents\Services\Domain\CapacityAssignment; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use Illuminate\Database\DatabaseManager; -class CapacityAssignmentTicketAssociationService +class CapacityAssignmentProductAssociationService { public function __construct( - private readonly TicketRepositoryInterface $ticketRepository, - public readonly DatabaseManager $databaseManager, + private readonly ProductRepositoryInterface $productRepository, + public readonly DatabaseManager $databaseManager, ) { } - public function addCapacityToTickets( + public function addCapacityToProducts( int $capacityAssignmentId, - ?array $ticketIds, + ?array $productIds, bool $removePreviousAssignments = true ): void { - $this->databaseManager->transaction(function () use ($capacityAssignmentId, $ticketIds, $removePreviousAssignments) { - $this->associateTicketsWithCapacityAssignment( + $this->databaseManager->transaction(function () use ($capacityAssignmentId, $productIds, $removePreviousAssignments) { + $this->associateProductsWithCapacityAssignment( capacityAssignmentId: $capacityAssignmentId, - ticketIds: $ticketIds, + productIds: $productIds, removePreviousAssignments: $removePreviousAssignments, ); }); } - private function associateTicketsWithCapacityAssignment( + private function associateProductsWithCapacityAssignment( int $capacityAssignmentId, - ?array $ticketIds, + ?array $productIds, bool $removePreviousAssignments = true ): void { - if (empty($ticketIds)) { + if (empty($productIds)) { return; } if ($removePreviousAssignments) { - $this->ticketRepository->removeCapacityAssignmentFromTickets( + $this->productRepository->removeCapacityAssignmentFromProducts( capacityAssignmentId: $capacityAssignmentId, ); } - $this->ticketRepository->addCapacityAssignmentToTickets( + $this->productRepository->addCapacityAssignmentToProducts( capacityAssignmentId: $capacityAssignmentId, - ticketIds: array_unique($ticketIds), + productIds: array_unique($productIds), ); } } diff --git a/backend/app/Services/Domain/CapacityAssignment/CreateCapacityAssignmentService.php b/backend/app/Services/Domain/CapacityAssignment/CreateCapacityAssignmentService.php index b5bb21a88f..4c7e68c6e6 100644 --- a/backend/app/Services/Domain/CapacityAssignment/CreateCapacityAssignmentService.php +++ b/backend/app/Services/Domain/CapacityAssignment/CreateCapacityAssignmentService.php @@ -5,11 +5,11 @@ use HiEvents\DomainObjects\CapacityAssignmentDomainObject; use HiEvents\DomainObjects\Enums\CapacityAssignmentAppliesTo; use HiEvents\DomainObjects\Generated\CapacityAssignmentDomainObjectAbstract; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Repository\Interfaces\CapacityAssignmentRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketPriceRepositoryInterface; -use HiEvents\Services\Domain\Ticket\EventTicketValidationService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Repository\Interfaces\ProductPriceRepositoryInterface; +use HiEvents\Services\Domain\Product\EventProductValidationService; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use Illuminate\Database\DatabaseManager; class CreateCapacityAssignmentService @@ -17,32 +17,32 @@ class CreateCapacityAssignmentService public function __construct( private readonly DatabaseManager $databaseManager, private readonly CapacityAssignmentRepositoryInterface $capacityAssignmentRepository, - private readonly EventTicketValidationService $eventTicketValidationService, - private readonly CapacityAssignmentTicketAssociationService $capacityAssignmentTicketAssociationService, - private readonly TicketPriceRepositoryInterface $ticketPriceRepository, + private readonly EventProductValidationService $eventProductValidationService, + private readonly CapacityAssignmentProductAssociationService $capacityAssignmentProductAssociationService, + private readonly ProductPriceRepositoryInterface $productPriceRepository, ) { } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function createCapacityAssignment( CapacityAssignmentDomainObject $capacityAssignment, - array $ticketIds, + array $productIds, ): CapacityAssignmentDomainObject { - $this->eventTicketValidationService->validateTicketIds($ticketIds, $capacityAssignment->getEventId()); + $this->eventProductValidationService->validateProductIds($productIds, $capacityAssignment->getEventId()); - return $this->persistAssignmentAndAssociateTickets($capacityAssignment, $ticketIds); + return $this->persistAssignmentAndAssociateProducts($capacityAssignment, $productIds); } - private function persistAssignmentAndAssociateTickets( + private function persistAssignmentAndAssociateProducts( CapacityAssignmentDomainObject $capacityAssignment, - ?array $ticketIds, + ?array $productIds, ): CapacityAssignmentDomainObject { - return $this->databaseManager->transaction(function () use ($capacityAssignment, $ticketIds) { + return $this->databaseManager->transaction(function () use ($capacityAssignment, $productIds) { /** @var CapacityAssignmentDomainObject $capacityAssignment */ $capacityAssignment = $this->capacityAssignmentRepository->create([ CapacityAssignmentDomainObjectAbstract::NAME => $capacityAssignment->getName(), @@ -50,13 +50,13 @@ private function persistAssignmentAndAssociateTickets( CapacityAssignmentDomainObjectAbstract::CAPACITY => $capacityAssignment->getCapacity(), CapacityAssignmentDomainObjectAbstract::APPLIES_TO => $capacityAssignment->getAppliesTo(), CapacityAssignmentDomainObjectAbstract::STATUS => $capacityAssignment->getStatus(), - CapacityAssignmentDomainObjectAbstract::USED_CAPACITY => $this->getUsedCapacity($ticketIds), + CapacityAssignmentDomainObjectAbstract::USED_CAPACITY => $this->getUsedCapacity($productIds), ]); - if ($capacityAssignment->getAppliesTo() === CapacityAssignmentAppliesTo::TICKETS->name) { - $this->capacityAssignmentTicketAssociationService->addCapacityToTickets( + if ($capacityAssignment->getAppliesTo() === CapacityAssignmentAppliesTo::PRODUCTS->name) { + $this->capacityAssignmentProductAssociationService->addCapacityToProducts( capacityAssignmentId: $capacityAssignment->getId(), - ticketIds: $ticketIds, + productIds: $productIds, removePreviousAssignments: false, ); } @@ -65,10 +65,10 @@ private function persistAssignmentAndAssociateTickets( }); } - private function getUsedCapacity(array $ticketIds): int + private function getUsedCapacity(array $productIds): int { - $ticketPrices = $this->ticketPriceRepository->findWhereIn('ticket_id', $ticketIds); + $productPrices = $this->productPriceRepository->findWhereIn('product_id', $productIds); - return $ticketPrices->sum(fn(TicketPriceDomainObject $ticketPrice) => $ticketPrice->getQuantitySold()); + return $productPrices->sum(fn(ProductPriceDomainObject $productPrice) => $productPrice->getQuantitySold()); } } diff --git a/backend/app/Services/Domain/CapacityAssignment/Exception/TicketsDoNotBelongToEventException.php b/backend/app/Services/Domain/CapacityAssignment/Exception/TicketsDoNotBelongToEventException.php index 8e2cbefb8a..1505af45b5 100644 --- a/backend/app/Services/Domain/CapacityAssignment/Exception/TicketsDoNotBelongToEventException.php +++ b/backend/app/Services/Domain/CapacityAssignment/Exception/TicketsDoNotBelongToEventException.php @@ -4,7 +4,7 @@ use Exception; -class TicketsDoNotBelongToEventException extends Exception +class ProductsDoNotBelongToEventException extends Exception { } diff --git a/backend/app/Services/Domain/CapacityAssignment/UpdateCapacityAssignmentService.php b/backend/app/Services/Domain/CapacityAssignment/UpdateCapacityAssignmentService.php index a0d0899a27..b355cd00ac 100644 --- a/backend/app/Services/Domain/CapacityAssignment/UpdateCapacityAssignmentService.php +++ b/backend/app/Services/Domain/CapacityAssignment/UpdateCapacityAssignmentService.php @@ -6,8 +6,8 @@ use HiEvents\DomainObjects\Enums\CapacityAssignmentAppliesTo; use HiEvents\DomainObjects\Generated\CapacityAssignmentDomainObjectAbstract; use HiEvents\Repository\Interfaces\CapacityAssignmentRepositoryInterface; -use HiEvents\Services\Domain\Ticket\EventTicketValidationService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\EventProductValidationService; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use Illuminate\Database\DatabaseManager; class UpdateCapacityAssignmentService @@ -15,33 +15,33 @@ class UpdateCapacityAssignmentService public function __construct( private readonly DatabaseManager $databaseManager, private readonly CapacityAssignmentRepositoryInterface $capacityAssignmentRepository, - private readonly EventTicketValidationService $eventTicketValidationService, - private readonly CapacityAssignmentTicketAssociationService $capacityAssignmentTicketAssociationService, + private readonly EventProductValidationService $eventProductValidationService, + private readonly CapacityAssignmentProductAssociationService $capacityAssignmentProductAssociationService, ) { } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function updateCapacityAssignment( CapacityAssignmentDomainObject $capacityAssignment, - ?array $ticketIds = null, + ?array $productIds = null, ): CapacityAssignmentDomainObject { - if ($ticketIds !== null) { - $this->eventTicketValidationService->validateTicketIds($ticketIds, $capacityAssignment->getEventId()); + if ($productIds !== null) { + $this->eventProductValidationService->validateProductIds($productIds, $capacityAssignment->getEventId()); } - return $this->updateAssignmentAndAssociateTickets($capacityAssignment, $ticketIds); + return $this->updateAssignmentAndAssociateProducts($capacityAssignment, $productIds); } - private function updateAssignmentAndAssociateTickets( + private function updateAssignmentAndAssociateProducts( CapacityAssignmentDomainObject $capacityAssignment, - ?array $ticketIds + ?array $productIds ): CapacityAssignmentDomainObject { - return $this->databaseManager->transaction(function () use ($capacityAssignment, $ticketIds) { + return $this->databaseManager->transaction(function () use ($capacityAssignment, $productIds) { /** @var CapacityAssignmentDomainObject $capacityAssignment */ $this->capacityAssignmentRepository->updateWhere( attributes: [ @@ -57,10 +57,10 @@ private function updateAssignmentAndAssociateTickets( ] ); - if ($capacityAssignment->getAppliesTo() === CapacityAssignmentAppliesTo::TICKETS->name) { - $this->capacityAssignmentTicketAssociationService->addCapacityToTickets( + if ($capacityAssignment->getAppliesTo() === CapacityAssignmentAppliesTo::PRODUCTS->name) { + $this->capacityAssignmentProductAssociationService->addCapacityToProducts( capacityAssignmentId: $capacityAssignment->getId(), - ticketIds: $ticketIds, + productIds: $productIds, ); } diff --git a/backend/app/Services/Domain/CheckInList/CheckInListDataService.php b/backend/app/Services/Domain/CheckInList/CheckInListDataService.php index 3795ad043a..f80a8c3c7b 100644 --- a/backend/app/Services/Domain/CheckInList/CheckInListDataService.php +++ b/backend/app/Services/Domain/CheckInList/CheckInListDataService.php @@ -7,7 +7,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract; use HiEvents\DomainObjects\Generated\CheckInListDomainObjectAbstract; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Exceptions\CannotCheckInException; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\CheckInListRepositoryInterface; @@ -30,9 +30,9 @@ public function verifyAttendeeBelongsToCheckInList( AttendeeDomainObject $attendee, ): void { - $allowedTicketIds = $checkInList->getTickets()->map(fn($ticket) => $ticket->getId())->toArray() ?? []; + $allowedProductIds = $checkInList->getProducts()->map(fn($product) => $product->getId())->toArray() ?? []; - if (!in_array($attendee->getTicketId(), $allowedTicketIds, true)) { + if (!in_array($attendee->getProductId(), $allowedProductIds, true)) { throw new CannotCheckInException( __('Attendee :attendee_name is not allowed to check in using this check-in list', [ 'attendee_name' => $attendee->getFullName(), @@ -74,7 +74,7 @@ public function getAttendees(array $attendeePublicIds): Collection public function getCheckInList(string $checkInListUuid): CheckInListDomainObject { $checkInList = $this->checkInListRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findFirstWhere([ CheckInListDomainObjectAbstract::SHORT_ID => $checkInListUuid, ]); diff --git a/backend/app/Services/Domain/CheckInList/CheckInListTicketAssociationService.php b/backend/app/Services/Domain/CheckInList/CheckInListTicketAssociationService.php index 24c96c4e16..2153c44620 100644 --- a/backend/app/Services/Domain/CheckInList/CheckInListTicketAssociationService.php +++ b/backend/app/Services/Domain/CheckInList/CheckInListTicketAssociationService.php @@ -2,52 +2,52 @@ namespace HiEvents\Services\Domain\CheckInList; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use Illuminate\Database\DatabaseManager; -class CheckInListTicketAssociationService +class CheckInListProductAssociationService { public function __construct( - private readonly TicketRepositoryInterface $ticketRepository, - public readonly DatabaseManager $databaseManager, + private readonly ProductRepositoryInterface $productRepository, + public readonly DatabaseManager $databaseManager, ) { } - public function addCheckInListToTickets( + public function addCheckInListToProducts( int $checkInListId, - ?array $ticketIds, + ?array $productIds, bool $removePreviousAssignments = true ): void { - $this->databaseManager->transaction(function () use ($checkInListId, $ticketIds, $removePreviousAssignments) { - $this->associateTicketsWithCheckInList( + $this->databaseManager->transaction(function () use ($checkInListId, $productIds, $removePreviousAssignments) { + $this->associateProductsWithCheckInList( checkInListId: $checkInListId, - ticketIds: $ticketIds, + productIds: $productIds, removePreviousAssignments: $removePreviousAssignments, ); }); } - private function associateTicketsWithCheckInList( + private function associateProductsWithCheckInList( int $checkInListId, - ?array $ticketIds, + ?array $productIds, bool $removePreviousAssignments = true ): void { - if (empty($ticketIds)) { + if (empty($productIds)) { return; } if ($removePreviousAssignments) { - $this->ticketRepository->removeCheckInListFromTickets( + $this->productRepository->removeCheckInListFromProducts( checkInListId: $checkInListId, ); } - $this->ticketRepository->addCheckInListToTickets( + $this->productRepository->addCheckInListToProducts( checkInListId: $checkInListId, - ticketIds: array_unique($ticketIds), + productIds: array_unique($productIds), ); } } diff --git a/backend/app/Services/Domain/CheckInList/CreateAttendeeCheckInService.php b/backend/app/Services/Domain/CheckInList/CreateAttendeeCheckInService.php index 737619b5ba..127ab15767 100644 --- a/backend/app/Services/Domain/CheckInList/CreateAttendeeCheckInService.php +++ b/backend/app/Services/Domain/CheckInList/CreateAttendeeCheckInService.php @@ -64,7 +64,7 @@ public function checkInAttendees( if ($attendee->getStatus() === AttendeeStatus::CANCELLED->name) { $errors->addError( key: $attendee->getPublicId(), - message: __('Attendee :attendee_name\'s ticket is cancelled', [ + message: __('Attendee :attendee_name\'s product is cancelled', [ 'attendee_name' => $attendee->getFullName(), ]) ); @@ -87,7 +87,7 @@ public function checkInAttendees( AttendeeCheckInDomainObjectAbstract::ATTENDEE_ID => $attendee->getId(), AttendeeCheckInDomainObjectAbstract::CHECK_IN_LIST_ID => $checkInList->getId(), AttendeeCheckInDomainObjectAbstract::IP_ADDRESS => $checkInUserIpAddress, - AttendeeCheckInDomainObjectAbstract::TICKET_ID => $attendee->getTicketId(), + AttendeeCheckInDomainObjectAbstract::PRODUCT_ID => $attendee->getProductId(), AttendeeCheckInDomainObjectAbstract::SHORT_ID => IdHelper::shortId(IdHelper::CHECK_IN_PREFIX), AttendeeCheckInDomainObjectAbstract::EVENT_ID => $checkInList->getEventId(), ]) diff --git a/backend/app/Services/Domain/CheckInList/CreateCheckInListService.php b/backend/app/Services/Domain/CheckInList/CreateCheckInListService.php index 93aa2938de..a4e95f929c 100644 --- a/backend/app/Services/Domain/CheckInList/CreateCheckInListService.php +++ b/backend/app/Services/Domain/CheckInList/CreateCheckInListService.php @@ -8,16 +8,16 @@ use HiEvents\Helper\IdHelper; use HiEvents\Repository\Interfaces\CheckInListRepositoryInterface; use HiEvents\Repository\Interfaces\EventRepositoryInterface; -use HiEvents\Services\Domain\Ticket\EventTicketValidationService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\EventProductValidationService; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use Illuminate\Database\DatabaseManager; class CreateCheckInListService { public function __construct( private readonly CheckInListRepositoryInterface $checkInListRepository, - private readonly EventTicketValidationService $eventTicketValidationService, - private readonly CheckInListTicketAssociationService $checkInListTicketAssociationService, + private readonly EventProductValidationService $eventProductValidationService, + private readonly CheckInListProductAssociationService $checkInListProductAssociationService, private readonly DatabaseManager $databaseManager, private readonly EventRepositoryInterface $eventRepository, @@ -26,12 +26,12 @@ public function __construct( } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ - public function createCheckInList(CheckInListDomainObject $checkInList, array $ticketIds): CheckInListDomainObject + public function createCheckInList(CheckInListDomainObject $checkInList, array $productIds): CheckInListDomainObject { - return $this->databaseManager->transaction(function () use ($checkInList, $ticketIds) { - $this->eventTicketValidationService->validateTicketIds($ticketIds, $checkInList->getEventId()); + return $this->databaseManager->transaction(function () use ($checkInList, $productIds) { + $this->eventProductValidationService->validateProductIds($productIds, $checkInList->getEventId()); $event = $this->eventRepository->findById($checkInList->getEventId()); $newCheckInList = $this->checkInListRepository->create([ @@ -47,9 +47,9 @@ public function createCheckInList(CheckInListDomainObject $checkInList, array $t CheckInListDomainObjectAbstract::SHORT_ID => IdHelper::shortId(IdHelper::CHECK_IN_LIST_PREFIX), ]); - $this->checkInListTicketAssociationService->addCheckInListToTickets( + $this->checkInListProductAssociationService->addCheckInListToProducts( checkInListId: $newCheckInList->getId(), - ticketIds: $ticketIds, + productIds: $productIds, removePreviousAssignments: false, ); diff --git a/backend/app/Services/Domain/CheckInList/UpdateCheckInListService.php b/backend/app/Services/Domain/CheckInList/UpdateCheckInListService.php index b26fd8b6c1..11a441deaf 100644 --- a/backend/app/Services/Domain/CheckInList/UpdateCheckInListService.php +++ b/backend/app/Services/Domain/CheckInList/UpdateCheckInListService.php @@ -7,16 +7,16 @@ use HiEvents\Helper\DateHelper; use HiEvents\Repository\Interfaces\CheckInListRepositoryInterface; use HiEvents\Repository\Interfaces\EventRepositoryInterface; -use HiEvents\Services\Domain\Ticket\EventTicketValidationService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\EventProductValidationService; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use Illuminate\Database\DatabaseManager; class UpdateCheckInListService { public function __construct( private readonly DatabaseManager $databaseManager, - private readonly EventTicketValidationService $eventTicketValidationService, - private readonly CheckInListTicketAssociationService $checkInListTicketAssociationService, + private readonly EventProductValidationService $eventProductValidationService, + private readonly CheckInListProductAssociationService $checkInListProductAssociationService, private readonly CheckInListRepositoryInterface $checkInListRepository, private readonly EventRepositoryInterface $eventRepository, ) @@ -24,12 +24,12 @@ public function __construct( } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ - public function updateCheckInList(CheckInListDomainObject $checkInList, array $ticketIds): CheckInListDomainObject + public function updateCheckInList(CheckInListDomainObject $checkInList, array $productIds): CheckInListDomainObject { - return $this->databaseManager->transaction(function () use ($checkInList, $ticketIds) { - $this->eventTicketValidationService->validateTicketIds($ticketIds, $checkInList->getEventId()); + return $this->databaseManager->transaction(function () use ($checkInList, $productIds) { + $this->eventProductValidationService->validateProductIds($productIds, $checkInList->getEventId()); $event = $this->eventRepository->findById($checkInList->getEventId()); $this->checkInListRepository->updateWhere( @@ -50,9 +50,9 @@ public function updateCheckInList(CheckInListDomainObject $checkInList, array $t ] ); - $this->checkInListTicketAssociationService->addCheckInListToTickets( + $this->checkInListProductAssociationService->addCheckInListToProducts( checkInListId: $checkInList->getId(), - ticketIds: $ticketIds, + productIds: $productIds, ); return $this->checkInListRepository->findFirstWhere( diff --git a/backend/app/Services/Domain/Event/CreateEventService.php b/backend/app/Services/Domain/Event/CreateEventService.php index 4d8fdd8648..1ff21f1ed1 100644 --- a/backend/app/Services/Domain/Event/CreateEventService.php +++ b/backend/app/Services/Domain/Event/CreateEventService.php @@ -104,7 +104,7 @@ private function createEventStatistics(EventDomainObject $event): void { $this->eventStatisticsRepository->create([ 'event_id' => $event->getId(), - 'tickets_sold' => 0, + 'products_sold' => 0, 'sales_total_gross' => 0, 'sales_total_before_additions' => 0, 'total_tax' => 0, diff --git a/backend/app/Services/Domain/Event/DTO/DuplicateEventDataDTO.php b/backend/app/Services/Domain/Event/DTO/DuplicateEventDataDTO.php index 83262ea5e8..b06047234b 100644 --- a/backend/app/Services/Domain/Event/DTO/DuplicateEventDataDTO.php +++ b/backend/app/Services/Domain/Event/DTO/DuplicateEventDataDTO.php @@ -11,7 +11,7 @@ public function __construct( public int $accountId, public string $title, public string $startDate, - public bool $duplicateTickets = true, + public bool $duplicateProducts = true, public bool $duplicateQuestions = true, public bool $duplicateSettings = true, public bool $duplicatePromoCodes = true, diff --git a/backend/app/Services/Domain/Event/DTO/EventDailyStatsResponseDTO.php b/backend/app/Services/Domain/Event/DTO/EventDailyStatsResponseDTO.php index f6d88472de..180eaad700 100644 --- a/backend/app/Services/Domain/Event/DTO/EventDailyStatsResponseDTO.php +++ b/backend/app/Services/Domain/Event/DTO/EventDailyStatsResponseDTO.php @@ -9,7 +9,7 @@ public function __construct( public float $total_fees, public float $total_tax, public float $total_sales_gross, - public int $tickets_sold, + public int $products_sold, public int $orders_created, ) { diff --git a/backend/app/Services/Domain/Event/DuplicateEventService.php b/backend/app/Services/Domain/Event/DuplicateEventService.php index 96b7112a47..96dffb1782 100644 --- a/backend/app/Services/Domain/Event/DuplicateEventService.php +++ b/backend/app/Services/Domain/Event/DuplicateEventService.php @@ -12,8 +12,8 @@ use HiEvents\DomainObjects\QuestionDomainObject; use HiEvents\DomainObjects\Status\EventStatus; use HiEvents\DomainObjects\TaxAndFeesDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\ImageRepositoryInterface; @@ -21,7 +21,7 @@ use HiEvents\Services\Domain\CheckInList\CreateCheckInListService; use HiEvents\Services\Domain\PromoCode\CreatePromoCodeService; use HiEvents\Services\Domain\Question\CreateQuestionService; -use HiEvents\Services\Domain\Ticket\CreateTicketService; +use HiEvents\Services\Domain\Product\CreateProductService; use HTMLPurifier; use Illuminate\Database\DatabaseManager; use Throwable; @@ -31,7 +31,7 @@ class DuplicateEventService public function __construct( private readonly EventRepositoryInterface $eventRepository, private readonly CreateEventService $createEventService, - private readonly CreateTicketService $createTicketService, + private readonly CreateProductService $createProductService, private readonly CreateQuestionService $createQuestionService, private readonly CreatePromoCodeService $createPromoCodeService, private readonly CreateCapacityAssignmentService $createCapacityAssignmentService, @@ -51,7 +51,7 @@ public function duplicateEvent( string $accountId, string $title, string $startDate, - bool $duplicateTickets = true, + bool $duplicateProducts = true, bool $duplicateQuestions = true, bool $duplicateSettings = true, bool $duplicatePromoCodes = true, @@ -79,8 +79,8 @@ public function duplicateEvent( cloneEventSettings: $duplicateSettings, ); - if ($duplicateTickets) { - $this->cloneExistingTickets( + if ($duplicateProducts) { + $this->cloneExistingProducts( event: $event, newEventId: $newEvent->getId(), duplicateQuestions: $duplicateQuestions, @@ -131,7 +131,7 @@ private function cloneExistingEvent(EventDomainObject $event, bool $cloneEventSe /** * @throws Throwable */ - private function cloneExistingTickets( + private function cloneExistingProducts( EventDomainObject $event, int $newEventId, bool $duplicateQuestions, @@ -140,41 +140,41 @@ private function cloneExistingTickets( bool $duplicateCheckInLists, ): array { - $oldTicketToNewTicketMap = []; + $oldProductToNewProductMap = []; - foreach ($event->getTickets() as $ticket) { - $ticket->setEventId($newEventId); - $newTicket = $this->createTicketService->createTicket( - ticket: $ticket, + foreach ($event->getProducts() as $product) { + $product->setEventId($newEventId); + $newProduct = $this->createProductService->createProduct( + product: $product, accountId: $event->getAccountId(), - taxAndFeeIds: $ticket->getTaxAndFees()?->map(fn($taxAndFee) => $taxAndFee->getId())?->toArray(), + taxAndFeeIds: $product->getTaxAndFees()?->map(fn($taxAndFee) => $taxAndFee->getId())?->toArray(), ); - $oldTicketToNewTicketMap[$ticket->getId()] = $newTicket->getId(); + $oldProductToNewProductMap[$product->getId()] = $newProduct->getId(); } if ($duplicateQuestions) { - $this->cloneQuestions($event, $newEventId, $oldTicketToNewTicketMap); + $this->cloneQuestions($event, $newEventId, $oldProductToNewProductMap); } if ($duplicatePromoCodes) { - $this->clonePromoCodes($event, $newEventId, $oldTicketToNewTicketMap); + $this->clonePromoCodes($event, $newEventId, $oldProductToNewProductMap); } if ($duplicateCapacityAssignments) { - $this->cloneCapacityAssignments($event, $newEventId, $oldTicketToNewTicketMap); + $this->cloneCapacityAssignments($event, $newEventId, $oldProductToNewProductMap); } if ($duplicateCheckInLists) { - $this->cloneCheckInLists($event, $newEventId, $oldTicketToNewTicketMap); + $this->cloneCheckInLists($event, $newEventId, $oldProductToNewProductMap); } - return $oldTicketToNewTicketMap; + return $oldProductToNewProductMap; } /** * @throws Throwable */ - private function cloneQuestions(EventDomainObject $event, int $newEventId, array $oldTicketToNewTicketMap): void + private function cloneQuestions(EventDomainObject $event, int $newEventId, array $oldProductToNewProductMap): void { foreach ($event->getQuestions() as $question) { $this->createQuestionService->createQuestion( @@ -187,8 +187,8 @@ private function cloneQuestions(EventDomainObject $event, int $newEventId, array ->setOptions($question->getOptions()) ->setIsHidden($question->getIsHidden()), array_map( - static fn(TicketDomainObject $ticket) => $oldTicketToNewTicketMap[$ticket->getId()], - $question->getTickets()?->all(), + static fn(ProductDomainObject $product) => $oldProductToNewProductMap[$product->getId()], + $question->getProducts()?->all(), ), ); } @@ -197,16 +197,16 @@ private function cloneQuestions(EventDomainObject $event, int $newEventId, array /** * @throws Throwable */ - private function clonePromoCodes(EventDomainObject $event, int $newEventId, array $oldTicketToNewTicketMap): void + private function clonePromoCodes(EventDomainObject $event, int $newEventId, array $oldProductToNewProductMap): void { foreach ($event->getPromoCodes() as $promoCode) { $this->createPromoCodeService->createPromoCode( (new PromoCodeDomainObject()) ->setCode($promoCode->getCode()) ->setEventId($newEventId) - ->setApplicableTicketIds(array_map( - static fn($ticketId) => $oldTicketToNewTicketMap[$ticketId], - $promoCode->getApplicableTicketIds() ?? [], + ->setApplicableProductIds(array_map( + static fn($productId) => $oldProductToNewProductMap[$productId], + $promoCode->getApplicableProductIds() ?? [], )) ->setDiscountType($promoCode->getDiscountType()) ->setDiscount($promoCode->getDiscount()) @@ -216,7 +216,7 @@ private function clonePromoCodes(EventDomainObject $event, int $newEventId, arra } } - private function cloneCapacityAssignments(EventDomainObject $event, int $newEventId, $oldTicketToNewTicketMap): void + private function cloneCapacityAssignments(EventDomainObject $event, int $newEventId, $oldProductToNewProductMap): void { /** @var CapacityAssignmentDomainObject $capacityAssignment */ foreach ($event->getCapacityAssignments() as $capacityAssignment) { @@ -227,13 +227,13 @@ private function cloneCapacityAssignments(EventDomainObject $event, int $newEven ->setCapacity($capacityAssignment->getCapacity()) ->setAppliesTo($capacityAssignment->getAppliesTo()) ->setStatus($capacityAssignment->getStatus()), - ticketIds: $capacityAssignment->getTickets() - ?->map(fn($ticket) => $oldTicketToNewTicketMap[$ticket->getId()])?->toArray() ?? [], + productIds: $capacityAssignment->getProducts() + ?->map(fn($product) => $oldProductToNewProductMap[$product->getId()])?->toArray() ?? [], ); } } - private function cloneCheckInLists(EventDomainObject $event, int $newEventId, $oldTicketToNewTicketMap): void + private function cloneCheckInLists(EventDomainObject $event, int $newEventId, $oldProductToNewProductMap): void { foreach ($event->getCheckInLists() as $checkInList) { $this->createCheckInListService->createCheckInList( @@ -243,8 +243,8 @@ private function cloneCheckInLists(EventDomainObject $event, int $newEventId, $o ->setExpiresAt($checkInList->getExpiresAt()) ->setActivatesAt($checkInList->getActivatesAt()) ->setEventId($newEventId), - ticketIds: $checkInList->getTickets() - ?->map(fn($ticket) => $oldTicketToNewTicketMap[$ticket->getId()])?->toArray() ?? [], + productIds: $checkInList->getProducts() + ?->map(fn($product) => $oldProductToNewProductMap[$product->getId()])?->toArray() ?? [], ); } } @@ -272,20 +272,20 @@ private function getEventWithRelations(string $eventId, string $accountId): Even return $this->eventRepository ->loadRelation(EventSettingDomainObject::class) ->loadRelation( - new Relationship(TicketDomainObject::class, [ - new Relationship(TicketPriceDomainObject::class), + new Relationship(ProductDomainObject::class, [ + new Relationship(ProductPriceDomainObject::class), new Relationship(TaxAndFeesDomainObject::class) ]) ) ->loadRelation(PromoCodeDomainObject::class) ->loadRelation(new Relationship(QuestionDomainObject::class, [ - new Relationship(TicketDomainObject::class), + new Relationship(ProductDomainObject::class), ])) ->loadRelation(new Relationship(CapacityAssignmentDomainObject::class, [ - new Relationship(TicketDomainObject::class), + new Relationship(ProductDomainObject::class), ])) ->loadRelation(new Relationship(CheckInListDomainObject::class, [ - new Relationship(TicketDomainObject::class), + new Relationship(ProductDomainObject::class), ])) ->loadRelation(ImageDomainObject::class) ->findFirstWhere([ diff --git a/backend/app/Services/Domain/Event/EventStatsFetchService.php b/backend/app/Services/Domain/Event/EventStatsFetchService.php index 5344d7e4fd..d22d868176 100644 --- a/backend/app/Services/Domain/Event/EventStatsFetchService.php +++ b/backend/app/Services/Domain/Event/EventStatsFetchService.php @@ -25,7 +25,7 @@ public function getEventStats(EventStatsRequestDTO $requestData): EventStatsResp // Aggregate total statistics for the event for all time $totalsQuery = <<start_date, end_date: $requestData->end_date, check_in_stats: $this->getCheckedInStats($eventId), - total_tickets_sold: $totalsResult->total_tickets_sold ?? 0, + total_products_sold: $totalsResult->total_products_sold ?? 0, total_orders: $totalsResult->total_orders ?? 0, total_gross_sales: $totalsResult->total_gross_sales ?? 0, total_fees: $totalsResult->total_fees ?? 0, @@ -77,7 +77,7 @@ public function getDailyEventStats(EventStatsRequestDTO $requestData): Collectio COALESCE(SUM(eds.total_tax), 0) AS total_tax, COALESCE(SUM(eds.sales_total_gross), 0) AS total_sales_gross, COALESCE(SUM(eds.orders_created), 0) AS orders_created, - COALESCE(SUM(eds.tickets_sold), 0) AS tickets_sold + COALESCE(SUM(eds.products_sold), 0) AS products_sold FROM date_series ds LEFT JOIN event_daily_statistics eds ON ds.date = eds.date AND eds.deleted_at IS NULL AND eds.event_id = :eventId GROUP BY ds.date @@ -100,7 +100,7 @@ public function getDailyEventStats(EventStatsRequestDTO $requestData): Collectio total_fees: $result->total_fees, total_tax: $result->total_tax, total_sales_gross: $result->total_sales_gross, - tickets_sold: $result->tickets_sold, + products_sold: $result->products_sold, orders_created: $result->orders_created, ); }); diff --git a/backend/app/Services/Domain/EventStatistics/EventStatisticsUpdateService.php b/backend/app/Services/Domain/EventStatistics/EventStatisticsUpdateService.php index 7da0c4bfd3..5fbf9b48ff 100644 --- a/backend/app/Services/Domain/EventStatistics/EventStatisticsUpdateService.php +++ b/backend/app/Services/Domain/EventStatistics/EventStatisticsUpdateService.php @@ -3,7 +3,7 @@ namespace HiEvents\Services\Domain\EventStatistics; use HiEvents\DomainObjects\Generated\PromoCodeDomainObjectAbstract; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; use HiEvents\DomainObjects\OrderDomainObject; use HiEvents\DomainObjects\OrderItemDomainObject; use HiEvents\Exceptions\EventStatisticsVersionMismatchException; @@ -11,7 +11,7 @@ use HiEvents\Repository\Interfaces\EventStatisticRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Values\MoneyValue; use Illuminate\Database\DatabaseManager; use Illuminate\Support\Carbon; @@ -26,7 +26,7 @@ { public function __construct( private PromoCodeRepositoryInterface $promoCodeRepository, - private TicketRepositoryInterface $ticketRepository, + private ProductRepositoryInterface $productRepository, private EventStatisticRepositoryInterface $eventStatisticsRepository, private EventDailyStatisticRepositoryInterface $eventDailyStatisticRepository, private DatabaseManager $databaseManager, @@ -50,7 +50,7 @@ public function updateStatistics(OrderDomainObject $order): void $this->updateEventStats($order); $this->updateEventDailyStats($order); $this->updatePromoCodeCounts($order); - $this->updateTicketStatistics($order); + $this->updateProductStatistics($order); }); } @@ -126,12 +126,12 @@ private function updatePromoCodeCounts(OrderDomainObject $order): void } } - private function updateTicketStatistics(OrderDomainObject $order): void + private function updateProductStatistics(OrderDomainObject $order): void { foreach ($order->getOrderItems() as $orderItem) { - $this->ticketRepository->increment( - $orderItem->getTicketId(), - TicketDomainObjectAbstract::SALES_VOLUME, + $this->productRepository->increment( + $orderItem->getProductId(), + ProductDomainObjectAbstract::SALES_VOLUME, $orderItem->getTotalBeforeAdditions(), ); } @@ -153,7 +153,7 @@ private function updateEventStats(OrderDomainObject $order): void if ($eventStatistics === null) { $this->eventStatisticsRepository->create([ 'event_id' => $order->getEventId(), - 'tickets_sold' => $order->getOrderItems() + 'products_sold' => $order->getOrderItems() ?->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity()), 'sales_total_gross' => $order->getTotalGross(), 'sales_total_before_additions' => $order->getTotalBeforeAdditions(), @@ -167,7 +167,7 @@ private function updateEventStats(OrderDomainObject $order): void $update = $this->eventStatisticsRepository->updateWhere( attributes: [ - 'tickets_sold' => $eventStatistics->getTicketsSold() + $order->getOrderItems() + 'products_sold' => $eventStatistics->getProductsSold() + $order->getOrderItems() ?->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity()), 'sales_total_gross' => $eventStatistics->getSalesTotalGross() + $order->getTotalGross(), 'sales_total_before_additions' => $eventStatistics->getSalesTotalBeforeAdditions() + $order->getTotalBeforeAdditions(), @@ -208,7 +208,7 @@ private function updateEventDailyStats(OrderDomainObject $order): void $this->eventDailyStatisticRepository->create([ 'event_id' => $order->getEventId(), 'date' => (new Carbon($order->getCreatedAt()))->format('Y-m-d'), - 'tickets_sold' => $order->getOrderItems()?->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity()), + 'products_sold' => $order->getOrderItems()?->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity()), 'sales_total_gross' => $order->getTotalGross(), 'sales_total_before_additions' => $order->getTotalBeforeAdditions(), 'total_tax' => $order->getTotalTax(), @@ -220,7 +220,7 @@ private function updateEventDailyStats(OrderDomainObject $order): void $update = $this->eventDailyStatisticRepository->updateWhere( attributes: [ - 'tickets_sold' => $eventDailyStatistic->getTicketsSold() + $order->getOrderItems()->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity()), + 'products_sold' => $eventDailyStatistic->getProductsSold() + $order->getOrderItems()->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity()), 'sales_total_gross' => $eventDailyStatistic->getSalesTotalGross() + $order->getTotalGross(), 'sales_total_before_additions' => $eventDailyStatistic->getSalesTotalBeforeAdditions() + $order->getTotalBeforeAdditions(), 'total_tax' => $eventDailyStatistic->getTotalTax() + $order->getTotalTax(), diff --git a/backend/app/Services/Domain/Mail/SendEventEmailMessagesService.php b/backend/app/Services/Domain/Mail/SendEventEmailMessagesService.php index f4a6b05c85..8ab6cbda5f 100644 --- a/backend/app/Services/Domain/Mail/SendEventEmailMessagesService.php +++ b/backend/app/Services/Domain/Mail/SendEventEmailMessagesService.php @@ -70,8 +70,8 @@ public function send(SendMessageDTO $messageData): void case MessageTypeEnum::ORDER: $this->sendOrderMessages($messageData, $event, $order); break; - case MessageTypeEnum::TICKET: - $this->sendTicketMessages($messageData, $event); + case MessageTypeEnum::PRODUCT: + $this->sendProductMessages($messageData, $event); break; case MessageTypeEnum::EVENT: $this->sendEventMessages($messageData, $event); @@ -95,11 +95,11 @@ private function sendAttendeeMessages(SendMessageDTO $messageData, EventDomainOb $this->emailAttendees($attendees, $messageData, $event); } - private function sendTicketMessages(SendMessageDTO $messageData, EventDomainObject $event): void + private function sendProductMessages(SendMessageDTO $messageData, EventDomainObject $event): void { $attendees = $this->attendeeRepository->findWhereIn( - field: 'ticket_id', - values: $messageData->ticket_ids, + field: 'product_id', + values: $messageData->product_ids, additionalWhere: [ 'event_id' => $messageData->event_id, 'status' => AttendeeStatus::ACTIVE->name, diff --git a/backend/app/Services/Domain/Mail/SendOrderDetailsService.php b/backend/app/Services/Domain/Mail/SendOrderDetailsService.php index 62040573f9..aa8146fb96 100644 --- a/backend/app/Services/Domain/Mail/SendOrderDetailsService.php +++ b/backend/app/Services/Domain/Mail/SendOrderDetailsService.php @@ -14,7 +14,7 @@ use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; -use HiEvents\Services\Domain\Attendee\SendAttendeeTicketService; +use HiEvents\Services\Domain\Attendee\SendAttendeeProductService; use Illuminate\Mail\Mailer; readonly class SendOrderDetailsService @@ -23,12 +23,12 @@ public function __construct( private EventRepositoryInterface $eventRepository, private OrderRepositoryInterface $orderRepository, private Mailer $mailer, - private SendAttendeeTicketService $sendAttendeeTicketService, + private SendAttendeeProductService $sendAttendeeProductService, ) { } - public function sendOrderSummaryAndTicketEmails(OrderDomainObject $order): void + public function sendOrderSummaryAndProductEmails(OrderDomainObject $order): void { $order = $this->orderRepository ->loadRelation(OrderItemDomainObject::class) @@ -42,7 +42,7 @@ public function sendOrderSummaryAndTicketEmails(OrderDomainObject $order): void if ($order->isOrderCompleted()) { $this->sendOrderSummaryEmails($order, $event); - $this->sendAttendeeTicketEmails($order, $event); + $this->sendAttendeeProductEmails($order, $event); } if ($order->isOrderFailed()) { @@ -57,7 +57,7 @@ public function sendOrderSummaryAndTicketEmails(OrderDomainObject $order): void } } - private function sendAttendeeTicketEmails(OrderDomainObject $order, EventDomainObject $event): void + private function sendAttendeeProductEmails(OrderDomainObject $order, EventDomainObject $event): void { $sentEmails = []; foreach ($order->getAttendees() as $attendee) { @@ -65,7 +65,7 @@ private function sendAttendeeTicketEmails(OrderDomainObject $order, EventDomainO continue; } - $this->sendAttendeeTicketService->send( + $this->sendAttendeeProductService->send( attendee: $attendee, event: $event, eventSettings: $event->getEventSettings(), diff --git a/backend/app/Services/Domain/Order/OrderCancelService.php b/backend/app/Services/Domain/Order/OrderCancelService.php index b9e1125d82..3455cec02e 100644 --- a/backend/app/Services/Domain/Order/OrderCancelService.php +++ b/backend/app/Services/Domain/Order/OrderCancelService.php @@ -11,7 +11,7 @@ use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use Illuminate\Contracts\Mail\Mailer; use Illuminate\Database\DatabaseManager; use Throwable; @@ -19,12 +19,12 @@ readonly class OrderCancelService { public function __construct( - private Mailer $mailer, - private AttendeeRepositoryInterface $attendeeRepository, - private EventRepositoryInterface $eventRepository, - private OrderRepositoryInterface $orderRepository, - private DatabaseManager $databaseManager, - private TicketQuantityUpdateService $ticketQuantityService, + private Mailer $mailer, + private AttendeeRepositoryInterface $attendeeRepository, + private EventRepositoryInterface $eventRepository, + private OrderRepositoryInterface $orderRepository, + private DatabaseManager $databaseManager, + private ProductQuantityUpdateService $productQuantityService, ) { } @@ -36,7 +36,7 @@ public function cancelOrder(OrderDomainObject $order): void { $this->databaseManager->transaction(function () use ($order) { $this->cancelAttendees($order); - $this->adjustTicketQuantities($order); + $this->adjustProductQuantities($order); $this->updateOrderStatus($order); $event = $this->eventRepository @@ -66,18 +66,18 @@ private function cancelAttendees(OrderDomainObject $order): void ); } - private function adjustTicketQuantities(OrderDomainObject $order): void + private function adjustProductQuantities(OrderDomainObject $order): void { $attendees = $this->attendeeRepository->findWhere([ 'order_id' => $order->getId(), 'status' => AttendeeStatus::ACTIVE->name, ]); - $ticketIdCountMap = $attendees - ->map(fn(AttendeeDomainObject $attendee) => $attendee->getTicketPriceId())->countBy(); + $productIdCountMap = $attendees + ->map(fn(AttendeeDomainObject $attendee) => $attendee->getProductPriceId())->countBy(); - foreach ($ticketIdCountMap as $ticketPriceId => $count) { - $this->ticketQuantityService->decreaseQuantitySold($ticketPriceId, $count); + foreach ($productIdCountMap as $productPriceId => $count) { + $this->productQuantityService->decreaseQuantitySold($productPriceId, $count); } } diff --git a/backend/app/Services/Domain/Order/OrderCreateRequestValidationService.php b/backend/app/Services/Domain/Order/OrderCreateRequestValidationService.php index b55ad8c76e..f075f65b98 100644 --- a/backend/app/Services/Domain/Order/OrderCreateRequestValidationService.php +++ b/backend/app/Services/Domain/Order/OrderCreateRequestValidationService.php @@ -4,18 +4,18 @@ use Exception; use HiEvents\DomainObjects\CapacityAssignmentDomainObject; -use HiEvents\DomainObjects\Enums\TicketType; +use HiEvents\DomainObjects\Enums\ProductPriceType; use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\Generated\PromoCodeDomainObjectAbstract; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Helper\Currency; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; -use HiEvents\Services\Domain\Ticket\AvailableTicketQuantitiesFetchService; -use HiEvents\Services\Domain\Ticket\DTO\AvailableTicketQuantitiesDTO; -use HiEvents\Services\Domain\Ticket\DTO\AvailableTicketQuantitiesResponseDTO; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; +use HiEvents\Services\Domain\Product\AvailableProductQuantitiesFetchService; +use HiEvents\Services\Domain\Product\DTO\AvailableProductQuantitiesDTO; +use HiEvents\Services\Domain\Product\DTO\AvailableProductQuantitiesResponseDTO; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; @@ -23,13 +23,13 @@ class OrderCreateRequestValidationService { - private AvailableTicketQuantitiesResponseDTO $availableTicketQuantities; + private AvailableProductQuantitiesResponseDTO $availableProductQuantities; public function __construct( - readonly private TicketRepositoryInterface $ticketRepository, - readonly private PromoCodeRepositoryInterface $promoCodeRepository, - readonly private EventRepositoryInterface $eventRepository, - readonly private AvailableTicketQuantitiesFetchService $fetchAvailableTicketQuantitiesService, + readonly private ProductRepositoryInterface $productRepository, + readonly private PromoCodeRepositoryInterface $promoCodeRepository, + readonly private EventRepositoryInterface $eventRepository, + readonly private AvailableProductQuantitiesFetchService $fetchAvailableProductQuantitiesService, ) { } @@ -44,16 +44,16 @@ public function validateRequestData(int $eventId, array $data = []): void $event = $this->eventRepository->findById($eventId); $this->validatePromoCode($eventId, $data); - $this->validateTicketSelection($data); + $this->validateProductSelection($data); - $this->availableTicketQuantities = $this->fetchAvailableTicketQuantitiesService - ->getAvailableTicketQuantities( + $this->availableProductQuantities = $this->fetchAvailableProductQuantitiesService + ->getAvailableProductQuantities( $event->getId(), ignoreCache: true, ); $this->validateOverallCapacity($data); - $this->validateTicketDetails($event, $data); + $this->validateProductDetails($event, $data); } /** @@ -81,12 +81,12 @@ private function validatePromoCode(int $eventId, array $data): void private function validateTypes(array $data): void { $validator = Validator::make($data, [ - 'tickets' => 'required|array', - 'tickets.*.ticket_id' => 'required|integer', - 'tickets.*.quantities' => 'required|array', - 'tickets.*.quantities.*.quantity' => 'required|integer', - 'tickets.*.quantities.*.price_id' => 'required|integer', - 'tickets.*.quantities.*.price' => 'numeric|min:0', + 'products' => 'required|array', + 'products.*.product_id' => 'required|integer', + 'products.*.quantities' => 'required|array', + 'products.*.quantities.*.quantity' => 'required|integer', + 'products.*.quantities.*.price_id' => 'required|integer', + 'products.*.quantities.*.price' => 'numeric|min:0', ]); if ($validator->fails()) { @@ -97,12 +97,12 @@ private function validateTypes(array $data): void /** * @throws ValidationException */ - private function validateTicketSelection(array $data): void + private function validateProductSelection(array $data): void { - $ticketData = collect($data['tickets']); - if ($ticketData->isEmpty() || $ticketData->sum(fn($ticket) => collect($ticket['quantities'])->sum('quantity')) === 0) { + $productData = collect($data['products']); + if ($productData->isEmpty() || $productData->sum(fn($product) => collect($product['quantities'])->sum('quantity')) === 0) { throw ValidationException::withMessages([ - 'tickets' => __('You haven\'t selected any tickets') + 'products' => __('You haven\'t selected any products') ]); } } @@ -110,150 +110,150 @@ private function validateTicketSelection(array $data): void /** * @throws Exception */ - private function getTickets(array $data): Collection + private function getProducts(array $data): Collection { - $ticketIds = collect($data['tickets'])->pluck('ticket_id'); - return $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) - ->findWhereIn('id', $ticketIds->toArray()); + $productIds = collect($data['products'])->pluck('product_id'); + return $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) + ->findWhereIn('id', $productIds->toArray()); } /** * @throws ValidationException * @throws Exception */ - private function validateTicketDetails(EventDomainObject $event, array $data): void + private function validateProductDetails(EventDomainObject $event, array $data): void { - $tickets = $this->getTickets($data); + $products = $this->getProducts($data); - foreach ($data['tickets'] as $ticketIndex => $ticketAndQuantities) { - $this->validateSingleTicketDetails($event, $ticketIndex, $ticketAndQuantities, $tickets); + foreach ($data['products'] as $productIndex => $productAndQuantities) { + $this->validateSingleProductDetails($event, $productIndex, $productAndQuantities, $products); } } /** * @throws ValidationException */ - private function validateSingleTicketDetails(EventDomainObject $event, int $ticketIndex, array $ticketAndQuantities, $tickets): void + private function validateSingleProductDetails(EventDomainObject $event, int $productIndex, array $productAndQuantities, $products): void { - $ticketId = $ticketAndQuantities['ticket_id']; - $totalQuantity = collect($ticketAndQuantities['quantities'])->sum('quantity'); + $productId = $productAndQuantities['product_id']; + $totalQuantity = collect($productAndQuantities['quantities'])->sum('quantity'); if ($totalQuantity === 0) { return; } - /** @var TicketDomainObject $ticket */ - $ticket = $tickets->filter(fn($t) => $t->getId() === $ticketId)->first(); - if (!$ticket) { - throw new NotFoundHttpException(sprintf('Ticket ID %d not found', $ticketId)); + /** @var ProductDomainObject $product */ + $product = $products->filter(fn($t) => $t->getId() === $productId)->first(); + if (!$product) { + throw new NotFoundHttpException(sprintf('Product ID %d not found', $productId)); } - $this->validateTicketEvent( + $this->validateProductEvent( event: $event, - ticketId: $ticketId, - ticket: $ticket + productId: $productId, + product: $product ); - $this->validateTicketQuantity( - ticketIndex: $ticketIndex, - ticketAndQuantities: $ticketAndQuantities, - ticket: $ticket + $this->validateProductQuantity( + productIndex: $productIndex, + productAndQuantities: $productAndQuantities, + product: $product ); - $this->validateTicketTypeAndPrice( + $this->validateProductTypeAndPrice( event: $event, - ticketIndex: $ticketIndex, - ticketAndQuantities: $ticketAndQuantities, - ticket: $ticket + productIndex: $productIndex, + productAndQuantities: $productAndQuantities, + product: $product ); - $this->validateSoldOutTickets( - ticketId: $ticketId, - ticketIndex: $ticketIndex, - ticket: $ticket + $this->validateSoldOutProducts( + productId: $productId, + productIndex: $productIndex, + product: $product ); $this->validatePriceIdAndQuantity( - ticketIndex: $ticketIndex, - ticketAndQuantities: $ticketAndQuantities, - ticket: $ticket + productIndex: $productIndex, + productAndQuantities: $productAndQuantities, + product: $product ); } /** * @throws ValidationException */ - private function validateTicketQuantity(int $ticketIndex, array $ticketAndQuantities, TicketDomainObject $ticket): void + private function validateProductQuantity(int $productIndex, array $productAndQuantities, ProductDomainObject $product): void { - $totalQuantity = collect($ticketAndQuantities['quantities'])->sum('quantity'); - $maxPerOrder = (int)$ticket->getMaxPerOrder() ?: 100; + $totalQuantity = collect($productAndQuantities['quantities'])->sum('quantity'); + $maxPerOrder = (int)$product->getMaxPerOrder() ?: 100; - $capacityMaximum = $this->availableTicketQuantities - ->ticketQuantities - ->where('ticket_id', $ticket->getId()) - ->map(fn(AvailableTicketQuantitiesDTO $price) => $price->capacities) + $capacityMaximum = $this->availableProductQuantities + ->productQuantities + ->where('product_id', $product->getId()) + ->map(fn(AvailableProductQuantitiesDTO $price) => $price->capacities) ->flatten() ->min(fn(CapacityAssignmentDomainObject $capacity) => $capacity->getCapacity()); - $ticketAvailableQuantity = $this->availableTicketQuantities - ->ticketQuantities - ->first(fn(AvailableTicketQuantitiesDTO $price) => $price->ticket_id === $ticket->getId()) + $productAvailableQuantity = $this->availableProductQuantities + ->productQuantities + ->first(fn(AvailableProductQuantitiesDTO $price) => $price->product_id === $product->getId()) ->quantity_available; - # if there are fewer tickets available than the configured minimum, we allow less than the minimum to be purchased - $minPerOrder = min((int)$ticket->getMinPerOrder() ?: 1, + # if there are fewer products available than the configured minimum, we allow less than the minimum to be purchased + $minPerOrder = min((int)$product->getMinPerOrder() ?: 1, $capacityMaximum ?: $maxPerOrder, - $ticketAvailableQuantity ?: $maxPerOrder); + $productAvailableQuantity ?: $maxPerOrder); - $this->validateTicketPricesQuantity( - quantities: $ticketAndQuantities['quantities'], - ticket: $ticket, - ticketIndex: $ticketIndex + $this->validateProductPricesQuantity( + quantities: $productAndQuantities['quantities'], + product: $product, + productIndex: $productIndex ); if ($totalQuantity > $maxPerOrder) { throw ValidationException::withMessages([ - "tickets.$ticketIndex" => __("The maximum number of tickets available for :tickets is :max", [ + "products.$productIndex" => __("The maximum number of products available for :products is :max", [ 'max' => $maxPerOrder, - 'ticket' => $ticket->getTitle(), + 'product' => $product->getTitle(), ]), ]); } if ($totalQuantity < $minPerOrder) { throw ValidationException::withMessages([ - "tickets.$ticketIndex" => __("You must order at least :min tickets for :ticket", [ + "products.$productIndex" => __("You must order at least :min products for :product", [ 'min' => $minPerOrder, - 'ticket' => $ticket->getTitle(), + 'product' => $product->getTitle(), ]), ]); } } - private function validateTicketEvent(EventDomainObject $event, int $ticketId, TicketDomainObject $ticket): void + private function validateProductEvent(EventDomainObject $event, int $productId, ProductDomainObject $product): void { - if ($ticket->getEventId() !== $event->getId()) { - throw new NotFoundHttpException(sprintf('Ticket ID %d not found for event ID %d', $ticketId, $event->getId())); + if ($product->getEventId() !== $event->getId()) { + throw new NotFoundHttpException(sprintf('Product ID %d not found for event ID %d', $productId, $event->getId())); } } /** * @throws ValidationException */ - private function validateTicketTypeAndPrice( + private function validateProductTypeAndPrice( EventDomainObject $event, - int $ticketIndex, - array $ticketAndQuantities, - TicketDomainObject $ticket + int $productIndex, + array $productAndQuantities, + ProductDomainObject $product ): void { - if ($ticket->getType() === TicketType::DONATION->name) { - $price = $ticketAndQuantities['quantities'][0]['price'] ?? 0; - if ($price < $ticket->getPrice()) { - $formattedPrice = Currency::format($ticket->getPrice(), $event->getCurrency()); + if ($product->getType() === ProductPriceType::DONATION->name) { + $price = $productAndQuantities['quantities'][0]['price'] ?? 0; + if ($price < $product->getPrice()) { + $formattedPrice = Currency::format($product->getPrice(), $event->getCurrency()); throw ValidationException::withMessages([ - "tickets.$ticketIndex.quantities.0.price" => __("The minimum amount is :price", ['price' => $formattedPrice]), + "products.$productIndex.quantities.0.price" => __("The minimum amount is :price", ['price' => $formattedPrice]), ]); } } @@ -262,13 +262,13 @@ private function validateTicketTypeAndPrice( /** * @throws ValidationException */ - private function validateSoldOutTickets(int $ticketId, int $ticketIndex, TicketDomainObject $ticket): void + private function validateSoldOutProducts(int $productId, int $productIndex, ProductDomainObject $product): void { - if ($ticket->isSoldOut()) { + if ($product->isSoldOut()) { throw ValidationException::withMessages([ - "tickets.$ticketIndex" => __("The ticket :ticket is sold out", [ - 'id' => $ticketId, - 'ticket' => $ticket->getTitle(), + "products.$productIndex" => __("The product :product is sold out", [ + 'id' => $productId, + 'product' => $product->getTitle(), ]), ]); } @@ -277,24 +277,24 @@ private function validateSoldOutTickets(int $ticketId, int $ticketIndex, TicketD /** * @throws ValidationException */ - private function validatePriceIdAndQuantity(int $ticketIndex, array $ticketAndQuantities, TicketDomainObject $ticket): void + private function validatePriceIdAndQuantity(int $productIndex, array $productAndQuantities, ProductDomainObject $product): void { $errors = []; - foreach ($ticketAndQuantities['quantities'] as $quantityIndex => $quantityData) { + foreach ($productAndQuantities['quantities'] as $quantityIndex => $quantityData) { $priceId = $quantityData['price_id'] ?? null; $quantity = $quantityData['quantity'] ?? null; if (null === $priceId || null === $quantity) { $missingField = null === $priceId ? 'price_id' : 'quantity'; - $errors["tickets.$ticketIndex.quantities.$quantityIndex.$missingField"] = __(":field must be specified", [ + $errors["products.$productIndex.quantities.$quantityIndex.$missingField"] = __(":field must be specified", [ 'field' => ucfirst($missingField) ]); } - $validPriceIds = $ticket->getTicketPrices()?->map(fn(TicketPriceDomainObject $price) => $price->getId()); + $validPriceIds = $product->getProductPrices()?->map(fn(ProductPriceDomainObject $price) => $price->getId()); if (!in_array($priceId, $validPriceIds->toArray(), true)) { - $errors["tickets.$ticketIndex.quantities.$quantityIndex.price_id"] = __('Invalid price ID'); + $errors["products.$productIndex.quantities.$quantityIndex.price_id"] = __('Invalid price ID'); } } @@ -306,32 +306,32 @@ private function validatePriceIdAndQuantity(int $ticketIndex, array $ticketAndQu /** * @throws ValidationException */ - private function validateTicketPricesQuantity(array $quantities, TicketDomainObject $ticket, int $ticketIndex): void + private function validateProductPricesQuantity(array $quantities, ProductDomainObject $product, int $productIndex): void { - foreach ($quantities as $ticketQuantity) { - $numberAvailable = $this->availableTicketQuantities - ->ticketQuantities - ->where('ticket_id', $ticket->getId()) - ->where('price_id', $ticketQuantity['price_id']) + foreach ($quantities as $productQuantity) { + $numberAvailable = $this->availableProductQuantities + ->productQuantities + ->where('product_id', $product->getId()) + ->where('price_id', $productQuantity['price_id']) ->first()?->quantity_available; - /** @var TicketPriceDomainObject $ticketPrice */ - $ticketPrice = $ticket->getTicketPrices() - ?->first(fn(TicketPriceDomainObject $price) => $price->getId() === $ticketQuantity['price_id']); + /** @var ProductPriceDomainObject $productPrice */ + $productPrice = $product->getProductPrices() + ?->first(fn(ProductPriceDomainObject $price) => $price->getId() === $productQuantity['price_id']); - if ($ticketQuantity['quantity'] > $numberAvailable) { + if ($productQuantity['quantity'] > $numberAvailable) { if ($numberAvailable === 0) { throw ValidationException::withMessages([ - "tickets.$ticketIndex" => __("The ticket :ticket is sold out", [ - 'ticket' => $ticket->getTitle() . ($ticketPrice->getLabel() ? ' - ' . $ticketPrice->getLabel() : ''), + "products.$productIndex" => __("The product :product is sold out", [ + 'product' => $product->getTitle() . ($productPrice->getLabel() ? ' - ' . $productPrice->getLabel() : ''), ]), ]); } throw ValidationException::withMessages([ - "tickets.$ticketIndex" => __("The maximum number of tickets available for :ticket is :max", [ + "products.$productIndex" => __("The maximum number of products available for :product is :max", [ 'max' => $numberAvailable, - 'ticket' => $ticket->getTitle() . ($ticketPrice->getLabel() ? ' - ' . $ticketPrice->getLabel() : ''), + 'product' => $product->getTitle() . ($productPrice->getLabel() ? ' - ' . $productPrice->getLabel() : ''), ]), ]); } @@ -343,34 +343,34 @@ private function validateTicketPricesQuantity(array $quantities, TicketDomainObj */ private function validateOverallCapacity(array $data): void { - foreach ($this->availableTicketQuantities->capacities as $capacity) { - if ($capacity->getTickets() === null) { + foreach ($this->availableProductQuantities->capacities as $capacity) { + if ($capacity->getProducts() === null) { continue; } - $ticketIds = $capacity->getTickets()->map(fn(TicketDomainObject $ticket) => $ticket->getId()); - $totalQuantity = collect($data['tickets']) - ->filter(fn($ticket) => in_array($ticket['ticket_id'], $ticketIds->toArray(), true)) - ->sum(fn($ticket) => collect($ticket['quantities'])->sum('quantity')); + $productIds = $capacity->getProducts()->map(fn(ProductDomainObject $product) => $product->getId()); + $totalQuantity = collect($data['products']) + ->filter(fn($product) => in_array($product['product_id'], $productIds->toArray(), true)) + ->sum(fn($product) => collect($product['quantities'])->sum('quantity')); - $reservedTicketQuantities = $capacity->getTickets() - ->map(fn(TicketDomainObject $ticket) => $this - ->availableTicketQuantities - ->ticketQuantities - ->where('ticket_id', $ticket->getId()) + $reservedProductQuantities = $capacity->getProducts() + ->map(fn(ProductDomainObject $product) => $this + ->availableProductQuantities + ->productQuantities + ->where('product_id', $product->getId()) ->sum('quantity_reserved') ) ->sum(); - if ($totalQuantity > ($capacity->getAvailableCapacity() - $reservedTicketQuantities)) { - if ($capacity->getAvailableCapacity() - $reservedTicketQuantities <= 0) { + if ($totalQuantity > ($capacity->getAvailableCapacity() - $reservedProductQuantities)) { + if ($capacity->getAvailableCapacity() - $reservedProductQuantities <= 0) { throw ValidationException::withMessages([ - 'tickets' => __('Sorry, these tickets are sold out'), + 'products' => __('Sorry, these products are sold out'), ]); } throw ValidationException::withMessages([ - 'tickets' => __('The maximum number of tickets available is :max', [ + 'products' => __('The maximum number of products available is :max', [ 'max' => $capacity->getAvailableCapacity(), ]), ]); diff --git a/backend/app/Services/Domain/Order/OrderItemProcessingService.php b/backend/app/Services/Domain/Order/OrderItemProcessingService.php index 3ab93a6e7b..8536ecee84 100644 --- a/backend/app/Services/Domain/Order/OrderItemProcessingService.php +++ b/backend/app/Services/Domain/Order/OrderItemProcessingService.php @@ -3,19 +3,19 @@ namespace HiEvents\Services\Domain\Order; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; use HiEvents\DomainObjects\OrderDomainObject; use HiEvents\DomainObjects\PromoCodeDomainObject; use HiEvents\DomainObjects\TaxAndFeesDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Helper\Currency; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Services\Domain\Tax\TaxAndFeeCalculationService; -use HiEvents\Services\Domain\Ticket\DTO\OrderTicketPriceDTO; -use HiEvents\Services\Domain\Ticket\TicketPriceService; -use HiEvents\Services\Handlers\Order\DTO\TicketOrderDetailsDTO; +use HiEvents\Services\Domain\Product\DTO\OrderProductPriceDTO; +use HiEvents\Services\Domain\Product\ProductPriceService; +use HiEvents\Services\Handlers\Order\DTO\ProductOrderDetailsDTO; use Illuminate\Support\Collection; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -23,49 +23,49 @@ { public function __construct( private OrderRepositoryInterface $orderRepository, - private TicketRepositoryInterface $ticketRepository, + private ProductRepositoryInterface $productRepository, private TaxAndFeeCalculationService $taxCalculationService, - private TicketPriceService $ticketPriceService, + private ProductPriceService $productPriceService, ) { } /** * @param OrderDomainObject $order - * @param Collection $ticketsOrderDetails + * @param Collection $productsOrderDetails * @param EventDomainObject $event * @param PromoCodeDomainObject|null $promoCode * @return Collection */ public function process( OrderDomainObject $order, - Collection $ticketsOrderDetails, + Collection $productsOrderDetails, EventDomainObject $event, ?PromoCodeDomainObject $promoCode ): Collection { $orderItems = collect(); - foreach ($ticketsOrderDetails as $ticketOrderDetail) { - $ticket = $this->ticketRepository + foreach ($productsOrderDetails as $productOrderDetail) { + $product = $this->productRepository ->loadRelation(TaxAndFeesDomainObject::class) - ->loadRelation(TicketPriceDomainObject::class) + ->loadRelation(ProductPriceDomainObject::class) ->findFirstWhere([ - TicketDomainObjectAbstract::ID => $ticketOrderDetail->ticket_id, - TicketDomainObjectAbstract::EVENT_ID => $event->getId(), + ProductDomainObjectAbstract::ID => $productOrderDetail->product_id, + ProductDomainObjectAbstract::EVENT_ID => $event->getId(), ]); - if ($ticket === null) { + if ($product === null) { throw new ResourceNotFoundException( - __('Ticket with id :id not found', ['id' => $ticketOrderDetail->ticket_id]) + __('Product with id :id not found', ['id' => $productOrderDetail->product_id]) ); } - $ticketOrderDetail->quantities->each(function (OrderTicketPriceDTO $ticketPrice) use ($promoCode, $order, $orderItems, $ticket) { - if ($ticketPrice->quantity === 0) { + $productOrderDetail->quantities->each(function (OrderProductPriceDTO $productPrice) use ($promoCode, $order, $orderItems, $product) { + if ($productPrice->quantity === 0) { return; } - $orderItemData = $this->calculateOrderItemData($ticket, $ticketPrice, $order, $promoCode); + $orderItemData = $this->calculateOrderItemData($product, $productPrice, $order, $promoCode); $orderItems->push($this->orderRepository->addOrderItem($orderItemData)); }); } @@ -74,33 +74,33 @@ public function process( } private function calculateOrderItemData( - TicketDomainObject $ticket, - OrderTicketPriceDTO $ticketPriceDetails, + ProductDomainObject $product, + OrderProductPriceDTO $productPriceDetails, OrderDomainObject $order, ?PromoCodeDomainObject $promoCode ): array { - $prices = $this->ticketPriceService->getPrice($ticket, $ticketPriceDetails, $promoCode); + $prices = $this->productPriceService->getPrice($product, $productPriceDetails, $promoCode); $priceWithDiscount = $prices->price; $priceBeforeDiscount = $prices->price_before_discount; - $itemTotalWithDiscount = $priceWithDiscount * $ticketPriceDetails->quantity; + $itemTotalWithDiscount = $priceWithDiscount * $productPriceDetails->quantity; - $taxesAndFees = $this->taxCalculationService->calculateTaxAndFeesForTicket( - ticket: $ticket, + $taxesAndFees = $this->taxCalculationService->calculateTaxAndFeesForProduct( + product: $product, price: $priceWithDiscount, - quantity: $ticketPriceDetails->quantity + quantity: $productPriceDetails->quantity ); return [ - 'ticket_id' => $ticket->getId(), - 'ticket_price_id' => $ticketPriceDetails->price_id, - 'quantity' => $ticketPriceDetails->quantity, + 'product_id' => $product->getId(), + 'product_price_id' => $productPriceDetails->price_id, + 'quantity' => $productPriceDetails->quantity, 'price_before_discount' => $priceBeforeDiscount, 'total_before_additions' => Currency::round($itemTotalWithDiscount), 'price' => $priceWithDiscount, 'order_id' => $order->getId(), - 'item_name' => $this->getOrderItemLabel($ticket, $ticketPriceDetails->price_id), + 'item_name' => $this->getOrderItemLabel($product, $productPriceDetails->price_id), 'total_tax' => $taxesAndFees->taxTotal, 'total_service_fee' => $taxesAndFees->feeTotal, 'total_gross' => Currency::round($itemTotalWithDiscount + $taxesAndFees->taxTotal + $taxesAndFees->feeTotal), @@ -108,14 +108,14 @@ private function calculateOrderItemData( ]; } - private function getOrderItemLabel(TicketDomainObject $ticket, int $priceId): string + private function getOrderItemLabel(ProductDomainObject $product, int $priceId): string { - if ($ticket->isTieredType()) { - return $ticket->getTitle() . ' - ' . $ticket->getTicketPrices() + if ($product->isTieredType()) { + return $product->getTitle() . ' - ' . $product->getProductPrices() ?->filter(fn($p) => $p->getId() === $priceId)->first() ?->getLabel(); } - return $ticket->getTitle(); + return $product->getTitle(); } } diff --git a/backend/app/Services/Domain/Payment/Stripe/EventHandlers/PaymentIntentSucceededHandler.php b/backend/app/Services/Domain/Payment/Stripe/EventHandlers/PaymentIntentSucceededHandler.php index 8f7f37b29c..11bc97e98c 100644 --- a/backend/app/Services/Domain/Payment/Stripe/EventHandlers/PaymentIntentSucceededHandler.php +++ b/backend/app/Services/Domain/Payment/Stripe/EventHandlers/PaymentIntentSucceededHandler.php @@ -19,7 +19,7 @@ use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; use HiEvents\Services\Domain\Payment\Stripe\StripeRefundExpiredOrderService; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use Illuminate\Database\DatabaseManager; use Stripe\Exception\ApiErrorException; use Stripe\PaymentIntent; @@ -30,7 +30,7 @@ public function __construct( private OrderRepositoryInterface $orderRepository, private StripePaymentsRepository $stripePaymentsRepository, - private TicketQuantityUpdateService $quantityUpdateService, + private ProductQuantityUpdateService $quantityUpdateService, private StripeRefundExpiredOrderService $refundExpiredOrderService, private DatabaseManager $databaseManager, ) @@ -93,8 +93,8 @@ private function updateStripePaymentInfo(PaymentIntent $paymentIntent, StripePay /** * If the order has expired (reserved_until is in the past), refund the payment and throw an exception. - * This does seem quite extreme, but it ensures we don't oversell tickets. As far as I can see - * this is how Ticketmaster and other ticketing systems work. + * This does seem quite extreme, but it ensures we don't oversell products. As far as I can see + * this is how Productmaster and other producting systems work. * * @throws ApiErrorException * @throws RoundingNecessaryException @@ -102,7 +102,7 @@ private function updateStripePaymentInfo(PaymentIntent $paymentIntent, StripePay * @throws MathException * @throws UnknownCurrencyException * @throws NumberFormatException - * @todo We could check to see if there are tickets available, and if so, complete the order. + * @todo We could check to see if there are products available, and if so, complete the order. * This would be a better user experience. * */ diff --git a/backend/app/Services/Domain/Product/AvailableProductQuantitiesFetchService.php b/backend/app/Services/Domain/Product/AvailableProductQuantitiesFetchService.php new file mode 100644 index 0000000000..12e060e2dc --- /dev/null +++ b/backend/app/Services/Domain/Product/AvailableProductQuantitiesFetchService.php @@ -0,0 +1,173 @@ +config->get('app.homepage_product_quantities_cache_ttl')) { + $cachedData = $this->getDataFromCache($eventId); + if ($cachedData) { + return $cachedData; + } + } + + $capacities = $this->capacityAssignmentRepository + ->loadRelation(ProductDomainObject::class) + ->findWhere([ + 'event_id' => $eventId, + 'applies_to' => CapacityAssignmentAppliesTo::PRODUCTS->name, + 'status' => CapacityAssignmentStatus::ACTIVE->name, + ]); + + $reservedProductQuantities = $this->fetchReservedProductQuantities($eventId); + $productCapacities = $this->calculateProductCapacities($capacities); + + $quantities = $reservedProductQuantities->map(function (AvailableProductQuantitiesDTO $dto) use ($productCapacities) { + $productId = $dto->product_id; + if (isset($productCapacities[$productId])) { + $dto->quantity_available = min(array_merge([$dto->quantity_available], $productCapacities[$productId]->map->getAvailableCapacity()->toArray())); + $dto->capacities = $productCapacities[$productId]; + } + + return $dto; + }); + + $finalData = new AvailableProductQuantitiesResponseDTO( + productQuantities: $quantities, + capacities: $capacities + ); + + if (!$ignoreCache && $this->config->get('app.homepage_product_quantities_cache_ttl')) { + $this->cache->put($this->getCacheKey($eventId), $finalData, $this->config->get('app.homepage_product_quantities_cache_ttl')); + } + + return $finalData; + } + + private function fetchReservedProductQuantities(int $eventId): Collection + { + $result = $this->db->select(<< NOW() + AND orders.deleted_at IS NULL + THEN order_items.quantity + ELSE 0 + END + ) AS quantity_reserved + FROM products + JOIN product_prices ON products.id = product_prices.product_id + LEFT JOIN order_items ON order_items.product_id = products.id + AND order_items.product_price_id = product_prices.id + LEFT JOIN orders ON orders.id = order_items.order_id + AND orders.event_id = products.event_id + AND orders.deleted_at IS NULL + WHERE + products.event_id = :eventId + AND products.deleted_at IS NULL + AND product_prices.deleted_at IS NULL + GROUP BY products.id, product_prices.id + ) + SELECT + products.id AS product_id, + product_prices.id AS product_price_id, + products.title AS product_title, + product_prices.label AS price_label, + product_prices.initial_quantity_available, + product_prices.quantity_sold, + COALESCE( + product_prices.initial_quantity_available + - product_prices.quantity_sold + - COALESCE(reserved_quantities.quantity_reserved, 0), + 0) AS quantity_available, + COALESCE(reserved_quantities.quantity_reserved, 0) AS quantity_reserved, + CASE WHEN product_prices.initial_quantity_available IS NULL + THEN TRUE + ELSE FALSE + END AS unlimited_quantity_available + FROM products + JOIN product_prices ON products.id = product_prices.product_id + LEFT JOIN reserved_quantities ON products.id = reserved_quantities.product_id + AND product_prices.id = reserved_quantities.product_price_id + WHERE + products.event_id = :eventId + AND products.deleted_at IS NULL + AND product_prices.deleted_at IS NULL + GROUP BY products.id, product_prices.id, reserved_quantities.quantity_reserved; + SQL, [ + 'eventId' => $eventId, + 'reserved' => OrderStatus::RESERVED->name + ]); + + return collect($result)->map(fn($row) => AvailableProductQuantitiesDTO::fromArray([ + 'product_id' => $row->product_id, + 'price_id' => $row->product_price_id, + 'product_title' => $row->product_title, + 'price_label' => $row->price_label, + 'quantity_available' => $row->unlimited_quantity_available ? Constants::INFINITE : $row->quantity_available, + 'initial_quantity_available' => $row->initial_quantity_available, + 'quantity_reserved' => $row->quantity_reserved, + 'capacities' => new Collection(), + ])); + } + + /** + * @param Collection $capacities + */ + private function calculateProductCapacities(Collection $capacities): array + { + $productCapacities = []; + foreach ($capacities as $capacity) { + foreach ($capacity->getProducts() as $product) { + $productId = $product->getId(); + if (!isset($productCapacities[$productId])) { + $productCapacities[$productId] = collect(); + } + + $productCapacities[$productId]->push($capacity); + } + } + + return $productCapacities; + } + + private function getDataFromCache(int $eventId): ?AvailableProductQuantitiesResponseDTO + { + return $this->cache->get($this->getCacheKey($eventId)); + } + + private function getCacheKey(int $eventId): string + { + return "event.$eventId.available_product_quantities"; + } +} diff --git a/backend/app/Services/Domain/Product/CreateProductService.php b/backend/app/Services/Domain/Product/CreateProductService.php new file mode 100644 index 0000000000..85032c3914 --- /dev/null +++ b/backend/app/Services/Domain/Product/CreateProductService.php @@ -0,0 +1,117 @@ +databaseManager->transaction(function () use ($accountId, $taxAndFeeIds, $product) { + $persistedProduct = $this->persistProduct($product); + + if ($taxAndFeeIds) { + $this->associateTaxesAndFees($persistedProduct, $taxAndFeeIds, $accountId); + } + + return $this->createProductPrices($persistedProduct, $product); + }); + } + + private function persistProduct(ProductDomainObject $productsData): ProductDomainObject + { + $event = $this->eventRepository->findById($productsData->getEventId()); + + return $this->productRepository->create([ + 'title' => $productsData->getTitle(), + 'type' => $productsData->getType(), + 'order' => $productsData->getOrder(), + 'sale_start_date' => $productsData->getSaleStartDate() + ? DateHelper::convertToUTC($productsData->getSaleStartDate(), $event->getTimezone()) + : null, + 'sale_end_date' => $productsData->getSaleEndDate() + ? DateHelper::convertToUTC($productsData->getSaleEndDate(), $event->getTimezone()) + : null, + 'max_per_order' => $productsData->getMaxPerOrder(), + 'description' => $this->purifier->purify($productsData->getDescription()), + 'min_per_order' => $productsData->getMinPerOrder(), + 'is_hidden' => $productsData->getIsHidden(), + 'hide_before_sale_start_date' => $productsData->getHideBeforeSaleStartDate(), + 'hide_after_sale_end_date' => $productsData->getHideAfterSaleEndDate(), + 'hide_when_sold_out' => $productsData->getHideWhenSoldOut(), + 'show_quantity_remaining' => $productsData->getShowQuantityRemaining(), + 'is_hidden_without_promo_code' => $productsData->getIsHiddenWithoutPromoCode(), + 'event_id' => $productsData->getEventId(), + ]); + } + + /** + * @throws Exception + */ + private function createProductTaxesAndFees( + ProductDomainObject $product, + array $taxAndFeeIds, + int $accountId, + ): Collection + { + return $this->taxAndProductAssociationService->addTaxesToProduct( + new TaxAndProductAssociateParams( + productId: $product->getId(), + accountId: $accountId, + taxAndFeeIds: $taxAndFeeIds, + ), + ); + } + + /** + * @throws Exception + */ + private function associateTaxesAndFees(ProductDomainObject $persistedProduct, array $taxAndFeeIds, int $accountId): void + { + $persistedProduct->setTaxAndFees($this->createProductTaxesAndFees( + product: $persistedProduct, + taxAndFeeIds: $taxAndFeeIds, + accountId: $accountId, + )); + } + + private function createProductPrices(ProductDomainObject $persistedProduct, ProductDomainObject $product): ProductDomainObject + { + $prices = $this->priceCreateService->createPrices( + productId: $persistedProduct->getId(), + prices: $product->getProductPrices(), + event: $this->eventRepository->findById($product->getEventId()), + ); + + return $persistedProduct->setProductPrices($prices); + } +} diff --git a/backend/app/Services/Domain/Ticket/DTO/AvailableTicketQuantitiesDTO.php b/backend/app/Services/Domain/Product/DTO/AvailableProductQuantitiesDTO.php similarity index 74% rename from backend/app/Services/Domain/Ticket/DTO/AvailableTicketQuantitiesDTO.php rename to backend/app/Services/Domain/Product/DTO/AvailableProductQuantitiesDTO.php index 72c032b6b1..01c68affaf 100644 --- a/backend/app/Services/Domain/Ticket/DTO/AvailableTicketQuantitiesDTO.php +++ b/backend/app/Services/Domain/Product/DTO/AvailableProductQuantitiesDTO.php @@ -1,17 +1,17 @@ */ - public Collection $ticketQuantities, + /** @var Collection */ + public Collection $productQuantities, /** @var Collection */ public ?Collection $capacities = null, ) diff --git a/backend/app/Services/Domain/Product/DTO/CreateProductDTO.php b/backend/app/Services/Domain/Product/DTO/CreateProductDTO.php new file mode 100644 index 0000000000..3784ab8573 --- /dev/null +++ b/backend/app/Services/Domain/Product/DTO/CreateProductDTO.php @@ -0,0 +1,10 @@ +productRepository->findWhere([ + 'event_id' => $eventId, + ])->map(fn(ProductDomainObject $product) => $product->getId()) + ->toArray(); + + $invalidProductIds = array_diff($productIds, $validProductIds); + + if (!empty($invalidProductIds)) { + throw new UnrecognizedProductIdException( + __('Invalid product ids: :ids', ['ids' => implode(', ', $invalidProductIds)]) + ); + } + } +} diff --git a/backend/app/Services/Domain/Product/Exception/UnrecognizedProductIdException.php b/backend/app/Services/Domain/Product/Exception/UnrecognizedProductIdException.php new file mode 100644 index 0000000000..f788e3fe82 --- /dev/null +++ b/backend/app/Services/Domain/Product/Exception/UnrecognizedProductIdException.php @@ -0,0 +1,10 @@ + $products + * @param PromoCodeDomainObject|null $promoCode + * @param bool $hideSoldOutProducts + * @return Collection + */ + public function filter( + Collection $products, + ?PromoCodeDomainObject $promoCode = null, + bool $hideSoldOutProducts = true, + ): Collection + { + if ($products->isEmpty()) { + return $products; + } + + $productQuantities = $this + ->fetchAvailableProductQuantitiesService + ->getAvailableProductQuantities($products->first()->getEventId()); + + return $products + ->map(fn(ProductDomainObject $product) => $this->processProduct($product, $productQuantities->productQuantities, $promoCode)) + ->reject(fn(ProductDomainObject $product) => $this->filterProduct($product, $promoCode, $hideSoldOutProducts)) + ->each(fn(ProductDomainObject $product) => $this->processProductPrices($product, $hideSoldOutProducts)); + } + + private function isHiddenByPromoCode(ProductDomainObject $product, ?PromoCodeDomainObject $promoCode): bool + { + return $product->getIsHiddenWithoutPromoCode() && !( + $promoCode + && $promoCode->appliesToProduct($product) + ); + } + + private function shouldProductBeDiscounted(?PromoCodeDomainObject $promoCode, ProductDomainObject $product): bool + { + if ($product->isDonationType() || $product->isFreeType()) { + return false; + } + + return $promoCode + && $promoCode->isDiscountCode() + && $promoCode->appliesToProduct($product); + } + + /** + * @param PromoCodeDomainObject|null $promoCode + * @param ProductDomainObject $product + * @param Collection $productQuantities + * @return ProductDomainObject + */ + private function processProduct( + ProductDomainObject $product, + Collection $productQuantities, + ?PromoCodeDomainObject $promoCode = null, + ): ProductDomainObject + { + if ($this->shouldProductBeDiscounted($promoCode, $product)) { + $product->getProductPrices()?->each(function (ProductPriceDomainObject $price) use ($product, $promoCode) { + $price->setPriceBeforeDiscount($price->getPrice()); + $price->setPrice($this->productPriceService->getIndividualPrice($product, $price, $promoCode)); + }); + } + + $product->getProductPrices()?->map(function (ProductPriceDomainObject $price) use ($productQuantities) { + $availableQuantity = $productQuantities->where('price_id', $price->getId())->first()?->quantity_available; + $availableQuantity = $availableQuantity === Constants::INFINITE ? null : $availableQuantity; + $price->setQuantityAvailable( + max($availableQuantity, 0) + ); + }); + + // If there is a capacity assigned to the product, we set the capacity to capacity available qty, or the sum of all + // product prices qty, whichever is lower + $productQuantities->each(function (AvailableProductQuantitiesDTO $quantity) use ($product) { + if ($quantity->capacities !== null && $quantity->capacities->isNotEmpty() && $quantity->product_id === $product->getId()) { + $product->setQuantityAvailable( + $quantity->capacities->min(fn(CapacityAssignmentDomainObject $capacity) => $capacity->getAvailableCapacity()) + ); + } + }); + + return $product; + } + + private function filterProduct( + ProductDomainObject $product, + ?PromoCodeDomainObject $promoCode = null, + bool $hideSoldOutProducts = true, + ): bool + { + $hidden = false; + + if ($this->isHiddenByPromoCode($product, $promoCode)) { + $product->setOffSaleReason(__('Product is hidden without promo code')); + $hidden = true; + } + + if ($product->isSoldOut() && $product->getHideWhenSoldOut()) { + $product->setOffSaleReason(__('Product is sold out')); + $hidden = true; + } + + if ($product->isBeforeSaleStartDate() && $product->getHideBeforeSaleStartDate()) { + $product->setOffSaleReason(__('Product is before sale start date')); + $hidden = true; + } + + if ($product->isAfterSaleEndDate() && $product->getHideAfterSaleEndDate()) { + $product->setOffSaleReason(__('Product is after sale end date')); + $hidden = true; + } + + if ($product->getIsHidden()) { + $product->setOffSaleReason(__('Product is hidden')); + $hidden = true; + } + + return $hidden && $hideSoldOutProducts; + } + + private function processProductPrice(ProductDomainObject $product, ProductPriceDomainObject $price): void + { + $taxAndFees = $this->taxCalculationService + ->calculateTaxAndFeesForProductPrice($product, $price); + + $price + ->setTaxTotal(Currency::round($taxAndFees->taxTotal)) + ->setFeeTotal(Currency::round($taxAndFees->feeTotal)); + + $price->setIsAvailable($this->getPriceAvailability($price, $product)); + } + + private function filterProductPrice( + ProductDomainObject $product, + ProductPriceDomainObject $price, + bool $hideSoldOutProducts = true + ): bool + { + $hidden = false; + + if (!$product->isTieredType()) { + return false; + } + + if ($price->isBeforeSaleStartDate() && $product->getHideBeforeSaleStartDate()) { + $price->setOffSaleReason(__('Price is before sale start date')); + $hidden = true; + } + + if ($price->isAfterSaleEndDate() && $product->getHideAfterSaleEndDate()) { + $price->setOffSaleReason(__('Price is after sale end date')); + $hidden = true; + } + + if ($price->isSoldOut() && $product->getHideWhenSoldOut()) { + $price->setOffSaleReason(__('Price is sold out')); + $hidden = true; + } + + if ($price->getIsHidden()) { + $price->setOffSaleReason(__('Price is hidden')); + $hidden = true; + } + + return $hidden && $hideSoldOutProducts; + } + + private function processProductPrices(ProductDomainObject $product, bool $hideSoldOutProducts = true): void + { + $product->setProductPrices( + $product->getProductPrices() + ?->each(fn(ProductPriceDomainObject $price) => $this->processProductPrice($product, $price)) + ->reject(fn(ProductPriceDomainObject $price) => $this->filterProductPrice($product, $price, $hideSoldOutProducts)) + ); + } + + /** + * For non-tiered products, we can inherit the availability of the product. + * + * @param ProductPriceDomainObject $price + * @param ProductDomainObject $product + * @return bool + */ + private function getPriceAvailability(ProductPriceDomainObject $price, ProductDomainObject $product): bool + { + if ($product->isTieredType()) { + return !$price->isSoldOut() + && !$price->isBeforeSaleStartDate() + && !$price->isAfterSaleEndDate() + && !$price->getIsHidden(); + } + + return !$product->isSoldOut() + && !$product->isBeforeSaleStartDate() + && !$product->isAfterSaleEndDate() + && !$product->getIsHidden(); + } +} diff --git a/backend/app/Services/Domain/Ticket/TicketPriceCreateService.php b/backend/app/Services/Domain/Product/ProductPriceCreateService.php similarity index 66% rename from backend/app/Services/Domain/Ticket/TicketPriceCreateService.php rename to backend/app/Services/Domain/Product/ProductPriceCreateService.php index bff6de6dc7..57fae4a659 100644 --- a/backend/app/Services/Domain/Ticket/TicketPriceCreateService.php +++ b/backend/app/Services/Domain/Product/ProductPriceCreateService.php @@ -1,29 +1,29 @@ map(fn(TicketPriceDomainObject $price, int $index) => $this->ticketPriceRepository->create([ - 'ticket_id' => $ticketId, + return (new Collection($prices->map(fn(ProductPriceDomainObject $price, int $index) => $this->productPriceRepository->create([ + 'product_id' => $productId, 'price' => $price->getPrice(), 'label' => $price->getLabel(), 'sale_start_date' => $price->getSaleStartDate() diff --git a/backend/app/Services/Domain/Product/ProductPriceService.php b/backend/app/Services/Domain/Product/ProductPriceService.php new file mode 100644 index 0000000000..aa29d65503 --- /dev/null +++ b/backend/app/Services/Domain/Product/ProductPriceService.php @@ -0,0 +1,77 @@ +getPrice($product, new OrderProductPriceDTO( + quantity: 1, + price_id: $price->getId(), + ), $promoCode)->price; + } + + public function getPrice( + ProductDomainObject $product, + OrderProductPriceDTO $productOrderDetail, + ?PromoCodeDomainObject $promoCode + ): PriceDTO + { + $price = $this->determineProductPrice($product, $productOrderDetail); + + if ($product->getType() === ProductPriceType::FREE->name) { + return new PriceDTO(0.00); + } + + if ($product->getType() === ProductPriceType::DONATION->name) { + return new PriceDTO($price); + } + + if (!$promoCode || !$promoCode->appliesToProduct($product)) { + return new PriceDTO($price); + } + + if ($promoCode->getDiscountType() === PromoCodeDiscountTypeEnum::NONE->name) { + return new PriceDTO($price); + } + + if ($promoCode->isFixedDiscount()) { + $discountPrice = Currency::round($price - $promoCode->getDiscount()); + } elseif ($promoCode->isPercentageDiscount()) { + $discountPrice = Currency::round( + $price - ($price * ($promoCode->getDiscount() / 100)) + ); + } else { + $discountPrice = $price; + } + + return new PriceDTO( + price: max(0, $discountPrice), + price_before_discount: $price + ); + } + + private function determineProductPrice(ProductDomainObject $product, OrderProductPriceDTO $productOrderDetails): float + { + return match ($product->getType()) { + ProductPriceType::DONATION->name => max($product->getPrice(), $productOrderDetails->price), + ProductPriceType::PAID->name => $product->getPrice(), + ProductPriceType::FREE->name => 0.00, + ProductPriceType::TIERED->name => $product->getPriceById($productOrderDetails->price_id)?->getPrice() + }; + } +} diff --git a/backend/app/Services/Domain/Ticket/TicketPriceUpdateService.php b/backend/app/Services/Domain/Product/ProductPriceUpdateService.php similarity index 62% rename from backend/app/Services/Domain/Ticket/TicketPriceUpdateService.php rename to backend/app/Services/Domain/Product/ProductPriceUpdateService.php index f2bef504e6..02bcc08a4d 100644 --- a/backend/app/Services/Domain/Ticket/TicketPriceUpdateService.php +++ b/backend/app/Services/Domain/Product/ProductPriceUpdateService.php @@ -1,22 +1,22 @@ $existingPrices */ - Collection $existingPrices, - EventDomainObject $event, + ProductDomainObject $product, + UpsertProductDTO $productsData, + /** @var Collection $existingPrices */ + Collection $existingPrices, + EventDomainObject $event, ): void { - if ($ticketsData->type !== TicketType::TIERED) { - $prices = new Collection([new TicketPriceDTO( - price: $ticketsData->type === TicketType::FREE ? 0.00 : $ticketsData->prices->first()->price, + if ($productsData->type !== ProductPriceType::TIERED) { + $prices = new Collection([new ProductPriceDTO( + price: $productsData->type === ProductPriceType::FREE ? 0.00 : $productsData->prices->first()->price, label: null, sale_start_date: null, sale_end_date: null, - initial_quantity_available: $ticketsData->prices->first()->initial_quantity_available, + initial_quantity_available: $productsData->prices->first()->initial_quantity_available, id: $existingPrices->first()->getId(), )]); } else { - $prices = $ticketsData->prices; + $prices = $productsData->prices; } $order = 1; foreach ($prices as $price) { if ($price->id === null) { - $this->ticketPriceRepository->create([ - 'ticket_id' => $ticket->getId(), + $this->productPriceRepository->create([ + 'product_id' => $product->getId(), 'price' => $price->price, 'label' => $price->label, 'sale_start_date' => $price->sale_start_date @@ -64,8 +64,8 @@ public function updatePrices( 'order' => $order++, ]); } else { - $this->ticketPriceRepository->updateWhere([ - 'ticket_id' => $ticket->getId(), + $this->productPriceRepository->updateWhere([ + 'product_id' => $product->getId(), 'price' => $price->price, 'label' => $price->label, 'sale_start_date' => $price->sale_start_date @@ -93,16 +93,16 @@ private function deletePrices(?Collection $prices, Collection $existingPrices): { $pricesIds = $prices?->map(fn($price) => $price->id)->toArray(); - $existingPrices->each(function (TicketPriceDomainObject $price) use ($pricesIds) { + $existingPrices->each(function (ProductPriceDomainObject $price) use ($pricesIds) { if (in_array($price->getId(), $pricesIds)) { return; } if ($price->getQuantitySold() > 0) { throw new CannotDeleteEntityException( - __('Cannot delete ticket price with id :id because it has sales', ['id' => $price->getId()]) + __('Cannot delete product price with id :id because it has sales', ['id' => $price->getId()]) ); } - $this->ticketPriceRepository->deleteById($price->getId()); + $this->productPriceRepository->deleteById($price->getId()); }); } } diff --git a/backend/app/Services/Domain/Ticket/TicketQuantityUpdateService.php b/backend/app/Services/Domain/Product/ProductQuantityUpdateService.php similarity index 80% rename from backend/app/Services/Domain/Ticket/TicketQuantityUpdateService.php rename to backend/app/Services/Domain/Product/ProductQuantityUpdateService.php index edfe240298..3dc6a09fae 100644 --- a/backend/app/Services/Domain/Ticket/TicketQuantityUpdateService.php +++ b/backend/app/Services/Domain/Product/ProductQuantityUpdateService.php @@ -1,24 +1,24 @@ increaseCapacityAssignmentUsedCapacity($capacityAssignment->getId(), $adjustment); }); - $this->ticketPriceRepository->updateWhere([ + $this->productPriceRepository->updateWhere([ 'quantity_sold' => DB::raw('quantity_sold + ' . $adjustment), ], [ 'id' => $priceId, @@ -51,7 +51,7 @@ public function decreaseQuantitySold(int $priceId, int $adjustment = 1): void $this->decreaseCapacityAssignmentUsedCapacity($capacityAssignment->getId(), $adjustment); }); - $this->ticketPriceRepository->updateWhere([ + $this->productPriceRepository->updateWhere([ 'quantity_sold' => DB::raw('quantity_sold - ' . $adjustment), ], [ 'id' => $priceId, @@ -69,7 +69,7 @@ public function updateQuantitiesFromOrder(OrderDomainObject $order): void throw new InvalidArgumentException(__('Order has no order items')); } - $this->updateTicketQuantities($order); + $this->updateProductQuantities($order); }); } @@ -77,11 +77,11 @@ public function updateQuantitiesFromOrder(OrderDomainObject $order): void * @param OrderDomainObject $order * @return void */ - private function updateTicketQuantities(OrderDomainObject $order): void + private function updateProductQuantities(OrderDomainObject $order): void { /** @var OrderItemDomainObject $orderItem */ foreach ($order->getOrderItems() as $orderItem) { - $this->increaseQuantitySold($orderItem->getTicketPriceId(), $orderItem->getQuantity()); + $this->increaseQuantitySold($orderItem->getProductPriceId(), $orderItem->getQuantity()); } } @@ -109,10 +109,10 @@ private function decreaseCapacityAssignmentUsedCapacity(int $capacityAssignmentI */ private function getCapacityAssignments(int $priceId): Collection { - $price = $this->ticketPriceRepository->findFirstWhere([ + $price = $this->productPriceRepository->findFirstWhere([ 'id' => $priceId, ]); - return $this->ticketRepository->getCapacityAssignmentsByTicketId($price->getTicketId()); + return $this->productRepository->getCapacityAssignmentsByProductId($price->getProductId()); } } diff --git a/backend/app/Services/Domain/PromoCode/CreatePromoCodeService.php b/backend/app/Services/Domain/PromoCode/CreatePromoCodeService.php index 9e5609ce49..d8cdaa5ec5 100644 --- a/backend/app/Services/Domain/PromoCode/CreatePromoCodeService.php +++ b/backend/app/Services/Domain/PromoCode/CreatePromoCodeService.php @@ -9,30 +9,30 @@ use HiEvents\Helper\DateHelper; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface; -use HiEvents\Services\Domain\Ticket\EventTicketValidationService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\EventProductValidationService; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; class CreatePromoCodeService { public function __construct( - private readonly PromoCodeRepositoryInterface $promoCodeRepository, - private readonly EventTicketValidationService $eventTicketValidationService, - private readonly EventRepositoryInterface $eventRepository, + private readonly PromoCodeRepositoryInterface $promoCodeRepository, + private readonly EventProductValidationService $eventProductValidationService, + private readonly EventRepositoryInterface $eventRepository, ) { } /** * @throws ResourceConflictException - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function createPromoCode(PromoCodeDomainObject $promoCode): PromoCodeDomainObject { $this->checkForDuplicateCode($promoCode); - if (!empty($promoCode->getApplicableTicketIds())) { - $this->eventTicketValidationService->validateTicketIds( - ticketIds: $promoCode->getApplicableTicketIds(), + if (!empty($promoCode->getApplicableProductIds())) { + $this->eventProductValidationService->validateProductIds( + productIds: $promoCode->getApplicableProductIds(), eventId: $promoCode->getEventId() ); } @@ -50,7 +50,7 @@ public function createPromoCode(PromoCodeDomainObject $promoCode): PromoCodeDoma ? DateHelper::convertToUTC($promoCode->getExpiryDate(), $event->getTimezone()) : null, PromoCodeDomainObjectAbstract::MAX_ALLOWED_USAGES => $promoCode->getMaxAllowedUsages(), - PromoCodeDomainObjectAbstract::APPLICABLE_TICKET_IDS => $promoCode->getApplicableTicketIds(), + PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS => $promoCode->getApplicableProductIds(), ]); } diff --git a/backend/app/Services/Domain/Question/CreateQuestionService.php b/backend/app/Services/Domain/Question/CreateQuestionService.php index c50672deb4..42ef5ee6e7 100644 --- a/backend/app/Services/Domain/Question/CreateQuestionService.php +++ b/backend/app/Services/Domain/Question/CreateQuestionService.php @@ -24,7 +24,7 @@ public function __construct( */ public function createQuestion( QuestionDomainObject $question, - array $ticketIds, + array $productIds, ): QuestionDomainObject { return $this->databaseManager->transaction(fn() => $this->questionRepository->create([ @@ -36,6 +36,6 @@ public function createQuestion( QuestionDomainObjectAbstract::OPTIONS => $question->getOptions(), QuestionDomainObjectAbstract::IS_HIDDEN => $question->getIsHidden(), QuestionDomainObjectAbstract::DESCRIPTION => $this->purifier->purify($question->getDescription()), - ], $ticketIds)); + ], $productIds)); } } diff --git a/backend/app/Services/Domain/Question/EditQuestionService.php b/backend/app/Services/Domain/Question/EditQuestionService.php index 1c29a8d3c5..784fe93162 100644 --- a/backend/app/Services/Domain/Question/EditQuestionService.php +++ b/backend/app/Services/Domain/Question/EditQuestionService.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\Generated\QuestionDomainObjectAbstract; use HiEvents\DomainObjects\QuestionDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; use HTMLPurifier; use Illuminate\Database\DatabaseManager; @@ -25,10 +25,10 @@ public function __construct( */ public function editQuestion( QuestionDomainObject $question, - array $ticketIds, + array $productIds, ): QuestionDomainObject { - return $this->databaseManager->transaction(function () use ($question, $ticketIds) { + return $this->databaseManager->transaction(function () use ($question, $productIds) { $this->questionRepository->updateQuestion( questionId: $question->getId(), eventId: $question->getEventId(), @@ -42,11 +42,11 @@ public function editQuestion( QuestionDomainObjectAbstract::IS_HIDDEN => $question->getIsHidden(), QuestionDomainObjectAbstract::DESCRIPTION => $this->purifier->purify($question->getDescription()), ], - ticketIds: $ticketIds + productIds: $productIds ); return $this->questionRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findById($question->getId()); }); } diff --git a/backend/app/Services/Domain/Tax/DTO/TaxAndTicketAssociateParams.php b/backend/app/Services/Domain/Tax/DTO/TaxAndProductAssociateParams.php similarity index 71% rename from backend/app/Services/Domain/Tax/DTO/TaxAndTicketAssociateParams.php rename to backend/app/Services/Domain/Tax/DTO/TaxAndProductAssociateParams.php index cd645e87b9..f3b2b5fa70 100644 --- a/backend/app/Services/Domain/Tax/DTO/TaxAndTicketAssociateParams.php +++ b/backend/app/Services/Domain/Tax/DTO/TaxAndProductAssociateParams.php @@ -2,10 +2,10 @@ namespace HiEvents\Services\Domain\Tax\DTO; -class TaxAndTicketAssociateParams +class TaxAndProductAssociateParams { public function __construct( - public readonly int $ticketId, + public readonly int $productId, public readonly int $accountId, public readonly array $taxAndFeeIds, ) diff --git a/backend/app/Services/Domain/Tax/TaxAndFeeCalculationService.php b/backend/app/Services/Domain/Tax/TaxAndFeeCalculationService.php index 641ad7d9a8..f3343269bf 100644 --- a/backend/app/Services/Domain/Tax/TaxAndFeeCalculationService.php +++ b/backend/app/Services/Domain/Tax/TaxAndFeeCalculationService.php @@ -4,8 +4,8 @@ use HiEvents\DomainObjects\Enums\TaxCalculationType; use HiEvents\DomainObjects\TaxAndFeesDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Services\Domain\Tax\DTO\TaxCalculationResponse; use InvalidArgumentException; @@ -18,26 +18,26 @@ public function __construct(TaxAndFeeRollupService $taxRollupService) $this->taxRollupService = $taxRollupService; } - public function calculateTaxAndFeesForTicketPrice( - TicketDomainObject $ticket, - TicketPriceDomainObject $price, + public function calculateTaxAndFeesForProductPrice( + ProductDomainObject $product, + ProductPriceDomainObject $price, ): TaxCalculationResponse { - return $this->calculateTaxAndFeesForTicket($ticket, $price->getPrice()); + return $this->calculateTaxAndFeesForProduct($product, $price->getPrice()); } - public function calculateTaxAndFeesForTicket( - TicketDomainObject $ticket, - float $price, - int $quantity = 1 + public function calculateTaxAndFeesForProduct( + ProductDomainObject $product, + float $price, + int $quantity = 1 ): TaxCalculationResponse { $this->taxRollupService->resetRollUp(); - $fees = $ticket->getFees() + $fees = $product->getFees() ?->sum(fn($taxOrFee) => $this->calculateFee($taxOrFee, $price, $quantity)) ?: 0.00; - $taxFees = $ticket->getTaxRates() + $taxFees = $product->getTaxRates() ?->sum(fn($taxOrFee) => $this->calculateFee($taxOrFee, $price + $fees, $quantity)); return new TaxCalculationResponse( diff --git a/backend/app/Services/Domain/Tax/TaxAndTicketAssociationService.php b/backend/app/Services/Domain/Tax/TaxAndProductAssociationService.php similarity index 66% rename from backend/app/Services/Domain/Tax/TaxAndTicketAssociationService.php rename to backend/app/Services/Domain/Tax/TaxAndProductAssociationService.php index f59431faa3..05527137ee 100644 --- a/backend/app/Services/Domain/Tax/TaxAndTicketAssociationService.php +++ b/backend/app/Services/Domain/Tax/TaxAndProductAssociationService.php @@ -4,16 +4,16 @@ use Exception; use HiEvents\Exceptions\InvalidTaxOrFeeIdException; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Repository\Interfaces\TaxAndFeeRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; -use HiEvents\Services\Domain\Tax\DTO\TaxAndTicketAssociateParams; +use HiEvents\Services\Domain\Tax\DTO\TaxAndProductAssociateParams; use Illuminate\Support\Collection; -readonly class TaxAndTicketAssociationService +readonly class TaxAndProductAssociationService { public function __construct( private TaxAndFeeRepositoryInterface $taxAndFeeRepository, - private TicketRepositoryInterface $ticketRepository, + private ProductRepositoryInterface $ticketRepository, ) { } @@ -21,7 +21,7 @@ public function __construct( /** * @throws Exception */ - public function addTaxesToTicket(TaxAndTicketAssociateParams $params): Collection + public function addTaxesToProduct(TaxAndProductAssociateParams $params): Collection { $taxesAndFees = $this->taxAndFeeRepository->findWhereIn( field: 'id', @@ -36,7 +36,7 @@ public function addTaxesToTicket(TaxAndTicketAssociateParams $params): Collectio throw new InvalidTaxOrFeeIdException(__('One or more tax IDs are invalid')); } - $this->ticketRepository->addTaxesAndFeesToTicket($params->ticketId, $params->taxAndFeeIds); + $this->ticketRepository->addTaxesAndFeesToProduct($params->productId, $params->taxAndFeeIds); return $taxesAndFees; } diff --git a/backend/app/Services/Domain/Ticket/AvailableTicketQuantitiesFetchService.php b/backend/app/Services/Domain/Ticket/AvailableTicketQuantitiesFetchService.php deleted file mode 100644 index e3cca4ac33..0000000000 --- a/backend/app/Services/Domain/Ticket/AvailableTicketQuantitiesFetchService.php +++ /dev/null @@ -1,173 +0,0 @@ -config->get('app.homepage_ticket_quantities_cache_ttl')) { - $cachedData = $this->getDataFromCache($eventId); - if ($cachedData) { - return $cachedData; - } - } - - $capacities = $this->capacityAssignmentRepository - ->loadRelation(TicketDomainObject::class) - ->findWhere([ - 'event_id' => $eventId, - 'applies_to' => CapacityAssignmentAppliesTo::TICKETS->name, - 'status' => CapacityAssignmentStatus::ACTIVE->name, - ]); - - $reservedTicketQuantities = $this->fetchReservedTicketQuantities($eventId); - $ticketCapacities = $this->calculateTicketCapacities($capacities); - - $quantities = $reservedTicketQuantities->map(function (AvailableTicketQuantitiesDTO $dto) use ($ticketCapacities) { - $ticketId = $dto->ticket_id; - if (isset($ticketCapacities[$ticketId])) { - $dto->quantity_available = min(array_merge([$dto->quantity_available], $ticketCapacities[$ticketId]->map->getAvailableCapacity()->toArray())); - $dto->capacities = $ticketCapacities[$ticketId]; - } - - return $dto; - }); - - $finalData = new AvailableTicketQuantitiesResponseDTO( - ticketQuantities: $quantities, - capacities: $capacities - ); - - if (!$ignoreCache && $this->config->get('app.homepage_ticket_quantities_cache_ttl')) { - $this->cache->put($this->getCacheKey($eventId), $finalData, $this->config->get('app.homepage_ticket_quantities_cache_ttl')); - } - - return $finalData; - } - - private function fetchReservedTicketQuantities(int $eventId): Collection - { - $result = $this->db->select(<< NOW() - AND orders.deleted_at IS NULL - THEN order_items.quantity - ELSE 0 - END - ) AS quantity_reserved - FROM tickets - JOIN ticket_prices ON tickets.id = ticket_prices.ticket_id - LEFT JOIN order_items ON order_items.ticket_id = tickets.id - AND order_items.ticket_price_id = ticket_prices.id - LEFT JOIN orders ON orders.id = order_items.order_id - AND orders.event_id = tickets.event_id - AND orders.deleted_at IS NULL - WHERE - tickets.event_id = :eventId - AND tickets.deleted_at IS NULL - AND ticket_prices.deleted_at IS NULL - GROUP BY tickets.id, ticket_prices.id - ) - SELECT - tickets.id AS ticket_id, - ticket_prices.id AS ticket_price_id, - tickets.title AS ticket_title, - ticket_prices.label AS price_label, - ticket_prices.initial_quantity_available, - ticket_prices.quantity_sold, - COALESCE( - ticket_prices.initial_quantity_available - - ticket_prices.quantity_sold - - COALESCE(reserved_quantities.quantity_reserved, 0), - 0) AS quantity_available, - COALESCE(reserved_quantities.quantity_reserved, 0) AS quantity_reserved, - CASE WHEN ticket_prices.initial_quantity_available IS NULL - THEN TRUE - ELSE FALSE - END AS unlimited_quantity_available - FROM tickets - JOIN ticket_prices ON tickets.id = ticket_prices.ticket_id - LEFT JOIN reserved_quantities ON tickets.id = reserved_quantities.ticket_id - AND ticket_prices.id = reserved_quantities.ticket_price_id - WHERE - tickets.event_id = :eventId - AND tickets.deleted_at IS NULL - AND ticket_prices.deleted_at IS NULL - GROUP BY tickets.id, ticket_prices.id, reserved_quantities.quantity_reserved; - SQL, [ - 'eventId' => $eventId, - 'reserved' => OrderStatus::RESERVED->name - ]); - - return collect($result)->map(fn($row) => AvailableTicketQuantitiesDTO::fromArray([ - 'ticket_id' => $row->ticket_id, - 'price_id' => $row->ticket_price_id, - 'ticket_title' => $row->ticket_title, - 'price_label' => $row->price_label, - 'quantity_available' => $row->unlimited_quantity_available ? Constants::INFINITE : $row->quantity_available, - 'initial_quantity_available' => $row->initial_quantity_available, - 'quantity_reserved' => $row->quantity_reserved, - 'capacities' => new Collection(), - ])); - } - - /** - * @param Collection $capacities - */ - private function calculateTicketCapacities(Collection $capacities): array - { - $ticketCapacities = []; - foreach ($capacities as $capacity) { - foreach ($capacity->getTickets() as $ticket) { - $ticketId = $ticket->getId(); - if (!isset($ticketCapacities[$ticketId])) { - $ticketCapacities[$ticketId] = collect(); - } - - $ticketCapacities[$ticketId]->push($capacity); - } - } - - return $ticketCapacities; - } - - private function getDataFromCache(int $eventId): ?AvailableTicketQuantitiesResponseDTO - { - return $this->cache->get($this->getCacheKey($eventId)); - } - - private function getCacheKey(int $eventId): string - { - return "event.$eventId.available_ticket_quantities"; - } -} diff --git a/backend/app/Services/Domain/Ticket/CreateTicketService.php b/backend/app/Services/Domain/Ticket/CreateTicketService.php deleted file mode 100644 index 065e9b781a..0000000000 --- a/backend/app/Services/Domain/Ticket/CreateTicketService.php +++ /dev/null @@ -1,117 +0,0 @@ -databaseManager->transaction(function () use ($accountId, $taxAndFeeIds, $ticket) { - $persistedTicket = $this->persistTicket($ticket); - - if ($taxAndFeeIds) { - $this->associateTaxesAndFees($persistedTicket, $taxAndFeeIds, $accountId); - } - - return $this->createTicketPrices($persistedTicket, $ticket); - }); - } - - private function persistTicket(TicketDomainObject $ticketsData): TicketDomainObject - { - $event = $this->eventRepository->findById($ticketsData->getEventId()); - - return $this->ticketRepository->create([ - 'title' => $ticketsData->getTitle(), - 'type' => $ticketsData->getType(), - 'order' => $ticketsData->getOrder(), - 'sale_start_date' => $ticketsData->getSaleStartDate() - ? DateHelper::convertToUTC($ticketsData->getSaleStartDate(), $event->getTimezone()) - : null, - 'sale_end_date' => $ticketsData->getSaleEndDate() - ? DateHelper::convertToUTC($ticketsData->getSaleEndDate(), $event->getTimezone()) - : null, - 'max_per_order' => $ticketsData->getMaxPerOrder(), - 'description' => $this->purifier->purify($ticketsData->getDescription()), - 'min_per_order' => $ticketsData->getMinPerOrder(), - 'is_hidden' => $ticketsData->getIsHidden(), - 'hide_before_sale_start_date' => $ticketsData->getHideBeforeSaleStartDate(), - 'hide_after_sale_end_date' => $ticketsData->getHideAfterSaleEndDate(), - 'hide_when_sold_out' => $ticketsData->getHideWhenSoldOut(), - 'show_quantity_remaining' => $ticketsData->getShowQuantityRemaining(), - 'is_hidden_without_promo_code' => $ticketsData->getIsHiddenWithoutPromoCode(), - 'event_id' => $ticketsData->getEventId(), - ]); - } - - /** - * @throws Exception - */ - private function createTicketTaxesAndFees( - TicketDomainObject $ticket, - array $taxAndFeeIds, - int $accountId, - ): Collection - { - return $this->taxAndTicketAssociationService->addTaxesToTicket( - new TaxAndTicketAssociateParams( - ticketId: $ticket->getId(), - accountId: $accountId, - taxAndFeeIds: $taxAndFeeIds, - ), - ); - } - - /** - * @throws Exception - */ - private function associateTaxesAndFees(TicketDomainObject $persistedTicket, array $taxAndFeeIds, int $accountId): void - { - $persistedTicket->setTaxAndFees($this->createTicketTaxesAndFees( - ticket: $persistedTicket, - taxAndFeeIds: $taxAndFeeIds, - accountId: $accountId, - )); - } - - private function createTicketPrices(TicketDomainObject $persistedTicket, TicketDomainObject $ticket): TicketDomainObject - { - $prices = $this->priceCreateService->createPrices( - ticketId: $persistedTicket->getId(), - prices: $ticket->getTicketPrices(), - event: $this->eventRepository->findById($ticket->getEventId()), - ); - - return $persistedTicket->setTicketPrices($prices); - } -} diff --git a/backend/app/Services/Domain/Ticket/DTO/CreateTicketDTO.php b/backend/app/Services/Domain/Ticket/DTO/CreateTicketDTO.php deleted file mode 100644 index d2008ba496..0000000000 --- a/backend/app/Services/Domain/Ticket/DTO/CreateTicketDTO.php +++ /dev/null @@ -1,10 +0,0 @@ -ticketRepository->findWhere([ - 'event_id' => $eventId, - ])->map(fn(TicketDomainObject $ticket) => $ticket->getId()) - ->toArray(); - - $invalidTicketIds = array_diff($ticketIds, $validTicketIds); - - if (!empty($invalidTicketIds)) { - throw new UnrecognizedTicketIdException( - __('Invalid ticket ids: :ids', ['ids' => implode(', ', $invalidTicketIds)]) - ); - } - } -} diff --git a/backend/app/Services/Domain/Ticket/Exception/UnrecognizedTicketIdException.php b/backend/app/Services/Domain/Ticket/Exception/UnrecognizedTicketIdException.php deleted file mode 100644 index ed1ae38f98..0000000000 --- a/backend/app/Services/Domain/Ticket/Exception/UnrecognizedTicketIdException.php +++ /dev/null @@ -1,10 +0,0 @@ - $tickets - * @param PromoCodeDomainObject|null $promoCode - * @param bool $hideSoldOutTickets - * @return Collection - */ - public function filter( - Collection $tickets, - ?PromoCodeDomainObject $promoCode = null, - bool $hideSoldOutTickets = true, - ): Collection - { - if ($tickets->isEmpty()) { - return $tickets; - } - - $ticketQuantities = $this - ->fetchAvailableTicketQuantitiesService - ->getAvailableTicketQuantities($tickets->first()->getEventId()); - - return $tickets - ->map(fn(TicketDomainObject $ticket) => $this->processTicket($ticket, $ticketQuantities->ticketQuantities, $promoCode)) - ->reject(fn(TicketDomainObject $ticket) => $this->filterTicket($ticket, $promoCode, $hideSoldOutTickets)) - ->each(fn(TicketDomainObject $ticket) => $this->processTicketPrices($ticket, $hideSoldOutTickets)); - } - - private function isHiddenByPromoCode(TicketDomainObject $ticket, ?PromoCodeDomainObject $promoCode): bool - { - return $ticket->getIsHiddenWithoutPromoCode() && !( - $promoCode - && $promoCode->appliesToTicket($ticket) - ); - } - - private function shouldTicketBeDiscounted(?PromoCodeDomainObject $promoCode, TicketDomainObject $ticket): bool - { - if ($ticket->isDonationType() || $ticket->isFreeType()) { - return false; - } - - return $promoCode - && $promoCode->isDiscountCode() - && $promoCode->appliesToTicket($ticket); - } - - /** - * @param PromoCodeDomainObject|null $promoCode - * @param TicketDomainObject $ticket - * @param Collection $ticketQuantities - * @return TicketDomainObject - */ - private function processTicket( - TicketDomainObject $ticket, - Collection $ticketQuantities, - ?PromoCodeDomainObject $promoCode = null, - ): TicketDomainObject - { - if ($this->shouldTicketBeDiscounted($promoCode, $ticket)) { - $ticket->getTicketPrices()?->each(function (TicketPriceDomainObject $price) use ($ticket, $promoCode) { - $price->setPriceBeforeDiscount($price->getPrice()); - $price->setPrice($this->ticketPriceService->getIndividualPrice($ticket, $price, $promoCode)); - }); - } - - $ticket->getTicketPrices()?->map(function (TicketPriceDomainObject $price) use ($ticketQuantities) { - $availableQuantity = $ticketQuantities->where('price_id', $price->getId())->first()?->quantity_available; - $availableQuantity = $availableQuantity === Constants::INFINITE ? null : $availableQuantity; - $price->setQuantityAvailable( - max($availableQuantity, 0) - ); - }); - - // If there is a capacity assigned to the ticket, we set the capacity to capacity available qty, or the sum of all - // ticket prices qty, whichever is lower - $ticketQuantities->each(function (AvailableTicketQuantitiesDTO $quantity) use ($ticket) { - if ($quantity->capacities !== null && $quantity->capacities->isNotEmpty() && $quantity->ticket_id === $ticket->getId()) { - $ticket->setQuantityAvailable( - $quantity->capacities->min(fn(CapacityAssignmentDomainObject $capacity) => $capacity->getAvailableCapacity()) - ); - } - }); - - return $ticket; - } - - private function filterTicket( - TicketDomainObject $ticket, - ?PromoCodeDomainObject $promoCode = null, - bool $hideSoldOutTickets = true, - ): bool - { - $hidden = false; - - if ($this->isHiddenByPromoCode($ticket, $promoCode)) { - $ticket->setOffSaleReason(__('Ticket is hidden without promo code')); - $hidden = true; - } - - if ($ticket->isSoldOut() && $ticket->getHideWhenSoldOut()) { - $ticket->setOffSaleReason(__('Ticket is sold out')); - $hidden = true; - } - - if ($ticket->isBeforeSaleStartDate() && $ticket->getHideBeforeSaleStartDate()) { - $ticket->setOffSaleReason(__('Ticket is before sale start date')); - $hidden = true; - } - - if ($ticket->isAfterSaleEndDate() && $ticket->getHideAfterSaleEndDate()) { - $ticket->setOffSaleReason(__('Ticket is after sale end date')); - $hidden = true; - } - - if ($ticket->getIsHidden()) { - $ticket->setOffSaleReason(__('Ticket is hidden')); - $hidden = true; - } - - return $hidden && $hideSoldOutTickets; - } - - private function processTicketPrice(TicketDomainObject $ticket, TicketPriceDomainObject $price): void - { - $taxAndFees = $this->taxCalculationService - ->calculateTaxAndFeesForTicketPrice($ticket, $price); - - $price - ->setTaxTotal(Currency::round($taxAndFees->taxTotal)) - ->setFeeTotal(Currency::round($taxAndFees->feeTotal)); - - $price->setIsAvailable($this->getPriceAvailability($price, $ticket)); - } - - private function filterTicketPrice( - TicketDomainObject $ticket, - TicketPriceDomainObject $price, - bool $hideSoldOutTickets = true - ): bool - { - $hidden = false; - - if (!$ticket->isTieredType()) { - return false; - } - - if ($price->isBeforeSaleStartDate() && $ticket->getHideBeforeSaleStartDate()) { - $price->setOffSaleReason(__('Price is before sale start date')); - $hidden = true; - } - - if ($price->isAfterSaleEndDate() && $ticket->getHideAfterSaleEndDate()) { - $price->setOffSaleReason(__('Price is after sale end date')); - $hidden = true; - } - - if ($price->isSoldOut() && $ticket->getHideWhenSoldOut()) { - $price->setOffSaleReason(__('Price is sold out')); - $hidden = true; - } - - if ($price->getIsHidden()) { - $price->setOffSaleReason(__('Price is hidden')); - $hidden = true; - } - - return $hidden && $hideSoldOutTickets; - } - - private function processTicketPrices(TicketDomainObject $ticket, bool $hideSoldOutTickets = true): void - { - $ticket->setTicketPrices( - $ticket->getTicketPrices() - ?->each(fn(TicketPriceDomainObject $price) => $this->processTicketPrice($ticket, $price)) - ->reject(fn(TicketPriceDomainObject $price) => $this->filterTicketPrice($ticket, $price, $hideSoldOutTickets)) - ); - } - - /** - * For non-tiered tickets, we can inherit the availability of the ticket. - * - * @param TicketPriceDomainObject $price - * @param TicketDomainObject $ticket - * @return bool - */ - private function getPriceAvailability(TicketPriceDomainObject $price, TicketDomainObject $ticket): bool - { - if ($ticket->isTieredType()) { - return !$price->isSoldOut() - && !$price->isBeforeSaleStartDate() - && !$price->isAfterSaleEndDate() - && !$price->getIsHidden(); - } - - return !$ticket->isSoldOut() - && !$ticket->isBeforeSaleStartDate() - && !$ticket->isAfterSaleEndDate() - && !$ticket->getIsHidden(); - } -} diff --git a/backend/app/Services/Domain/Ticket/TicketPriceService.php b/backend/app/Services/Domain/Ticket/TicketPriceService.php deleted file mode 100644 index b2d652f160..0000000000 --- a/backend/app/Services/Domain/Ticket/TicketPriceService.php +++ /dev/null @@ -1,77 +0,0 @@ -getPrice($ticket, new OrderTicketPriceDTO( - quantity: 1, - price_id: $price->getId(), - ), $promoCode)->price; - } - - public function getPrice( - TicketDomainObject $ticket, - OrderTicketPriceDTO $ticketOrderDetail, - ?PromoCodeDomainObject $promoCode - ): PriceDTO - { - $price = $this->determineTicketPrice($ticket, $ticketOrderDetail); - - if ($ticket->getType() === TicketType::FREE->name) { - return new PriceDTO(0.00); - } - - if ($ticket->getType() === TicketType::DONATION->name) { - return new PriceDTO($price); - } - - if (!$promoCode || !$promoCode->appliesToTicket($ticket)) { - return new PriceDTO($price); - } - - if ($promoCode->getDiscountType() === PromoCodeDiscountTypeEnum::NONE->name) { - return new PriceDTO($price); - } - - if ($promoCode->isFixedDiscount()) { - $discountPrice = Currency::round($price - $promoCode->getDiscount()); - } elseif ($promoCode->isPercentageDiscount()) { - $discountPrice = Currency::round( - $price - ($price * ($promoCode->getDiscount() / 100)) - ); - } else { - $discountPrice = $price; - } - - return new PriceDTO( - price: max(0, $discountPrice), - price_before_discount: $price - ); - } - - private function determineTicketPrice(TicketDomainObject $ticket, OrderTicketPriceDTO $ticketOrderDetails): float - { - return match ($ticket->getType()) { - TicketType::DONATION->name => max($ticket->getPrice(), $ticketOrderDetails->price), - TicketType::PAID->name => $ticket->getPrice(), - TicketType::FREE->name => 0.00, - TicketType::TIERED->name => $ticket->getPriceById($ticketOrderDetails->price_id)?->getPrice() - }; - } -} diff --git a/backend/app/Services/Handlers/Attendee/CreateAttendeeHandler.php b/backend/app/Services/Handlers/Attendee/CreateAttendeeHandler.php index 8202210ac9..6f0dd5ac33 100644 --- a/backend/app/Services/Handlers/Attendee/CreateAttendeeHandler.php +++ b/backend/app/Services/Handlers/Attendee/CreateAttendeeHandler.php @@ -7,26 +7,26 @@ use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract; use HiEvents\DomainObjects\Generated\OrderDomainObjectAbstract; use HiEvents\DomainObjects\Generated\OrderItemDomainObjectAbstract; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; use HiEvents\DomainObjects\OrderDomainObject; use HiEvents\DomainObjects\OrderItemDomainObject; use HiEvents\DomainObjects\Status\AttendeeStatus; use HiEvents\DomainObjects\Status\OrderPaymentStatus; use HiEvents\DomainObjects\Status\OrderStatus; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Events\OrderStatusChangedEvent; -use HiEvents\Exceptions\InvalidTicketPriceId; -use HiEvents\Exceptions\NoTicketsAvailableException; +use HiEvents\Exceptions\InvalidProductPriceId; +use HiEvents\Exceptions\NoProductsAvailableException; use HiEvents\Helper\IdHelper; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; use HiEvents\Repository\Interfaces\TaxAndFeeRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Services\Domain\Order\OrderManagementService; use HiEvents\Services\Domain\Tax\TaxAndFeeRollupService; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use HiEvents\Services\Handlers\Attendee\DTO\CreateAttendeeDTO; use HiEvents\Services\Handlers\Attendee\DTO\CreateAttendeeTaxAndFeeDTO; use Illuminate\Database\DatabaseManager; @@ -39,9 +39,9 @@ class CreateAttendeeHandler public function __construct( private readonly AttendeeRepositoryInterface $attendeeRepository, private readonly OrderRepositoryInterface $orderRepository, - private readonly TicketRepositoryInterface $ticketRepository, + private readonly ProductRepositoryInterface $productRepository, private readonly EventRepositoryInterface $eventRepository, - private readonly TicketQuantityUpdateService $ticketQuantityAdjustmentService, + private readonly ProductQuantityUpdateService $productQuantityAdjustmentService, private readonly DatabaseManager $databaseManager, private readonly TaxAndFeeRepositoryInterface $taxAndFeeRepository, private readonly TaxAndFeeRollupService $taxAndFeeRollupService, @@ -51,7 +51,7 @@ public function __construct( } /** - * @throws NoTicketsAvailableException + * @throws NoProductsAvailableException * @throws Throwable */ public function handle(CreateAttendeeDTO $attendeeDTO): AttendeeDomainObject @@ -61,30 +61,30 @@ public function handle(CreateAttendeeDTO $attendeeDTO): AttendeeDomainObject $order = $this->createOrder($attendeeDTO->event_id, $attendeeDTO); - /** @var TicketDomainObject $ticket */ - $ticket = $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) + /** @var ProductDomainObject $product */ + $product = $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) ->findFirstWhere([ - TicketDomainObjectAbstract::ID => $attendeeDTO->ticket_id, - TicketDomainObjectAbstract::EVENT_ID => $attendeeDTO->event_id, + ProductDomainObjectAbstract::ID => $attendeeDTO->product_id, + ProductDomainObjectAbstract::EVENT_ID => $attendeeDTO->event_id, ]); - $availableQuantity = $this->ticketRepository->getQuantityRemainingForTicketPrice( - $attendeeDTO->ticket_id, - $attendeeDTO->ticket_price_id, + $availableQuantity = $this->productRepository->getQuantityRemainingForProductPrice( + $attendeeDTO->product_id, + $attendeeDTO->product_price_id, ); if ($availableQuantity <= 0) { - throw new NoTicketsAvailableException(__('There are no tickets available. ' . - 'If you would like to assign a ticket to this attendee,' . - ' please adjust the ticket\'s available quantity.')); + throw new NoProductsAvailableException(__('There are no products available. ' . + 'If you would like to assign a product to this attendee,' . + ' please adjust the product\'s available quantity.')); } - $ticketPriceId = $this->getTicketPriceId($attendeeDTO, $ticket); + $productPriceId = $this->getProductPriceId($attendeeDTO, $product); $this->processTaxesAndFees($attendeeDTO); - $orderItem = $this->createOrderItem($attendeeDTO, $order, $ticket, $ticketPriceId); + $orderItem = $this->createOrderItem($attendeeDTO, $order, $product, $productPriceId); $attendee = $this->createAttendee($order, $attendeeDTO); @@ -122,27 +122,27 @@ private function createOrder(int $eventId, CreateAttendeeDTO $attendeeDTO): Orde } /** - * @throws InvalidTicketPriceId + * @throws InvalidProductPriceId */ - private function getTicketPriceId(CreateAttendeeDTO $attendeeDTO, TicketDomainObject $ticket): int + private function getProductPriceId(CreateAttendeeDTO $attendeeDTO, ProductDomainObject $product): int { - $priceIds = $ticket->getTicketPrices()->map(fn(TicketPriceDomainObject $ticketPrice) => $ticketPrice->getId()); + $priceIds = $product->getProductPrices()->map(fn(ProductPriceDomainObject $productPrice) => $productPrice->getId()); - if ($attendeeDTO->ticket_price_id) { - if (!$priceIds->contains($attendeeDTO->ticket_price_id)) { - throw new InvalidTicketPriceId(__('The ticket price ID is invalid.')); + if ($attendeeDTO->product_price_id) { + if (!$priceIds->contains($attendeeDTO->product_price_id)) { + throw new InvalidProductPriceId(__('The product price ID is invalid.')); } - return $attendeeDTO->ticket_price_id; + return $attendeeDTO->product_price_id; } - /** @var TicketPriceDomainObject $ticketPrice */ - $ticketPrice = $ticket->getTicketPrices()->first(); + /** @var ProductPriceDomainObject $productPrice */ + $productPrice = $product->getProductPrices()->first(); - if ($ticketPrice) { - return $ticketPrice->getId(); + if ($productPrice) { + return $productPrice->getId(); } - throw new InvalidTicketPriceId(__('The ticket price ID is invalid.')); + throw new InvalidProductPriceId(__('The product price ID is invalid.')); } private function calculateTaxesAndFees(CreateAttendeeDTO $attendeeDTO): ?Collection @@ -186,11 +186,11 @@ private function processTaxesAndFees(CreateAttendeeDTO $attendeeDTO): void ); } - private function createOrderItem(CreateAttendeeDTO $attendeeDTO, OrderDomainObject $order, TicketDomainObject $ticket, int $ticketPriceId): OrderItemDomainObject + private function createOrderItem(CreateAttendeeDTO $attendeeDTO, OrderDomainObject $order, ProductDomainObject $product, int $productPriceId): OrderItemDomainObject { return $this->orderRepository->addOrderItem( [ - OrderItemDomainObjectAbstract::TICKET_ID => $attendeeDTO->ticket_id, + OrderItemDomainObjectAbstract::PRODUCT_ID => $attendeeDTO->product_id, OrderItemDomainObjectAbstract::QUANTITY => 1, OrderItemDomainObjectAbstract::TOTAL_BEFORE_ADDITIONS => $attendeeDTO->amount_paid, OrderItemDomainObjectAbstract::TOTAL_GROSS => $attendeeDTO->amount_paid + $this->taxAndFeeRollupService->getTotalTaxesAndFees(), @@ -198,8 +198,8 @@ private function createOrderItem(CreateAttendeeDTO $attendeeDTO, OrderDomainObje OrderItemDomainObjectAbstract::TOTAL_SERVICE_FEE => $this->taxAndFeeRollupService->getTotalFees(), OrderItemDomainObjectAbstract::PRICE => $attendeeDTO->amount_paid, OrderItemDomainObjectAbstract::ORDER_ID => $order->getId(), - OrderItemDomainObjectAbstract::ITEM_NAME => $ticket->getTitle(), - OrderItemDomainObjectAbstract::TICKET_PRICE_ID => $ticketPriceId, + OrderItemDomainObjectAbstract::ITEM_NAME => $product->getTitle(), + OrderItemDomainObjectAbstract::PRODUCT_PRICE_ID => $productPriceId, OrderItemDomainObjectAbstract::TAXES_AND_FEES_ROLLUP => $this->taxAndFeeRollupService->getRollUp(), ] ); @@ -209,8 +209,8 @@ private function createAttendee(OrderDomainObject $order, CreateAttendeeDTO $att { return $this->attendeeRepository->create([ AttendeeDomainObjectAbstract::EVENT_ID => $order->getEventId(), - AttendeeDomainObjectAbstract::TICKET_ID => $attendeeDTO->ticket_id, - AttendeeDomainObjectAbstract::TICKET_PRICE_ID => $attendeeDTO->ticket_price_id, + AttendeeDomainObjectAbstract::PRODUCT_ID => $attendeeDTO->product_id, + AttendeeDomainObjectAbstract::PRODUCT_PRICE_ID => $attendeeDTO->product_price_id, AttendeeDomainObjectAbstract::STATUS => AttendeeStatus::ACTIVE->name, AttendeeDomainObjectAbstract::EMAIL => $attendeeDTO->email, AttendeeDomainObjectAbstract::FIRST_NAME => $attendeeDTO->first_name, @@ -224,8 +224,8 @@ private function createAttendee(OrderDomainObject $order, CreateAttendeeDTO $att private function fireEventsAndUpdateQuantities(CreateAttendeeDTO $attendeeDTO, OrderDomainObject $order): void { - $this->ticketQuantityAdjustmentService->increaseQuantitySold( - priceId: $attendeeDTO->ticket_price_id, + $this->productQuantityAdjustmentService->increaseQuantitySold( + priceId: $attendeeDTO->product_price_id, ); event(new OrderStatusChangedEvent( diff --git a/backend/app/Services/Handlers/Attendee/DTO/CreateAttendeeDTO.php b/backend/app/Services/Handlers/Attendee/DTO/CreateAttendeeDTO.php index f7c852efc5..f06d9f0b2d 100644 --- a/backend/app/Services/Handlers/Attendee/DTO/CreateAttendeeDTO.php +++ b/backend/app/Services/Handlers/Attendee/DTO/CreateAttendeeDTO.php @@ -12,13 +12,13 @@ public function __construct( public readonly string $first_name, public readonly string $last_name, public readonly string $email, - public readonly int $ticket_id, + public readonly int $product_id, public readonly int $event_id, public readonly bool $send_confirmation_email, public readonly float $amount_paid, public readonly string $locale, public readonly ?bool $amount_includes_tax = false, - public readonly ?int $ticket_price_id = null, + public readonly ?int $product_price_id = null, #[CollectionOf(CreateAttendeeTaxAndFeeDTO::class)] public readonly ?Collection $taxes_and_fees = null, ) diff --git a/backend/app/Services/Handlers/Attendee/DTO/EditAttendeeDTO.php b/backend/app/Services/Handlers/Attendee/DTO/EditAttendeeDTO.php index 79b486591e..5ded58cc90 100644 --- a/backend/app/Services/Handlers/Attendee/DTO/EditAttendeeDTO.php +++ b/backend/app/Services/Handlers/Attendee/DTO/EditAttendeeDTO.php @@ -10,8 +10,8 @@ public function __construct( public string $first_name, public string $last_name, public string $email, - public int $ticket_id, - public int $ticket_price_id, + public int $product_id, + public int $product_price_id, public int $event_id, public int $attendee_id, ) diff --git a/backend/app/Services/Handlers/Attendee/EditAttendeeHandler.php b/backend/app/Services/Handlers/Attendee/EditAttendeeHandler.php index 48f3d739db..1d1257e059 100644 --- a/backend/app/Services/Handlers/Attendee/EditAttendeeHandler.php +++ b/backend/app/Services/Handlers/Attendee/EditAttendeeHandler.php @@ -3,14 +3,14 @@ namespace HiEvents\Services\Handlers\Attendee; use HiEvents\DomainObjects\AttendeeDomainObject; -use HiEvents\DomainObjects\Enums\TicketType; +use HiEvents\DomainObjects\Enums\ProductPriceType; use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; -use HiEvents\DomainObjects\TicketPriceDomainObject; -use HiEvents\Exceptions\NoTicketsAvailableException; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; +use HiEvents\DomainObjects\ProductPriceDomainObject; +use HiEvents\Exceptions\NoProductsAvailableException; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use HiEvents\Services\Handlers\Attendee\DTO\EditAttendeeDTO; use Illuminate\Database\DatabaseManager; use Illuminate\Validation\ValidationException; @@ -19,10 +19,10 @@ class EditAttendeeHandler { public function __construct( - private readonly AttendeeRepositoryInterface $attendeeRepository, - private readonly TicketRepositoryInterface $ticketRepository, - private readonly TicketQuantityUpdateService $ticketQuantityService, - private readonly DatabaseManager $databaseManager, + private readonly AttendeeRepositoryInterface $attendeeRepository, + private readonly ProductRepositoryInterface $productRepository, + private readonly ProductQuantityUpdateService $productQuantityService, + private readonly DatabaseManager $databaseManager, ) { } @@ -34,21 +34,21 @@ public function __construct( public function handle(EditAttendeeDTO $editAttendeeDTO): AttendeeDomainObject { return $this->databaseManager->transaction(function () use ($editAttendeeDTO) { - $this->validateTicketId($editAttendeeDTO); + $this->validateProductId($editAttendeeDTO); $attendee = $this->getAttendee($editAttendeeDTO); - $this->adjustTicketQuantities($attendee, $editAttendeeDTO); + $this->adjustProductQuantities($attendee, $editAttendeeDTO); return $this->updateAttendee($editAttendeeDTO); }); } - private function adjustTicketQuantities(AttendeeDomainObject $attendee, EditAttendeeDTO $editAttendeeDTO): void + private function adjustProductQuantities(AttendeeDomainObject $attendee, EditAttendeeDTO $editAttendeeDTO): void { - if ($attendee->getTicketPriceId() !== $editAttendeeDTO->ticket_price_id) { - $this->ticketQuantityService->decreaseQuantitySold($editAttendeeDTO->ticket_price_id); - $this->ticketQuantityService->increaseQuantitySold($attendee->getTicketPriceId()); + if ($attendee->getProductPriceId() !== $editAttendeeDTO->product_price_id) { + $this->productQuantityService->decreaseQuantitySold($editAttendeeDTO->product_price_id); + $this->productQuantityService->increaseQuantitySold($attendee->getProductPriceId()); } } @@ -58,7 +58,7 @@ private function updateAttendee(EditAttendeeDTO $editAttendeeDTO): AttendeeDomai 'first_name' => $editAttendeeDTO->first_name, 'last_name' => $editAttendeeDTO->last_name, 'email' => $editAttendeeDTO->email, - 'ticket_id' => $editAttendeeDTO->ticket_id, + 'product_id' => $editAttendeeDTO->product_id, ], [ 'event_id' => $editAttendeeDTO->event_id, ]); @@ -66,32 +66,32 @@ private function updateAttendee(EditAttendeeDTO $editAttendeeDTO): AttendeeDomai /** * @throws ValidationException - * @throws NoTicketsAvailableException + * @throws NoProductsAvailableException */ - private function validateTicketId(EditAttendeeDTO $editAttendeeDTO): void + private function validateProductId(EditAttendeeDTO $editAttendeeDTO): void { - $ticket = $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) + $product = $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) ->findFirstWhere([ - TicketDomainObjectAbstract::ID => $editAttendeeDTO->ticket_id, + ProductDomainObjectAbstract::ID => $editAttendeeDTO->product_id, ]); - if ($ticket->getEventId() !== $editAttendeeDTO->event_id) { + if ($product->getEventId() !== $editAttendeeDTO->event_id) { throw ValidationException::withMessages([ - 'ticket_id' => __('Ticket ID is not valid'), + 'product_id' => __('Product ID is not valid'), ]); } - $availableQuantity = $this->ticketRepository->getQuantityRemainingForTicketPrice( - ticketId: $editAttendeeDTO->ticket_id, - ticketPriceId: $ticket->getType() === TicketType::TIERED->name - ? $editAttendeeDTO->ticket_price_id - : $ticket->getTicketPrices()->first()->getId(), + $availableQuantity = $this->productRepository->getQuantityRemainingForProductPrice( + productId: $editAttendeeDTO->product_id, + productPriceId: $product->getType() === ProductPriceType::TIERED->name + ? $editAttendeeDTO->product_price_id + : $product->getProductPrices()->first()->getId(), ); if ($availableQuantity <= 0) { - throw new NoTicketsAvailableException( - __('There are no tickets available. If you would like to assign this ticket to this attendee, please adjust the ticket\'s available quantity.') + throw new NoProductsAvailableException( + __('There are no products available. If you would like to assign this product to this attendee, please adjust the product\'s available quantity.') ); } } diff --git a/backend/app/Services/Handlers/Attendee/PartialEditAttendeeHandler.php b/backend/app/Services/Handlers/Attendee/PartialEditAttendeeHandler.php index 5717bddfb7..1283559e46 100644 --- a/backend/app/Services/Handlers/Attendee/PartialEditAttendeeHandler.php +++ b/backend/app/Services/Handlers/Attendee/PartialEditAttendeeHandler.php @@ -5,7 +5,7 @@ use HiEvents\DomainObjects\AttendeeDomainObject; use HiEvents\DomainObjects\Status\AttendeeStatus; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use HiEvents\Services\Handlers\Attendee\DTO\PartialEditAttendeeDTO; use Illuminate\Database\DatabaseManager; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -14,9 +14,9 @@ class PartialEditAttendeeHandler { public function __construct( - private readonly AttendeeRepositoryInterface $attendeeRepository, - private readonly TicketQuantityUpdateService $ticketQuantityService, - private readonly DatabaseManager $databaseManager + private readonly AttendeeRepositoryInterface $attendeeRepository, + private readonly ProductQuantityUpdateService $productQuantityService, + private readonly DatabaseManager $databaseManager ) { } @@ -43,7 +43,7 @@ private function updateAttendee(PartialEditAttendeeDTO $data): AttendeeDomainObj } if ($data->status && $data->status !== $attendee->getStatus()) { - $this->adjustTicketQuantity($data, $attendee); + $this->adjustProductQuantity($data, $attendee); } return $this->attendeeRepository->updateByIdWhere( @@ -62,14 +62,14 @@ private function updateAttendee(PartialEditAttendeeDTO $data): AttendeeDomainObj } /** - * @todo - we should check ticket availability before updating the ticket quantity + * @todo - we should check product availability before updating the product quantity */ - private function adjustTicketQuantity(PartialEditAttendeeDTO $data, AttendeeDomainObject $attendee): void + private function adjustProductQuantity(PartialEditAttendeeDTO $data, AttendeeDomainObject $attendee): void { if ($data->status === AttendeeStatus::ACTIVE->name) { - $this->ticketQuantityService->increaseQuantitySold($attendee->getTicketPriceId()); + $this->productQuantityService->increaseQuantitySold($attendee->getProductPriceId()); } elseif ($data->status === AttendeeStatus::CANCELLED->name) { - $this->ticketQuantityService->decreaseQuantitySold($attendee->getTicketPriceId()); + $this->productQuantityService->decreaseQuantitySold($attendee->getProductPriceId()); } } } diff --git a/backend/app/Services/Handlers/Attendee/ResendAttendeeTicketHandler.php b/backend/app/Services/Handlers/Attendee/ResendAttendeeTicketHandler.php index 1424a16293..865241dec1 100644 --- a/backend/app/Services/Handlers/Attendee/ResendAttendeeTicketHandler.php +++ b/backend/app/Services/Handlers/Attendee/ResendAttendeeTicketHandler.php @@ -9,7 +9,7 @@ use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\EventRepositoryInterface; -use HiEvents\Services\Domain\Attendee\SendAttendeeTicketService; +use HiEvents\Services\Domain\Attendee\SendAttendeeProductService; use HiEvents\Services\Handlers\Attendee\DTO\ResendAttendeeTicketDTO; use Psr\Log\LoggerInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -17,7 +17,7 @@ readonly class ResendAttendeeTicketHandler { public function __construct( - private SendAttendeeTicketService $sendAttendeeTicketService, + private SendAttendeeProductService $sendAttendeeProductService, private AttendeeRepositoryInterface $attendeeRepository, private EventRepositoryInterface $eventRepository, private LoggerInterface $logger, @@ -28,11 +28,11 @@ public function __construct( /** * @throws ResourceConflictException */ - public function handle(ResendAttendeeTicketDTO $resendAttendeeTicketDTO): void + public function handle(ResendAttendeeTicketDTO $resendAttendeeProductDTO): void { $attendee = $this->attendeeRepository->findFirstWhere([ - 'id' => $resendAttendeeTicketDTO->attendeeId, - 'event_id' => $resendAttendeeTicketDTO->eventId, + 'id' => $resendAttendeeProductDTO->attendeeId, + 'event_id' => $resendAttendeeProductDTO->eventId, ]); if (!$attendee) { @@ -46,9 +46,9 @@ public function handle(ResendAttendeeTicketDTO $resendAttendeeTicketDTO): void $event = $this->eventRepository ->loadRelation(new Relationship(OrganizerDomainObject::class, name: 'organizer')) ->loadRelation(EventSettingDomainObject::class) - ->findById($resendAttendeeTicketDTO->eventId); + ->findById($resendAttendeeProductDTO->eventId); - $this->sendAttendeeTicketService->send( + $this->sendAttendeeProductService->send( attendee: $attendee, event: $event, eventSettings: $event->getEventSettings(), @@ -56,8 +56,8 @@ public function handle(ResendAttendeeTicketDTO $resendAttendeeTicketDTO): void ); $this->logger->info('Attendee ticket resent', [ - 'attendeeId' => $resendAttendeeTicketDTO->attendeeId, - 'eventId' => $resendAttendeeTicketDTO->eventId + 'attendeeId' => $resendAttendeeProductDTO->attendeeId, + 'eventId' => $resendAttendeeProductDTO->eventId ]); } } diff --git a/backend/app/Services/Handlers/CapacityAssignment/CreateCapacityAssignmentHandler.php b/backend/app/Services/Handlers/CapacityAssignment/CreateCapacityAssignmentHandler.php index bfa77f47b3..26284f36c9 100644 --- a/backend/app/Services/Handlers/CapacityAssignment/CreateCapacityAssignmentHandler.php +++ b/backend/app/Services/Handlers/CapacityAssignment/CreateCapacityAssignmentHandler.php @@ -5,7 +5,7 @@ use HiEvents\DomainObjects\CapacityAssignmentDomainObject; use HiEvents\DomainObjects\Enums\CapacityAssignmentAppliesTo; use HiEvents\Services\Domain\CapacityAssignment\CreateCapacityAssignmentService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CapacityAssignment\DTO\UpsertCapacityAssignmentDTO; class CreateCapacityAssignmentHandler @@ -17,7 +17,7 @@ public function __construct( } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function handle(UpsertCapacityAssignmentDTO $data): CapacityAssignmentDomainObject { @@ -25,12 +25,12 @@ public function handle(UpsertCapacityAssignmentDTO $data): CapacityAssignmentDom ->setName($data->name) ->setEventId($data->event_id) ->setCapacity($data->capacity) - ->setAppliesTo(CapacityAssignmentAppliesTo::TICKETS->name) + ->setAppliesTo(CapacityAssignmentAppliesTo::PRODUCTS->name) ->setStatus($data->status->name); return $this->createCapacityAssignmentService->createCapacityAssignment( $capacityAssignment, - $data->ticket_ids, + $data->product_ids, ); } } diff --git a/backend/app/Services/Handlers/CapacityAssignment/DTO/UpsertCapacityAssignmentDTO.php b/backend/app/Services/Handlers/CapacityAssignment/DTO/UpsertCapacityAssignmentDTO.php index 35669261c0..7ebd41bef3 100644 --- a/backend/app/Services/Handlers/CapacityAssignment/DTO/UpsertCapacityAssignmentDTO.php +++ b/backend/app/Services/Handlers/CapacityAssignment/DTO/UpsertCapacityAssignmentDTO.php @@ -13,7 +13,7 @@ public function __construct( public CapacityAssignmentStatus $status, public ?int $capacity, - public ?array $ticket_ids = null, + public ?array $product_ids = null, public ?int $id = null, ) { diff --git a/backend/app/Services/Handlers/CapacityAssignment/DeleteCapacityAssignmentHandler.php b/backend/app/Services/Handlers/CapacityAssignment/DeleteCapacityAssignmentHandler.php index bda3eac0ba..26b90b3f5c 100644 --- a/backend/app/Services/Handlers/CapacityAssignment/DeleteCapacityAssignmentHandler.php +++ b/backend/app/Services/Handlers/CapacityAssignment/DeleteCapacityAssignmentHandler.php @@ -3,14 +3,14 @@ namespace HiEvents\Services\Handlers\CapacityAssignment; use HiEvents\Repository\Interfaces\CapacityAssignmentRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use Illuminate\Database\DatabaseManager; class DeleteCapacityAssignmentHandler { public function __construct( private readonly CapacityAssignmentRepositoryInterface $capacityAssignmentRepository, - private readonly TicketRepositoryInterface $ticketRepository, + private readonly ProductRepositoryInterface $productRepository, private readonly DatabaseManager $databaseManager, ) { @@ -19,7 +19,7 @@ public function __construct( public function handle(int $id, int $eventId): void { $this->databaseManager->transaction(function () use ($id, $eventId) { - $this->ticketRepository->removeCapacityAssignmentFromTickets( + $this->productRepository->removeCapacityAssignmentFromProducts( capacityAssignmentId: $id, ); diff --git a/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentHandler.php b/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentHandler.php index cbdf770f03..57c8e22d7e 100644 --- a/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentHandler.php +++ b/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentHandler.php @@ -3,7 +3,7 @@ namespace HiEvents\Services\Handlers\CapacityAssignment; use HiEvents\DomainObjects\CapacityAssignmentDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Repository\Interfaces\CapacityAssignmentRepositoryInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -18,7 +18,7 @@ public function __construct( public function handle(int $capacityAssignmentId, int $eventId): CapacityAssignmentDomainObject { $capacityAssignment = $this->capacityAssignmentRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findFirstWhere([ 'event_id' => $eventId, 'id' => $capacityAssignmentId, diff --git a/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentsHandler.php b/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentsHandler.php index e4d0434b3b..8b0a0cb2a6 100644 --- a/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentsHandler.php +++ b/backend/app/Services/Handlers/CapacityAssignment/GetCapacityAssignmentsHandler.php @@ -2,7 +2,7 @@ namespace HiEvents\Services\Handlers\CapacityAssignment; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Repository\Interfaces\CapacityAssignmentRepositoryInterface; use HiEvents\Services\Handlers\CapacityAssignment\DTO\GetCapacityAssignmentsDTO; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -18,7 +18,7 @@ public function __construct( public function handle(GetCapacityAssignmentsDTO $dto): LengthAwarePaginator { return $this->capacityAssignmentRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findByEventId( eventId: $dto->eventId, params: $dto->queryParams, diff --git a/backend/app/Services/Handlers/CapacityAssignment/UpdateCapacityAssignmentHandler.php b/backend/app/Services/Handlers/CapacityAssignment/UpdateCapacityAssignmentHandler.php index 9c330d859b..d75f7f86f1 100644 --- a/backend/app/Services/Handlers/CapacityAssignment/UpdateCapacityAssignmentHandler.php +++ b/backend/app/Services/Handlers/CapacityAssignment/UpdateCapacityAssignmentHandler.php @@ -5,7 +5,7 @@ use HiEvents\DomainObjects\CapacityAssignmentDomainObject; use HiEvents\DomainObjects\Enums\CapacityAssignmentAppliesTo; use HiEvents\Services\Domain\CapacityAssignment\UpdateCapacityAssignmentService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CapacityAssignment\DTO\UpsertCapacityAssignmentDTO; class UpdateCapacityAssignmentHandler @@ -17,7 +17,7 @@ public function __construct( } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function handle(UpsertCapacityAssignmentDTO $data): CapacityAssignmentDomainObject { @@ -26,12 +26,12 @@ public function handle(UpsertCapacityAssignmentDTO $data): CapacityAssignmentDom ->setName($data->name) ->setEventId($data->event_id) ->setCapacity($data->capacity) - ->setAppliesTo(CapacityAssignmentAppliesTo::TICKETS->name) + ->setAppliesTo(CapacityAssignmentAppliesTo::PRODUCTS->name) ->setStatus($data->status->name); return $this->updateCapacityAssignmentService->updateCapacityAssignment( $capacityAssignment, - $data->ticket_ids, + $data->product_ids, ); } } diff --git a/backend/app/Services/Handlers/CheckInList/CreateCheckInListHandler.php b/backend/app/Services/Handlers/CheckInList/CreateCheckInListHandler.php index a3d5ed4554..da0ee904ad 100644 --- a/backend/app/Services/Handlers/CheckInList/CreateCheckInListHandler.php +++ b/backend/app/Services/Handlers/CheckInList/CreateCheckInListHandler.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\Services\Domain\CheckInList\CreateCheckInListService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CheckInList\DTO\UpsertCheckInListDTO; class CreateCheckInListHandler @@ -16,7 +16,7 @@ public function __construct( } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function handle(UpsertCheckInListDTO $listData): CheckInListDomainObject { @@ -29,7 +29,7 @@ public function handle(UpsertCheckInListDTO $listData): CheckInListDomainObject return $this->createCheckInListService->createCheckInList( checkInList: $checkInList, - ticketIds: $listData->ticketIds + productIds: $listData->productIds ); } } diff --git a/backend/app/Services/Handlers/CheckInList/DTO/UpsertCheckInListDTO.php b/backend/app/Services/Handlers/CheckInList/DTO/UpsertCheckInListDTO.php index 35348163e4..16296943e1 100644 --- a/backend/app/Services/Handlers/CheckInList/DTO/UpsertCheckInListDTO.php +++ b/backend/app/Services/Handlers/CheckInList/DTO/UpsertCheckInListDTO.php @@ -10,7 +10,7 @@ public function __construct( public string $name, public ?string $description, public int $eventId, - public array $ticketIds, + public array $productIds, public ?string $expiresAt = null, public ?string $activatesAt = null, public ?int $id = null, diff --git a/backend/app/Services/Handlers/CheckInList/GetCheckInListHandler.php b/backend/app/Services/Handlers/CheckInList/GetCheckInListHandler.php index fc442f76ba..83c40b12d1 100644 --- a/backend/app/Services/Handlers/CheckInList/GetCheckInListHandler.php +++ b/backend/app/Services/Handlers/CheckInList/GetCheckInListHandler.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\CheckInListRepositoryInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -20,7 +20,7 @@ public function __construct( public function handle(int $checkInListId, int $eventId): CheckInListDomainObject { $checkInList = $this->checkInListRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->loadRelation(new Relationship(domainObject: EventDomainObject::class, name: 'event')) ->findFirstWhere([ 'event_id' => $eventId, diff --git a/backend/app/Services/Handlers/CheckInList/GetCheckInListsHandler.php b/backend/app/Services/Handlers/CheckInList/GetCheckInListsHandler.php index be46595b23..aff2b2182a 100644 --- a/backend/app/Services/Handlers/CheckInList/GetCheckInListsHandler.php +++ b/backend/app/Services/Handlers/CheckInList/GetCheckInListsHandler.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\CheckInListRepositoryInterface; use HiEvents\Services\Handlers\CheckInList\DTO\GetCheckInListsDTO; @@ -21,7 +21,7 @@ public function __construct( public function handle(GetCheckInListsDTO $dto): LengthAwarePaginator { $checkInLists = $this->checkInListRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->loadRelation(new Relationship(domainObject: EventDomainObject::class, name: 'event')) ->findByEventId( eventId: $dto->eventId, diff --git a/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListAttendeesPublicHandler.php b/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListAttendeesPublicHandler.php index de6332ea0b..f1b1b6ae9f 100644 --- a/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListAttendeesPublicHandler.php +++ b/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListAttendeesPublicHandler.php @@ -5,7 +5,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\Generated\CheckInListDomainObjectAbstract; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Exceptions\CannotCheckInException; use HiEvents\Helper\DateHelper; use HiEvents\Http\DTO\QueryParamsDTO; @@ -30,7 +30,7 @@ public function __construct( public function handle(string $shortId, QueryParamsDTO $queryParams): Paginator { $checkInList = $this->checkInListRepository - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->loadRelation(new Relationship(EventDomainObject::class, name: 'event')) ->findFirstWhere([ CheckInListDomainObjectAbstract::SHORT_ID => $shortId, diff --git a/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListPublicHandler.php b/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListPublicHandler.php index bbd24480a0..b773da1b9c 100644 --- a/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListPublicHandler.php +++ b/backend/app/Services/Handlers/CheckInList/Public/GetCheckInListPublicHandler.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\DomainObjects\EventDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\CheckInListRepositoryInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -21,7 +21,7 @@ public function handle(string $shortId): CheckInListDomainObject { $checkInList = $this->checkInListRepository ->loadRelation(new Relationship(domainObject: EventDomainObject::class, name: 'event')) - ->loadRelation(TicketDomainObject::class) + ->loadRelation(ProductDomainObject::class) ->findFirstWhere([ 'short_id' => $shortId, ]); diff --git a/backend/app/Services/Handlers/CheckInList/UpdateCheckInlistHandler.php b/backend/app/Services/Handlers/CheckInList/UpdateCheckInlistHandler.php index 4430781712..0317a8bc9a 100644 --- a/backend/app/Services/Handlers/CheckInList/UpdateCheckInlistHandler.php +++ b/backend/app/Services/Handlers/CheckInList/UpdateCheckInlistHandler.php @@ -4,7 +4,7 @@ use HiEvents\DomainObjects\CheckInListDomainObject; use HiEvents\Services\Domain\CheckInList\UpdateCheckInListService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\CheckInList\DTO\UpsertCheckInListDTO; class UpdateCheckInlistHandler @@ -16,7 +16,7 @@ public function __construct( } /** - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function handle(UpsertCheckInListDTO $data): CheckInListDomainObject { @@ -30,7 +30,7 @@ public function handle(UpsertCheckInListDTO $data): CheckInListDomainObject return $this->updateCheckInlistService->updateCheckInlist( checkInList: $checkInList, - ticketIds: $data->ticketIds + productIds: $data->productIds ); } } diff --git a/backend/app/Services/Handlers/Event/DTO/EventStatsResponseDTO.php b/backend/app/Services/Handlers/Event/DTO/EventStatsResponseDTO.php index 534939473b..ca3a4e9d97 100644 --- a/backend/app/Services/Handlers/Event/DTO/EventStatsResponseDTO.php +++ b/backend/app/Services/Handlers/Event/DTO/EventStatsResponseDTO.php @@ -18,7 +18,7 @@ public function __construct( public EventCheckInStatsResponseDTO $check_in_stats, - public int $total_tickets_sold, + public int $total_products_sold, public int $total_orders, public float $total_gross_sales, public float $total_fees, diff --git a/backend/app/Services/Handlers/Event/DuplicateEventHandler.php b/backend/app/Services/Handlers/Event/DuplicateEventHandler.php index 1ebc2e20c2..081cfdb49c 100644 --- a/backend/app/Services/Handlers/Event/DuplicateEventHandler.php +++ b/backend/app/Services/Handlers/Event/DuplicateEventHandler.php @@ -25,7 +25,7 @@ public function handle(DuplicateEventDataDTO $data): EventDomainObject accountId: $data->accountId, title: $data->title, startDate: $data->startDate, - duplicateTickets: $data->duplicateTickets, + duplicateProducts: $data->duplicateProducts, duplicateQuestions: $data->duplicateQuestions, duplicateSettings: $data->duplicateSettings, duplicatePromoCodes: $data->duplicatePromoCodes, diff --git a/backend/app/Services/Handlers/Event/GetPublicEventHandler.php b/backend/app/Services/Handlers/Event/GetPublicEventHandler.php index 888f6c9227..6f30944740 100644 --- a/backend/app/Services/Handlers/Event/GetPublicEventHandler.php +++ b/backend/app/Services/Handlers/Event/GetPublicEventHandler.php @@ -8,13 +8,13 @@ use HiEvents\DomainObjects\ImageDomainObject; use HiEvents\DomainObjects\OrganizerDomainObject; use HiEvents\DomainObjects\TaxAndFeesDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface; use HiEvents\Services\Domain\Event\EventPageViewIncrementService; -use HiEvents\Services\Domain\Ticket\TicketFilterService; +use HiEvents\Services\Domain\Product\ProductFilterService; use HiEvents\Services\Handlers\Event\DTO\GetPublicEventDTO; class GetPublicEventHandler @@ -22,7 +22,7 @@ class GetPublicEventHandler public function __construct( private readonly EventRepositoryInterface $eventRepository, private readonly PromoCodeRepositoryInterface $promoCodeRepository, - private readonly TicketFilterService $ticketFilterService, + private readonly ProductFilterService $productFilterService, private readonly EventPageViewIncrementService $eventPageViewIncrementService, ) { @@ -32,8 +32,8 @@ public function handle(GetPublicEventDTO $data): EventDomainObject { $event = $this->eventRepository ->loadRelation( - new Relationship(TicketDomainObject::class, [ - new Relationship(TicketPriceDomainObject::class), + new Relationship(ProductDomainObject::class, [ + new Relationship(ProductPriceDomainObject::class), new Relationship(TaxAndFeesDomainObject::class) ]) ) @@ -55,6 +55,6 @@ public function handle(GetPublicEventDTO $data): EventDomainObject $this->eventPageViewIncrementService->increment($data->eventId, $data->ipAddress); } - return $event->setTickets($this->ticketFilterService->filter($event->getTickets(), $promoCodeDomainObject)); + return $event->setProducts($this->productFilterService->filter($event->getProducts(), $promoCodeDomainObject)); } } diff --git a/backend/app/Services/Handlers/Message/DTO/SendMessageDTO.php b/backend/app/Services/Handlers/Message/DTO/SendMessageDTO.php index 72619fe946..1da3b7b09e 100644 --- a/backend/app/Services/Handlers/Message/DTO/SendMessageDTO.php +++ b/backend/app/Services/Handlers/Message/DTO/SendMessageDTO.php @@ -19,7 +19,7 @@ public function __construct( public readonly ?int $order_id, public readonly ?int $id = null, public readonly ?array $attendee_ids = [], - public readonly ?array $ticket_ids = [], + public readonly ?array $product_ids = [], ) { diff --git a/backend/app/Services/Handlers/Message/SendMessageHandler.php b/backend/app/Services/Handlers/Message/SendMessageHandler.php index 5cd0387d85..ab04e7e66c 100644 --- a/backend/app/Services/Handlers/Message/SendMessageHandler.php +++ b/backend/app/Services/Handlers/Message/SendMessageHandler.php @@ -12,7 +12,7 @@ use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\MessageRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Services\Handlers\Message\DTO\SendMessageDTO; use HTMLPurifier; use Illuminate\Support\Collection; @@ -22,7 +22,7 @@ public function __construct( private OrderRepositoryInterface $orderRepository, private AttendeeRepositoryInterface $attendeeRepository, - private TicketRepositoryInterface $ticketRepository, + private ProductRepositoryInterface $productRepository, private MessageRepositoryInterface $messageRepository, private AccountRepositoryInterface $accountRepository, private HTMLPurifier $purifier, @@ -48,7 +48,7 @@ public function handle(SendMessageDTO $messageData): MessageDomainObject 'type' => $messageData->type->name, 'order_id' => $this->getOrderId($messageData), 'attendee_ids' => $this->getAttendeeIds($messageData)->toArray(), - 'ticket_ids' => $this->getTicketIds($messageData)->toArray(), + 'product_ids' => $this->getProductIds($messageData)->toArray(), 'sent_at' => Carbon::now()->toDateTimeString(), 'sent_by_user_id' => $messageData->sent_by_user_id, 'status' => MessageStatus::PROCESSING->name, @@ -63,7 +63,7 @@ public function handle(SendMessageDTO $messageData): MessageDomainObject 'is_test' => $messageData->is_test, 'order_id' => $message->getOrderId(), 'attendee_ids' => $message->getAttendeeIds(), - 'ticket_ids' => $message->getTicketIds(), + 'product_ids' => $message->getProductIds(), 'send_copy_to_current_user' => $messageData->send_copy_to_current_user, 'sent_by_user_id' => $messageData->sent_by_user_id, 'account_id' => $messageData->account_id, @@ -90,18 +90,18 @@ private function getAttendeeIds(SendMessageDTO $messageData): Collection } - private function getTicketIds(SendMessageDTO $messageData): Collection + private function getProductIds(SendMessageDTO $messageData): Collection { - $tickets = $this->ticketRepository->findWhereIn( + $products = $this->productRepository->findWhereIn( field: 'id', - values: $messageData->ticket_ids, + values: $messageData->product_ids, additionalWhere: [ 'event_id' => $messageData->event_id, ], columns: ['id'] ); - return $tickets->map(fn($attendee) => $attendee->getId()); + return $products->map(fn($attendee) => $attendee->getId()); } private function getOrderId(SendMessageDTO $messageData): ?int diff --git a/backend/app/Services/Handlers/Order/CompleteOrderHandler.php b/backend/app/Services/Handlers/Order/CompleteOrderHandler.php index 33a1819706..5cd9792720 100644 --- a/backend/app/Services/Handlers/Order/CompleteOrderHandler.php +++ b/backend/app/Services/Handlers/Order/CompleteOrderHandler.php @@ -9,22 +9,22 @@ use HiEvents\DomainObjects\AttendeeDomainObject; use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract; use HiEvents\DomainObjects\Generated\OrderDomainObjectAbstract; -use HiEvents\DomainObjects\Generated\TicketPriceDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductPriceDomainObjectAbstract; use HiEvents\DomainObjects\OrderDomainObject; use HiEvents\DomainObjects\OrderItemDomainObject; use HiEvents\DomainObjects\Status\AttendeeStatus; use HiEvents\DomainObjects\Status\OrderPaymentStatus; use HiEvents\DomainObjects\Status\OrderStatus; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Events\OrderStatusChangedEvent; use HiEvents\Exceptions\ResourceConflictException; use HiEvents\Helper\IdHelper; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; use HiEvents\Repository\Interfaces\QuestionAnswerRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketPriceRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductPriceRepositoryInterface; use HiEvents\Services\Domain\Payment\Stripe\EventHandlers\PaymentIntentSucceededHandler; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use HiEvents\Services\Handlers\Order\DTO\CompleteOrderAttendeeDTO; use HiEvents\Services\Handlers\Order\DTO\CompleteOrderDTO; use HiEvents\Services\Handlers\Order\DTO\CompleteOrderOrderDTO; @@ -43,8 +43,8 @@ public function __construct( private OrderRepositoryInterface $orderRepository, private AttendeeRepositoryInterface $attendeeRepository, private QuestionAnswerRepositoryInterface $questionAnswersRepository, - private TicketQuantityUpdateService $ticketQuantityUpdateService, - private TicketPriceRepositoryInterface $ticketPriceRepository, + private ProductQuantityUpdateService $productQuantityUpdateService, + private ProductPriceRepositoryInterface $productPriceRepository, ) { } @@ -68,13 +68,13 @@ public function handle(string $orderShortId, CompleteOrderDTO $orderData): Order } /** - * If there's no payment required, immediately update the ticket quantities, otherwise handle + * If there's no payment required, immediately update the product quantities, otherwise handle * this in the PaymentIntentEventHandlerService * * @see PaymentIntentSucceededHandler */ if (!$order->isPaymentRequired()) { - $this->ticketQuantityUpdateService->updateQuantitiesFromOrder($updatedOrder); + $this->productQuantityUpdateService->updateQuantitiesFromOrder($updatedOrder); } OrderStatusChangedEvent::dispatch($updatedOrder); @@ -91,22 +91,22 @@ private function createAttendees(Collection $attendees, OrderDomainObject $order $inserts = []; $publicIdIndex = 1; - $ticketsPrices = $this->ticketPriceRepository->findWhereIn( - field: TicketPriceDomainObjectAbstract::ID, - values: $attendees->pluck('ticket_price_id')->toArray(), + $productsPrices = $this->productPriceRepository->findWhereIn( + field: ProductPriceDomainObjectAbstract::ID, + values: $attendees->pluck('product_price_id')->toArray(), ); - $this->validateTicketPriceIdsMatchOrder($order, $ticketsPrices); + $this->validateProductPriceIdsMatchOrder($order, $productsPrices); foreach ($attendees as $attendee) { - $ticketId = $ticketsPrices->first( - fn(TicketPriceDomainObject $ticketPrice) => $ticketPrice->getId() === $attendee->ticket_price_id) - ->getTicketId(); + $productId = $productsPrices->first( + fn(ProductPriceDomainObject $productPrice) => $productPrice->getId() === $attendee->product_price_id) + ->getProductId(); $inserts[] = [ AttendeeDomainObjectAbstract::EVENT_ID => $order->getEventId(), - AttendeeDomainObjectAbstract::TICKET_ID => $ticketId, - AttendeeDomainObjectAbstract::TICKET_PRICE_ID => $attendee->ticket_price_id, + AttendeeDomainObjectAbstract::PRODUCT_ID => $productId, + AttendeeDomainObjectAbstract::PRODUCT_PRICE_ID => $attendee->product_price_id, AttendeeDomainObjectAbstract::STATUS => AttendeeStatus::ACTIVE->name, AttendeeDomainObjectAbstract::EMAIL => $attendee->email, AttendeeDomainObjectAbstract::FIRST_NAME => $attendee->first_name, @@ -126,7 +126,7 @@ private function createAttendees(Collection $attendees, OrderDomainObject $order AttendeeDomainObjectAbstract::ORDER_ID => $order->getId() ]); - $this->createAttendeeQuestions($attendees, $insertedAttendees, $order, $ticketsPrices); + $this->createAttendeeQuestions($attendees, $insertedAttendees, $order, $productsPrices); } private function createOrderQuestions(Collection $questions, OrderDomainObject $order): void @@ -147,18 +147,18 @@ private function createAttendeeQuestions( Collection $attendees, Collection $insertedAttendees, OrderDomainObject $order, - Collection $ticketPrices, + Collection $productPrices, ): void { $insertedIds = []; /** @var CompleteOrderAttendeeDTO $attendee */ foreach ($attendees as $attendee) { - $ticketId = $ticketPrices->first( - fn(TicketPriceDomainObject $ticketPrice) => $ticketPrice->getId() === $attendee->ticket_price_id) - ->getTicketId(); + $productId = $productPrices->first( + fn(ProductPriceDomainObject $productPrice) => $productPrice->getId() === $attendee->product_price_id) + ->getProductId(); $attendeeIterator = $insertedAttendees->filter( - fn(AttendeeDomainObject $insertedAttendee) => $insertedAttendee->getTicketId() === $ticketId + fn(AttendeeDomainObject $insertedAttendee) => $insertedAttendee->getProductId() === $productId && !in_array($insertedAttendee->getId(), $insertedIds, true) )->getIterator(); @@ -177,7 +177,7 @@ private function createAttendeeQuestions( 'question_id' => $question->question_id, 'answer' => $question->response['answer'] ?? $question->response, 'order_id' => $order->getId(), - 'ticket_id' => $ticketId, + 'product_id' => $productId, 'attendee_id' => $attendeeId ]); @@ -243,19 +243,19 @@ private function updateOrder(OrderDomainObject $order, CompleteOrderOrderDTO $or } /** - * Check if the passed ticket price IDs match what exist in the order_items table + * Check if the passed product price IDs match what exist in the order_items table * * @throws ResourceConflictException */ - private function validateTicketPriceIdsMatchOrder(OrderDomainObject $order, Collection $ticketsPrices): void + private function validateProductPriceIdsMatchOrder(OrderDomainObject $order, Collection $productsPrices): void { - $orderTicketPriceIds = $order->getOrderItems() - ?->map(fn(OrderItemDomainObject $orderItem) => $orderItem->getTicketPriceId())->toArray(); + $orderProductPriceIds = $order->getOrderItems() + ?->map(fn(OrderItemDomainObject $orderItem) => $orderItem->getProductPriceId())->toArray(); - $ticketsPricesIds = $ticketsPrices->map(fn(TicketPriceDomainObject $ticketPrice) => $ticketPrice->getId()); + $productsPricesIds = $productsPrices->map(fn(ProductPriceDomainObject $productPrice) => $productPrice->getId()); - if ($ticketsPricesIds->diff($orderTicketPriceIds)->isNotEmpty()) { - throw new ResourceConflictException(__('There is an unexpected ticket price ID in the order')); + if ($productsPricesIds->diff($orderProductPriceIds)->isNotEmpty()) { + throw new ResourceConflictException(__('There is an unexpected product price ID in the order')); } } } diff --git a/backend/app/Services/Handlers/Order/CreateOrderHandler.php b/backend/app/Services/Handlers/Order/CreateOrderHandler.php index ba63055e7e..80158bc7e3 100644 --- a/backend/app/Services/Handlers/Order/CreateOrderHandler.php +++ b/backend/app/Services/Handlers/Order/CreateOrderHandler.php @@ -64,7 +64,7 @@ public function handle( $orderItems = $this->orderItemProcessingService->process( order: $order, - ticketsOrderDetails: $createOrderPublicDTO->tickets, + productsOrderDetails: $createOrderPublicDTO->products, event: $event, promoCode: $promoCode, ); diff --git a/backend/app/Services/Handlers/Order/DTO/CompleteOrderAttendeeDTO.php b/backend/app/Services/Handlers/Order/DTO/CompleteOrderAttendeeDTO.php index 9d7bb32b14..28c3deb923 100644 --- a/backend/app/Services/Handlers/Order/DTO/CompleteOrderAttendeeDTO.php +++ b/backend/app/Services/Handlers/Order/DTO/CompleteOrderAttendeeDTO.php @@ -12,7 +12,7 @@ public function __construct( public readonly string $first_name, public readonly string $last_name, public readonly string $email, - public readonly int $ticket_price_id, + public readonly int $product_price_id, #[CollectionOf(OrderQuestionsDTO::class)] public readonly ?Collection $questions = null, ) diff --git a/backend/app/Services/Handlers/Order/DTO/CreateOrderPublicDTO.php b/backend/app/Services/Handlers/Order/DTO/CreateOrderPublicDTO.php index 17bf413dbb..e24ed402bb 100644 --- a/backend/app/Services/Handlers/Order/DTO/CreateOrderPublicDTO.php +++ b/backend/app/Services/Handlers/Order/DTO/CreateOrderPublicDTO.php @@ -9,9 +9,9 @@ class CreateOrderPublicDTO extends BaseDTO { public function __construct( /** - * @var Collection + * @var Collection */ - public readonly Collection $tickets, + public readonly Collection $products, public readonly bool $is_user_authenticated, public readonly string $session_identifier, public readonly ?string $order_locale = null, diff --git a/backend/app/Services/Handlers/Order/DTO/TicketOrderDetailsDTO.php b/backend/app/Services/Handlers/Order/DTO/TicketOrderDetailsDTO.php index 4395586134..b7cbed4f1a 100644 --- a/backend/app/Services/Handlers/Order/DTO/TicketOrderDetailsDTO.php +++ b/backend/app/Services/Handlers/Order/DTO/TicketOrderDetailsDTO.php @@ -4,14 +4,14 @@ use HiEvents\DataTransferObjects\Attributes\CollectionOf; use HiEvents\DataTransferObjects\BaseDTO; -use HiEvents\Services\Domain\Ticket\DTO\OrderTicketPriceDTO; +use HiEvents\Services\Domain\Product\DTO\OrderProductPriceDTO; use Illuminate\Support\Collection; -class TicketOrderDetailsDTO extends BaseDTO +class ProductOrderDetailsDTO extends BaseDTO { public function __construct( - public readonly int $ticket_id, - #[CollectionOf(OrderTicketPriceDTO::class)] + public readonly int $product_id, + #[CollectionOf(OrderProductPriceDTO::class)] public Collection $quantities, ) { diff --git a/backend/app/Services/Handlers/Order/GetOrderPublicHandler.php b/backend/app/Services/Handlers/Order/GetOrderPublicHandler.php index 214257aab2..2389d4115f 100644 --- a/backend/app/Services/Handlers/Order/GetOrderPublicHandler.php +++ b/backend/app/Services/Handlers/Order/GetOrderPublicHandler.php @@ -6,12 +6,12 @@ use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\EventSettingDomainObject; use HiEvents\DomainObjects\Generated\EventDomainObjectAbstract; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; use HiEvents\DomainObjects\OrderDomainObject; use HiEvents\DomainObjects\OrderItemDomainObject; use HiEvents\DomainObjects\Status\OrderStatus; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Exceptions\UnauthorizedException; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; @@ -59,13 +59,13 @@ private function getOrderDomainObject(GetOrderPublicDTO $getOrderData): ?OrderDo domainObject: AttendeeDomainObject::class, nested: [ new Relationship( - domainObject: TicketDomainObject::class, + domainObject: ProductDomainObject::class, nested: [ new Relationship( - domainObject: TicketPriceDomainObject::class, + domainObject: ProductPriceDomainObject::class, ) ], - name: TicketDomainObjectAbstract::SINGULAR_NAME, + name: ProductDomainObjectAbstract::SINGULAR_NAME, ) ], )) diff --git a/backend/app/Services/Handlers/Product/CreateProductHandler.php b/backend/app/Services/Handlers/Product/CreateProductHandler.php new file mode 100644 index 0000000000..a0e813a2f2 --- /dev/null +++ b/backend/app/Services/Handlers/Product/CreateProductHandler.php @@ -0,0 +1,60 @@ +prices->map(fn(ProductPriceDTO $price) => ProductPriceDomainObject::hydrateFromArray([ + ProductPriceDomainObjectAbstract::PRICE => $productsData->type === ProductPriceType::FREE ? 0.00 : $price->price, + ProductPriceDomainObjectAbstract::LABEL => $price->label, + ProductPriceDomainObjectAbstract::SALE_START_DATE => $price->sale_start_date, + ProductPriceDomainObjectAbstract::SALE_END_DATE => $price->sale_end_date, + ProductPriceDomainObjectAbstract::INITIAL_QUANTITY_AVAILABLE => $price->initial_quantity_available, + ProductPriceDomainObjectAbstract::IS_HIDDEN => $price->is_hidden, + ])); + + return $this->productCreateService->createProduct( + product: (new ProductDomainObject()) + ->setTitle($productsData->title) + ->setType($productsData->type->name) + ->setOrder($productsData->order) + ->setSaleStartDate($productsData->sale_start_date) + ->setSaleEndDate($productsData->sale_end_date) + ->setMaxPerOrder($productsData->max_per_order) + ->setDescription($productsData->description) + ->setMinPerOrder($productsData->min_per_order) + ->setIsHidden($productsData->is_hidden) + ->setHideBeforeSaleStartDate($productsData->hide_before_sale_start_date) + ->setHideAfterSaleEndDate($productsData->hide_after_sale_end_date) + ->setHideWhenSoldOut($productsData->hide_when_sold_out) + ->setShowQuantityRemaining($productsData->show_quantity_remaining) + ->setIsHiddenWithoutPromoCode($productsData->is_hidden_without_promo_code) + ->setProductPrices($productPrices) + ->setEventId($productsData->event_id), + accountId: $productsData->account_id, + taxAndFeeIds: $productsData->tax_and_fee_ids, + ); + } +} diff --git a/backend/app/Services/Handlers/Product/DTO/UpsertProductDTO.php b/backend/app/Services/Handlers/Product/DTO/UpsertProductDTO.php new file mode 100644 index 0000000000..736e637c5a --- /dev/null +++ b/backend/app/Services/Handlers/Product/DTO/UpsertProductDTO.php @@ -0,0 +1,41 @@ +databaseManager->transaction(function () use ($productId, $eventId) { + $this->deleteProduct($productId, $eventId); + }); + } + + /** + * @throws CannotDeleteEntityException + */ + private function deleteProduct(int $productId, int $eventId): void + { + $attendees = $this->attendeeRepository->findWhere( + [ + AttendeeDomainObjectAbstract::EVENT_ID => $eventId, + AttendeeDomainObjectAbstract::PRODUCT_ID => $productId, + ] + ); + + if ($attendees->count() > 0) { + throw new CannotDeleteEntityException( + __('You cannot delete this product because it has orders associated with it. You can hide it instead.') + ); + } + + $this->productRepository->deleteWhere( + [ + ProductDomainObjectAbstract::EVENT_ID => $eventId, + ProductDomainObjectAbstract::ID => $productId, + ] + ); + + $this->productPriceRepository->deleteWhere( + [ + ProductPriceDomainObjectAbstract::PRODUCT_ID => $productId, + ] + ); + + $this->logger->info(sprintf('Product %d was deleted from event %d', $productId, $eventId), [ + 'productId' => $productId, + 'eventId' => $eventId, + ]); + } +} diff --git a/backend/app/Services/Handlers/Product/EditProductHandler.php b/backend/app/Services/Handlers/Product/EditProductHandler.php new file mode 100644 index 0000000000..abb16134e2 --- /dev/null +++ b/backend/app/Services/Handlers/Product/EditProductHandler.php @@ -0,0 +1,138 @@ +databaseManager->transaction(function () use ($productsData) { + $where = [ + 'event_id' => $productsData->event_id, + 'id' => $productsData->product_id, + ]; + + $product = $this->updateProduct($productsData, $where); + + $this->addTaxes($product, $productsData); + + $this->priceUpdateService->updatePrices( + $product, + $productsData, + $product->getProductPrices(), + $this->eventRepository->findById($productsData->event_id) + ); + + return $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) + ->findById($product->getId()); + }); + } + + /** + * @throws CannotChangeProductTypeException + */ + private function updateProduct(UpsertProductDTO $productsData, array $where): ProductDomainObject + { + $event = $this->eventRepository->findById($productsData->event_id); + + $this->validateChangeInProductType($productsData); + + $this->productRepository->updateWhere( + attributes: [ + 'title' => $productsData->title, + 'type' => $productsData->type->name, + 'order' => $productsData->order, + 'sale_start_date' => $productsData->sale_start_date + ? DateHelper::convertToUTC($productsData->sale_start_date, $event->getTimezone()) + : null, + 'sale_end_date' => $productsData->sale_end_date + ? DateHelper::convertToUTC($productsData->sale_end_date, $event->getTimezone()) + : null, + 'max_per_order' => $productsData->max_per_order, + 'description' => $this->purifier->purify($productsData->description), + 'min_per_order' => $productsData->min_per_order, + 'is_hidden' => $productsData->is_hidden, + 'hide_before_sale_start_date' => $productsData->hide_before_sale_start_date, + 'hide_after_sale_end_date' => $productsData->hide_after_sale_end_date, + 'hide_when_sold_out' => $productsData->hide_when_sold_out, + 'show_quantity_remaining' => $productsData->show_quantity_remaining, + 'is_hidden_without_promo_code' => $productsData->is_hidden_without_promo_code, + ], + where: $where + ); + + return $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) + ->findFirstWhere($where); + } + + /** + * @throws Exception + */ + private function addTaxes(ProductDomainObject $product, UpsertProductDTO $productsData): void + { + $this->taxAndProductAssociationService->addTaxesToProduct( + new TaxAndProductAssociateParams( + productId: $product->getId(), + accountId: $productsData->account_id, + taxAndFeeIds: $productsData->tax_and_fee_ids, + ) + ); + } + + /** + * @throws CannotChangeProductTypeException + * @todo - We should probably check reserved products here as well + */ + private function validateChangeInProductType(UpsertProductDTO $productsData): void + { + $product = $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) + ->findById($productsData->product_id); + + $quantitySold = $product->getProductPrices() + ->sum(fn(ProductPriceDomainObject $price) => $price->getQuantitySold()); + + if ($product->getType() !== $productsData->type->name && $quantitySold > 0) { + throw new CannotChangeProductTypeException( + __('Product type cannot be changed as products have been registered for this type') + ); + } + } +} diff --git a/backend/app/Services/Handlers/Product/GetProductsHandler.php b/backend/app/Services/Handlers/Product/GetProductsHandler.php new file mode 100644 index 0000000000..0f0a1918aa --- /dev/null +++ b/backend/app/Services/Handlers/Product/GetProductsHandler.php @@ -0,0 +1,37 @@ +productRepository + ->loadRelation(ProductPriceDomainObject::class) + ->loadRelation(TaxAndFeesDomainObject::class) + ->findByEventId($eventId, $queryParamsDTO); + + $filteredProducts = $this->productFilterService->filter( + products: $productPaginator->getCollection(), + hideSoldOutProducts: false, + ); + + $productPaginator->setCollection($filteredProducts); + + return $productPaginator; + } +} diff --git a/backend/app/Services/Handlers/Product/SortProductsHandler.php b/backend/app/Services/Handlers/Product/SortProductsHandler.php new file mode 100644 index 0000000000..7f7bd80bbc --- /dev/null +++ b/backend/app/Services/Handlers/Product/SortProductsHandler.php @@ -0,0 +1,41 @@ +sortBy('order')->pluck('id')->toArray(); + + $productIdsResult = $this->productRepository->findWhere([ + 'event_id' => $eventId, + ]) + ->map(fn($product) => $product->getId()) + ->toArray(); + + // Check if the orderedProductIds array exactly matches the product IDs from the database + $missingInOrdered = array_diff($productIdsResult, $orderedProductIds); + $extraInOrdered = array_diff($orderedProductIds, $productIdsResult); + + if (!empty($missingInOrdered) || !empty($extraInOrdered)) { + throw new ResourceConflictException( + __('The ordered product IDs must exactly match all products for the event without missing or extra IDs.') + ); + } + + $this->productRepository->sortProducts($eventId, $orderedProductIds); + } +} diff --git a/backend/app/Services/Handlers/PromoCode/CreatePromoCodeHandler.php b/backend/app/Services/Handlers/PromoCode/CreatePromoCodeHandler.php index 84e954a393..dac9da0528 100644 --- a/backend/app/Services/Handlers/PromoCode/CreatePromoCodeHandler.php +++ b/backend/app/Services/Handlers/PromoCode/CreatePromoCodeHandler.php @@ -5,7 +5,7 @@ use HiEvents\DomainObjects\PromoCodeDomainObject; use HiEvents\Exceptions\ResourceConflictException; use HiEvents\Services\Domain\PromoCode\CreatePromoCodeService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\PromoCode\DTO\UpsertPromoCodeDTO; readonly class CreatePromoCodeHandler @@ -18,7 +18,7 @@ public function __construct( /** * @throws ResourceConflictException - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function handle(int $eventId, UpsertPromoCodeDTO $promoCodeDTO): PromoCodeDomainObject { @@ -30,7 +30,7 @@ public function handle(int $eventId, UpsertPromoCodeDTO $promoCodeDTO): PromoCod ->setDiscount($promoCodeDTO->discount) ->setExpiryDate($promoCodeDTO->expiry_date) ->setMaxAllowedUsages($promoCodeDTO->max_allowed_usages) - ->setApplicableTicketIds($promoCodeDTO->applicable_ticket_ids) + ->setApplicableProductIds($promoCodeDTO->applicable_product_ids) ); } } diff --git a/backend/app/Services/Handlers/PromoCode/DTO/UpsertPromoCodeDTO.php b/backend/app/Services/Handlers/PromoCode/DTO/UpsertPromoCodeDTO.php index 2ae5edfa4a..aa3976792a 100644 --- a/backend/app/Services/Handlers/PromoCode/DTO/UpsertPromoCodeDTO.php +++ b/backend/app/Services/Handlers/PromoCode/DTO/UpsertPromoCodeDTO.php @@ -9,7 +9,7 @@ class UpsertPromoCodeDTO public function __construct( public readonly string $code, public readonly int $event_id, - public readonly array $applicable_ticket_ids, + public readonly array $applicable_product_ids, public readonly PromoCodeDiscountTypeEnum $discount_type, public readonly ?float $discount, public readonly ?string $expiry_date, diff --git a/backend/app/Services/Handlers/PromoCode/UpdatePromoCodeHandler.php b/backend/app/Services/Handlers/PromoCode/UpdatePromoCodeHandler.php index 99197f9fbd..31ccddfe42 100644 --- a/backend/app/Services/Handlers/PromoCode/UpdatePromoCodeHandler.php +++ b/backend/app/Services/Handlers/PromoCode/UpdatePromoCodeHandler.php @@ -9,28 +9,28 @@ use HiEvents\Helper\DateHelper; use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface; -use HiEvents\Services\Domain\Ticket\EventTicketValidationService; -use HiEvents\Services\Domain\Ticket\Exception\UnrecognizedTicketIdException; +use HiEvents\Services\Domain\Product\EventProductValidationService; +use HiEvents\Services\Domain\Product\Exception\UnrecognizedProductIdException; use HiEvents\Services\Handlers\PromoCode\DTO\UpsertPromoCodeDTO; readonly class UpdatePromoCodeHandler { public function __construct( - private PromoCodeRepositoryInterface $promoCodeRepository, - private EventTicketValidationService $eventTicketValidationService, - private EventRepositoryInterface $eventRepository, + private PromoCodeRepositoryInterface $promoCodeRepository, + private EventProductValidationService $eventProductValidationService, + private EventRepositoryInterface $eventRepository, ) { } /** * @throws ResourceConflictException - * @throws UnrecognizedTicketIdException + * @throws UnrecognizedProductIdException */ public function handle(int $promoCodeId, UpsertPromoCodeDTO $promoCodeDTO): PromoCodeDomainObject { - $this->eventTicketValidationService->validateTicketIds( - ticketIds: $promoCodeDTO->applicable_ticket_ids, + $this->eventProductValidationService->validateProductIds( + productIds: $promoCodeDTO->applicable_product_ids, eventId: $promoCodeDTO->event_id ); @@ -57,7 +57,7 @@ public function handle(int $promoCodeId, UpsertPromoCodeDTO $promoCodeDTO): Prom ? DateHelper::convertToUTC($promoCodeDTO->expiry_date, $event->getTimezone()) : null, PromoCodeDomainObjectAbstract::MAX_ALLOWED_USAGES => $promoCodeDTO->max_allowed_usages, - PromoCodeDomainObjectAbstract::APPLICABLE_TICKET_IDS => $promoCodeDTO->applicable_ticket_ids, + PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS => $promoCodeDTO->applicable_product_ids, ]); } } diff --git a/backend/app/Services/Handlers/Question/CreateQuestionHandler.php b/backend/app/Services/Handlers/Question/CreateQuestionHandler.php index a8f641e768..bfa0c73df1 100644 --- a/backend/app/Services/Handlers/Question/CreateQuestionHandler.php +++ b/backend/app/Services/Handlers/Question/CreateQuestionHandler.php @@ -34,7 +34,7 @@ public function handle(UpsertQuestionDTO $createQuestionDTO): QuestionDomainObje return $this->createQuestionService->createQuestion( $question, - $createQuestionDTO->ticket_ids, + $createQuestionDTO->product_ids, ); } } diff --git a/backend/app/Services/Handlers/Question/DTO/UpsertQuestionDTO.php b/backend/app/Services/Handlers/Question/DTO/UpsertQuestionDTO.php index c10345406e..ffb76e271f 100644 --- a/backend/app/Services/Handlers/Question/DTO/UpsertQuestionDTO.php +++ b/backend/app/Services/Handlers/Question/DTO/UpsertQuestionDTO.php @@ -14,7 +14,7 @@ public function __construct( public bool $required, public ?array $options, public int $event_id, - public array $ticket_ids, + public array $product_ids, public bool $is_hidden, public QuestionBelongsTo $belongs_to, public ?string $description = null, diff --git a/backend/app/Services/Handlers/Question/EditQuestionHandler.php b/backend/app/Services/Handlers/Question/EditQuestionHandler.php index 3cfe83a15f..40c30be8ca 100644 --- a/backend/app/Services/Handlers/Question/EditQuestionHandler.php +++ b/backend/app/Services/Handlers/Question/EditQuestionHandler.php @@ -34,7 +34,7 @@ public function handle(int $questionId, UpsertQuestionDTO $createQuestionDTO): Q return $this->editQuestionService->editQuestion( question: $question, - ticketIds: $createQuestionDTO->ticket_ids, + productIds: $createQuestionDTO->product_ids, ); } } diff --git a/backend/app/Services/Handlers/Question/SortQuestionsHandler.php b/backend/app/Services/Handlers/Question/SortQuestionsHandler.php index 379271f9ef..bd3894d273 100644 --- a/backend/app/Services/Handlers/Question/SortQuestionsHandler.php +++ b/backend/app/Services/Handlers/Question/SortQuestionsHandler.php @@ -20,7 +20,7 @@ public function handle(int $eventId, array $data): void $questionIdResult = $this->questionRepository->findWhere([ 'event_id' => $eventId, ]) - ->map(fn($ticket) => $ticket->getId()) + ->map(fn($product) => $product->getId()) ->toArray(); $extraInOrdered = array_diff($orderedQuestionIds, $questionIdResult); diff --git a/backend/app/Services/Handlers/Ticket/CreateTicketHandler.php b/backend/app/Services/Handlers/Ticket/CreateTicketHandler.php deleted file mode 100644 index d8edb7e32c..0000000000 --- a/backend/app/Services/Handlers/Ticket/CreateTicketHandler.php +++ /dev/null @@ -1,60 +0,0 @@ -prices->map(fn(TicketPriceDTO $price) => TicketPriceDomainObject::hydrateFromArray([ - TicketPriceDomainObjectAbstract::PRICE => $ticketsData->type === TicketType::FREE ? 0.00 : $price->price, - TicketPriceDomainObjectAbstract::LABEL => $price->label, - TicketPriceDomainObjectAbstract::SALE_START_DATE => $price->sale_start_date, - TicketPriceDomainObjectAbstract::SALE_END_DATE => $price->sale_end_date, - TicketPriceDomainObjectAbstract::INITIAL_QUANTITY_AVAILABLE => $price->initial_quantity_available, - TicketPriceDomainObjectAbstract::IS_HIDDEN => $price->is_hidden, - ])); - - return $this->ticketCreateService->createTicket( - ticket: (new TicketDomainObject()) - ->setTitle($ticketsData->title) - ->setType($ticketsData->type->name) - ->setOrder($ticketsData->order) - ->setSaleStartDate($ticketsData->sale_start_date) - ->setSaleEndDate($ticketsData->sale_end_date) - ->setMaxPerOrder($ticketsData->max_per_order) - ->setDescription($ticketsData->description) - ->setMinPerOrder($ticketsData->min_per_order) - ->setIsHidden($ticketsData->is_hidden) - ->setHideBeforeSaleStartDate($ticketsData->hide_before_sale_start_date) - ->setHideAfterSaleEndDate($ticketsData->hide_after_sale_end_date) - ->setHideWhenSoldOut($ticketsData->hide_when_sold_out) - ->setShowQuantityRemaining($ticketsData->show_quantity_remaining) - ->setIsHiddenWithoutPromoCode($ticketsData->is_hidden_without_promo_code) - ->setTicketPrices($ticketPrices) - ->setEventId($ticketsData->event_id), - accountId: $ticketsData->account_id, - taxAndFeeIds: $ticketsData->tax_and_fee_ids, - ); - } -} diff --git a/backend/app/Services/Handlers/Ticket/DTO/UpsertTicketDTO.php b/backend/app/Services/Handlers/Ticket/DTO/UpsertTicketDTO.php deleted file mode 100644 index 8563bf63a7..0000000000 --- a/backend/app/Services/Handlers/Ticket/DTO/UpsertTicketDTO.php +++ /dev/null @@ -1,41 +0,0 @@ -databaseManager->transaction(function () use ($ticketId, $eventId) { - $this->deleteTicket($ticketId, $eventId); - }); - } - - /** - * @throws CannotDeleteEntityException - */ - private function deleteTicket(int $ticketId, int $eventId): void - { - $attendees = $this->attendeeRepository->findWhere( - [ - AttendeeDomainObjectAbstract::EVENT_ID => $eventId, - AttendeeDomainObjectAbstract::TICKET_ID => $ticketId, - ] - ); - - if ($attendees->count() > 0) { - throw new CannotDeleteEntityException( - __('You cannot delete this ticket because it has orders associated with it. You can hide it instead.') - ); - } - - $this->ticketRepository->deleteWhere( - [ - TicketDomainObjectAbstract::EVENT_ID => $eventId, - TicketDomainObjectAbstract::ID => $ticketId, - ] - ); - - $this->ticketPriceRepository->deleteWhere( - [ - TicketPriceDomainObjectAbstract::TICKET_ID => $ticketId, - ] - ); - - $this->logger->info(sprintf('Ticket %d was deleted from event %d', $ticketId, $eventId), [ - 'ticketId' => $ticketId, - 'eventId' => $eventId, - ]); - } -} diff --git a/backend/app/Services/Handlers/Ticket/EditTicketHandler.php b/backend/app/Services/Handlers/Ticket/EditTicketHandler.php deleted file mode 100644 index e33d17cf21..0000000000 --- a/backend/app/Services/Handlers/Ticket/EditTicketHandler.php +++ /dev/null @@ -1,138 +0,0 @@ -databaseManager->transaction(function () use ($ticketsData) { - $where = [ - 'event_id' => $ticketsData->event_id, - 'id' => $ticketsData->ticket_id, - ]; - - $ticket = $this->updateTicket($ticketsData, $where); - - $this->addTaxes($ticket, $ticketsData); - - $this->priceUpdateService->updatePrices( - $ticket, - $ticketsData, - $ticket->getTicketPrices(), - $this->eventRepository->findById($ticketsData->event_id) - ); - - return $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) - ->findById($ticket->getId()); - }); - } - - /** - * @throws CannotChangeTicketTypeException - */ - private function updateTicket(UpsertTicketDTO $ticketsData, array $where): TicketDomainObject - { - $event = $this->eventRepository->findById($ticketsData->event_id); - - $this->validateChangeInTicketType($ticketsData); - - $this->ticketRepository->updateWhere( - attributes: [ - 'title' => $ticketsData->title, - 'type' => $ticketsData->type->name, - 'order' => $ticketsData->order, - 'sale_start_date' => $ticketsData->sale_start_date - ? DateHelper::convertToUTC($ticketsData->sale_start_date, $event->getTimezone()) - : null, - 'sale_end_date' => $ticketsData->sale_end_date - ? DateHelper::convertToUTC($ticketsData->sale_end_date, $event->getTimezone()) - : null, - 'max_per_order' => $ticketsData->max_per_order, - 'description' => $this->purifier->purify($ticketsData->description), - 'min_per_order' => $ticketsData->min_per_order, - 'is_hidden' => $ticketsData->is_hidden, - 'hide_before_sale_start_date' => $ticketsData->hide_before_sale_start_date, - 'hide_after_sale_end_date' => $ticketsData->hide_after_sale_end_date, - 'hide_when_sold_out' => $ticketsData->hide_when_sold_out, - 'show_quantity_remaining' => $ticketsData->show_quantity_remaining, - 'is_hidden_without_promo_code' => $ticketsData->is_hidden_without_promo_code, - ], - where: $where - ); - - return $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) - ->findFirstWhere($where); - } - - /** - * @throws Exception - */ - private function addTaxes(TicketDomainObject $ticket, UpsertTicketDTO $ticketsData): void - { - $this->taxAndTicketAssociationService->addTaxesToTicket( - new TaxAndTicketAssociateParams( - ticketId: $ticket->getId(), - accountId: $ticketsData->account_id, - taxAndFeeIds: $ticketsData->tax_and_fee_ids, - ) - ); - } - - /** - * @throws CannotChangeTicketTypeException - * @todo - We should probably check reserved tickets here as well - */ - private function validateChangeInTicketType(UpsertTicketDTO $ticketsData): void - { - $ticket = $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) - ->findById($ticketsData->ticket_id); - - $quantitySold = $ticket->getTicketPrices() - ->sum(fn(TicketPriceDomainObject $price) => $price->getQuantitySold()); - - if ($ticket->getType() !== $ticketsData->type->name && $quantitySold > 0) { - throw new CannotChangeTicketTypeException( - __('Ticket type cannot be changed as tickets have been registered for this type') - ); - } - } -} diff --git a/backend/app/Services/Handlers/Ticket/GetTicketsHandler.php b/backend/app/Services/Handlers/Ticket/GetTicketsHandler.php deleted file mode 100644 index 898e952d34..0000000000 --- a/backend/app/Services/Handlers/Ticket/GetTicketsHandler.php +++ /dev/null @@ -1,37 +0,0 @@ -ticketRepository - ->loadRelation(TicketPriceDomainObject::class) - ->loadRelation(TaxAndFeesDomainObject::class) - ->findByEventId($eventId, $queryParamsDTO); - - $filteredTickets = $this->ticketFilterService->filter( - tickets: $ticketPaginator->getCollection(), - hideSoldOutTickets: false, - ); - - $ticketPaginator->setCollection($filteredTickets); - - return $ticketPaginator; - } -} diff --git a/backend/app/Services/Handlers/Ticket/SortTicketsHandler.php b/backend/app/Services/Handlers/Ticket/SortTicketsHandler.php deleted file mode 100644 index 2506098568..0000000000 --- a/backend/app/Services/Handlers/Ticket/SortTicketsHandler.php +++ /dev/null @@ -1,41 +0,0 @@ -sortBy('order')->pluck('id')->toArray(); - - $ticketIdsResult = $this->ticketRepository->findWhere([ - 'event_id' => $eventId, - ]) - ->map(fn($ticket) => $ticket->getId()) - ->toArray(); - - // Check if the orderedTicketIds array exactly matches the ticket IDs from the database - $missingInOrdered = array_diff($ticketIdsResult, $orderedTicketIds); - $extraInOrdered = array_diff($orderedTicketIds, $ticketIdsResult); - - if (!empty($missingInOrdered) || !empty($extraInOrdered)) { - throw new ResourceConflictException( - __('The ordered ticket IDs must exactly match all tickets for the event without missing or extra IDs.') - ); - } - - $this->ticketRepository->sortTickets($eventId, $orderedTicketIds); - } -} diff --git a/backend/app/Validators/CompleteOrderValidator.php b/backend/app/Validators/CompleteOrderValidator.php index 6b8ffc5b9a..fcf8bf6c13 100644 --- a/backend/app/Validators/CompleteOrderValidator.php +++ b/backend/app/Validators/CompleteOrderValidator.php @@ -6,13 +6,13 @@ use HiEvents\DomainObjects\Enums\QuestionBelongsTo; use HiEvents\DomainObjects\Generated\QuestionDomainObjectAbstract; -use HiEvents\DomainObjects\Generated\TicketDomainObjectAbstract; +use HiEvents\DomainObjects\Generated\ProductDomainObjectAbstract; use HiEvents\DomainObjects\QuestionDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; -use HiEvents\Repository\Interfaces\TicketRepositoryInterface; +use HiEvents\Repository\Interfaces\ProductRepositoryInterface; use HiEvents\Validators\Rules\AttendeeQuestionRule; use HiEvents\Validators\Rules\OrderQuestionRule; use Illuminate\Routing\Route; @@ -21,7 +21,7 @@ class CompleteOrderValidator extends BaseValidator { public function __construct( private readonly QuestionRepositoryInterface $questionRepository, - private readonly TicketRepositoryInterface $ticketRepository, + private readonly ProductRepositoryInterface $productRepository, private readonly Route $route ) { @@ -31,8 +31,8 @@ public function rules(): array { $questions = $this->questionRepository ->loadRelation( - new Relationship(TicketDomainObject::class, [ - new Relationship(TicketPriceDomainObject::class) + new Relationship(ProductDomainObject::class, [ + new Relationship(ProductPriceDomainObject::class) ]) ) ->findWhere( @@ -41,25 +41,25 @@ public function rules(): array $orderQuestions = $questions->filter( fn(QuestionDomainObject $question) => $question->getBelongsTo() === QuestionBelongsTo::ORDER->name ); - $ticketQuestions = $questions->filter( - fn(QuestionDomainObject $question) => $question->getBelongsTo() === QuestionBelongsTo::TICKET->name + $productQuestions = $questions->filter( + fn(QuestionDomainObject $question) => $question->getBelongsTo() === QuestionBelongsTo::PRODUCT->name ); - $tickets = $this->ticketRepository - ->loadRelation(TicketPriceDomainObject::class) + $products = $this->productRepository + ->loadRelation(ProductPriceDomainObject::class) ->findWhere( - [TicketDomainObjectAbstract::EVENT_ID => $this->route->parameter('event_id')] + [ProductDomainObjectAbstract::EVENT_ID => $this->route->parameter('event_id')] ); return [ 'order.first_name' => ['required', 'string', 'max:40'], 'order.last_name' => ['required', 'string', 'max:40'], - 'order.questions' => new OrderQuestionRule($orderQuestions, $tickets), + 'order.questions' => new OrderQuestionRule($orderQuestions, $products), 'order.email' => 'required|email', 'attendees.*.first_name' => ['required', 'string', 'max:40'], 'attendees.*.last_name' => ['required', 'string', 'max:40'], 'attendees.*.email' => ['required', 'email'], - 'attendees' => new AttendeeQuestionRule($ticketQuestions, $tickets), + 'attendees' => new AttendeeQuestionRule($productQuestions, $products), // Address validation is intentionally not strict, as we want to support all countries 'order.address.address_line_1' => ['string', 'max:255'], diff --git a/backend/app/Validators/Rules/AttendeeQuestionRule.php b/backend/app/Validators/Rules/AttendeeQuestionRule.php index 87640c9e59..caf5dea651 100644 --- a/backend/app/Validators/Rules/AttendeeQuestionRule.php +++ b/backend/app/Validators/Rules/AttendeeQuestionRule.php @@ -14,14 +14,14 @@ class AttendeeQuestionRule extends BaseQuestionRule protected function validateRequiredQuestionArePresent(Collection $orderAttendees): void { foreach ($orderAttendees as $attendee) { - $ticketId = $this->getTicketIdFromTicketPriceId($attendee['ticket_price_id']); + $productId = $this->getProductIdFromProductPriceId($attendee['product_price_id']); $questions = $attendee['questions'] ?? []; $requiredQuestionIds = $this->questions - ->filter(function (QuestionDomainObject $question) use ($ticketId) { + ->filter(function (QuestionDomainObject $question) use ($productId) { return $question->getRequired() && !$question->getIsHidden() - && $question->getTickets()?->map(fn($ticket) => $ticket->getId())->contains($ticketId); + && $question->getProducts()?->map(fn($product) => $product->getId())->contains($productId); }) ->map(fn(QuestionDomainObject $question) => $question->getId()); diff --git a/backend/app/Validators/Rules/BaseQuestionRule.php b/backend/app/Validators/Rules/BaseQuestionRule.php index 7571bf1334..b64da09f69 100644 --- a/backend/app/Validators/Rules/BaseQuestionRule.php +++ b/backend/app/Validators/Rules/BaseQuestionRule.php @@ -5,8 +5,8 @@ use Closure; use HiEvents\DomainObjects\Enums\QuestionTypeEnum; use HiEvents\DomainObjects\QuestionDomainObject; -use HiEvents\DomainObjects\TicketDomainObject; -use HiEvents\DomainObjects\TicketPriceDomainObject; +use HiEvents\DomainObjects\ProductDomainObject; +use HiEvents\DomainObjects\ProductPriceDomainObject; use Illuminate\Contracts\Validation\DataAwareRule; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; @@ -34,7 +34,7 @@ abstract class BaseQuestionRule implements ValidationRule, DataAwareRule, Valida protected Collection $questions; - private Collection $tickets; + private Collection $products; protected Validator $validator; @@ -44,10 +44,10 @@ abstract protected function validateRequiredQuestionArePresent(Collection $data) abstract protected function validateQuestions(mixed $data): array; - public function __construct(Collection $questions, Collection $tickets) + public function __construct(Collection $questions, Collection $products) { $this->questions = $questions; - $this->tickets = $tickets; + $this->products = $products; } public function validate(string $attribute, mixed $value, Closure $fail): void @@ -73,16 +73,16 @@ public function setData(array $data): void $this->data = $data; } - protected function getTicketIdFromTicketPriceId(int $ticketPriceId): int + protected function getProductIdFromProductPriceId(int $productPriceId): int { - $ticketPrices = new Collection(); - $this->tickets->each(fn(TicketDomainObject $ticket) => $ticketPrices->push(...$ticket->getTicketPrices())); + $productPrices = new Collection(); + $this->products->each(fn(ProductDomainObject $product) => $productPrices->push(...$product->getProductPrices())); - /** @var TicketPriceDomainObject $ticketPrice */ - $ticketPrice = $ticketPrices - ->first(fn(TicketPriceDomainObject $ticketPrice) => $ticketPrice->getId() === $ticketPriceId); + /** @var ProductPriceDomainObject $productPrice */ + $productPrice = $productPrices + ->first(fn(ProductPriceDomainObject $productPrice) => $productPrice->getId() === $productPriceId); - return $ticketPrice->getTicketId(); + return $productPrice->getProductId(); } protected function isAnswerValid(QuestionDomainObject $questionDomainObject, mixed $response): bool diff --git a/backend/database/migrations/2024_09_20_032323_rename_tickets_to_products.php b/backend/database/migrations/2024_09_20_032323_rename_tickets_to_products.php new file mode 100644 index 0000000000..907f3fabba --- /dev/null +++ b/backend/database/migrations/2024_09_20_032323_rename_tickets_to_products.php @@ -0,0 +1,182 @@ +renameColumn('ticket_id', 'product_id'); + $table->renameColumn('ticket_price_id', 'product_price_id'); + }); + + Schema::table('attendees', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + $table->renameColumn('ticket_price_id', 'product_price_id'); + }); + + Schema::table('product_prices', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('product_taxes_and_fees', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('product_questions', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('product_check_in_lists', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('attendee_check_ins', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('product_capacity_assignments', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('question_answers', function (Blueprint $table) { + $table->renameColumn('ticket_id', 'product_id'); + }); + + Schema::table('promo_codes', function (Blueprint $table) { + $table->renameColumn('applicable_ticket_ids', 'applicable_product_ids'); + }); + + Schema::table('event_statistics', function (Blueprint $table) { + $table->renameColumn('tickets_sold', 'products_sold'); + }); + + Schema::table('event_daily_statistics', function (Blueprint $table) { + $table->renameColumn('tickets_sold', 'products_sold'); + }); + + Schema::table('messages', function (Blueprint $table) { + $table->renameColumn('ticket_ids', 'product_ids'); + }); + + Schema::table('event_settings', function (Blueprint $table) { + $table->renameColumn('ticket_page_message', 'product_page_message'); + }); + + $this->renameIndex('idx_ticket_prices_ticket_id', 'idx_product_prices_product_id'); + $this->renameIndex('order_items_ticket_id_index', 'order_items_product_id_index'); + $this->renameIndex('order_items_ticket_price_id_index', 'order_items_product_price_id_index'); + $this->renameIndex('idx_attendees_ticket_id_deleted_at', 'idx_attendees_product_id_deleted_at'); + $this->renameIndex('ticket_tax_and_fees_ticket_id_index', 'product_tax_and_fees_product_id_index'); + $this->renameIndex('idx_ticket_questions_active', 'idx_product_questions_active'); + $this->renameIndex('ticket_check_in_lists_ticket_id_check_in_list_id_index', 'product_check_in_lists_product_id_check_in_list_id_index'); + $this->renameIndex('idx_ticket_check_in_lists_ticket_id_deleted_at', 'idx_product_check_in_lists_product_id_deleted_at'); + $this->renameIndex('attendee_check_ins_ticket_id_index', 'attendee_check_ins_product_id_index'); + $this->renameIndex('ticket_capacity_assignments_ticket_id_index', 'product_capacity_assignments_product_id_index'); + $this->renameIndex('attendees_ticket_prices_id_fk', 'attendees_product_prices_id_fk'); + } + + public function down(): void + { + Schema::rename('products', 'tickets'); + + Schema::rename('product_prices', 'ticket_prices'); + Schema::rename('product_taxes_and_fees', 'ticket_taxes_and_fees'); + Schema::rename('product_questions', 'ticket_questions'); + Schema::rename('product_check_in_lists', 'ticket_check_in_lists'); + Schema::rename('product_capacity_assignments', 'ticket_capacity_assignments'); + + // Rename sequences back + DB::statement('ALTER SEQUENCE product_capacity_assignments_id_seq RENAME TO ticket_capacity_assignments_id_seq'); + DB::statement('ALTER SEQUENCE product_check_in_lists_id_seq RENAME TO ticket_check_in_lists_id_seq'); + + Schema::table('order_items', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + $table->renameColumn('product_price_id', 'ticket_price_id'); + }); + + Schema::table('attendees', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + $table->renameColumn('product_price_id', 'ticket_price_id'); + }); + + Schema::table('ticket_prices', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('ticket_taxes_and_fees', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('ticket_questions', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('ticket_check_in_lists', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('attendee_check_ins', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('ticket_capacity_assignments', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('question_answers', function (Blueprint $table) { + $table->renameColumn('product_id', 'ticket_id'); + }); + + Schema::table('promo_codes', function (Blueprint $table) { + $table->renameColumn('applicable_product_ids', 'applicable_ticket_ids'); + }); + + Schema::table('event_statistics', function (Blueprint $table) { + $table->renameColumn('products_sold', 'tickets_sold'); + }); + + Schema::table('event_daily_statistics', function (Blueprint $table) { + $table->renameColumn('products_sold', 'tickets_sold'); + }); + + Schema::table('messages', function (Blueprint $table) { + $table->renameColumn('product_ids', 'ticket_ids'); + }); + + Schema::table('event_settings', function (Blueprint $table) { + $table->renameColumn('product_page_message', 'ticket_page_message'); + }); + + $this->renameIndex('idx_product_prices_product_id', 'idx_ticket_prices_ticket_id'); + $this->renameIndex('order_items_product_id_index', 'order_items_ticket_id_index'); + $this->renameIndex('order_items_product_price_id_index', 'order_items_ticket_price_id_index'); + $this->renameIndex('idx_attendees_product_id_deleted_at', 'idx_attendees_ticket_id_deleted_at'); + $this->renameIndex('product_tax_and_fees_product_id_index', 'ticket_tax_and_fees_ticket_id_index'); + $this->renameIndex('idx_product_questions_active', 'idx_ticket_questions_active'); + $this->renameIndex('product_check_in_lists_product_id_check_in_list_id_index', 'ticket_check_in_lists_ticket_id_check_in_list_id_index'); + $this->renameIndex('idx_product_check_in_lists_product_id_deleted_at', 'idx_ticket_check_in_lists_ticket_id_deleted_at'); + $this->renameIndex('attendee_check_ins_product_id_index', 'attendee_check_ins_ticket_id_index'); + $this->renameIndex('product_capacity_assignments_product_id_index', 'ticket_capacity_assignments_ticket_id_index'); + $this->renameIndex('attendees_product_prices_id_fk', 'attendees_ticket_prices_id_fk'); + } + + private function renameIndex($from, $to): void + { + DB::statement("ALTER INDEX IF EXISTS {$from} RENAME TO {$to}"); + } +}; diff --git a/backend/database/migrations/2024_09_20_032838_add_product_type_to_products.php b/backend/database/migrations/2024_09_20_032838_add_product_type_to_products.php new file mode 100644 index 0000000000..fd8974b75a --- /dev/null +++ b/backend/database/migrations/2024_09_20_032838_add_product_type_to_products.php @@ -0,0 +1,24 @@ +enum('product_type', ProductType::valuesArray()) + ->default(ProductType::TICKET->name) + ->after('id'); + }); + } + + public function down(): void + { + Schema::table('products', static function (Blueprint $table) { + $table->dropColumn('product_type'); + }); + } +}; diff --git a/backend/routes/api.php b/backend/routes/api.php index 540639f1df..ef2909c2a0 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -87,12 +87,12 @@ use HiEvents\Http\Actions\TaxesAndFees\DeleteTaxOrFeeAction; use HiEvents\Http\Actions\TaxesAndFees\EditTaxOrFeeAction; use HiEvents\Http\Actions\TaxesAndFees\GetTaxOrFeeAction; -use HiEvents\Http\Actions\Tickets\CreateTicketAction; -use HiEvents\Http\Actions\Tickets\DeleteTicketAction; -use HiEvents\Http\Actions\Tickets\EditTicketAction; -use HiEvents\Http\Actions\Tickets\GetTicketAction; -use HiEvents\Http\Actions\Tickets\GetTicketsAction; -use HiEvents\Http\Actions\Tickets\SortTicketsAction; +use HiEvents\Http\Actions\Products\CreateProductAction; +use HiEvents\Http\Actions\Products\DeleteProductAction; +use HiEvents\Http\Actions\Products\EditProductAction; +use HiEvents\Http\Actions\Products\GetProductAction; +use HiEvents\Http\Actions\Products\GetProductsAction; +use HiEvents\Http\Actions\Products\SortProductsAction; use HiEvents\Http\Actions\Users\CancelEmailChangeAction; use HiEvents\Http\Actions\Users\ConfirmEmailAddressAction; use HiEvents\Http\Actions\Users\ConfirmEmailChangeAction; @@ -171,12 +171,12 @@ function (Router $router): void { $router->put('/events/{event_id}/status', UpdateEventStatusAction::class); $router->post('/events/{event_id}/duplicate', DuplicateEventAction::class); - $router->post('/events/{event_id}/tickets', CreateTicketAction::class); - $router->post('/events/{event_id}/tickets/sort', SortTicketsAction::class); - $router->put('/events/{event_id}/tickets/{ticket_id}', EditTicketAction::class); - $router->get('/events/{event_id}/tickets/{ticket_id}', GetTicketAction::class); - $router->delete('/events/{event_id}/tickets/{ticket_id}', DeleteTicketAction::class); - $router->get('/events/{event_id}/tickets', GetTicketsAction::class); + $router->post('/events/{event_id}/products', CreateProductAction::class); + $router->post('/events/{event_id}/products/sort', SortProductsAction::class); + $router->put('/events/{event_id}/products/{ticket_id}', EditProductAction::class); + $router->get('/events/{event_id}/products/{ticket_id}', GetProductAction::class); + $router->delete('/events/{event_id}/products/{ticket_id}', DeleteProductAction::class); + $router->get('/events/{event_id}/products', GetProductsAction::class); $router->get('/events/{event_id}/check_in_stats', GetEventCheckInStatsAction::class); $router->get('/events/{event_id}/stats', GetEventStatsAction::class); @@ -244,8 +244,8 @@ function (Router $router): void { // Events $router->get('/events/{event_id}', GetEventPublicAction::class); - // Tickets - $router->get('/events/{event_id}/tickets', GetEventPublicAction::class); + // Products + $router->get('/events/{event_id}/products', GetEventPublicAction::class); // Orders $router->post('/events/{event_id}/order', CreateOrderActionPublic::class); diff --git a/backend/tests/Unit/Services/Domain/Order/OrderCancelServiceTest.php b/backend/tests/Unit/Services/Domain/Order/OrderCancelServiceTest.php index 76a5c1af44..a65d2b0268 100644 --- a/backend/tests/Unit/Services/Domain/Order/OrderCancelServiceTest.php +++ b/backend/tests/Unit/Services/Domain/Order/OrderCancelServiceTest.php @@ -12,7 +12,7 @@ use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\OrderRepositoryInterface; use HiEvents\Services\Domain\Order\OrderCancelService; -use HiEvents\Services\Domain\Ticket\TicketQuantityUpdateService; +use HiEvents\Services\Domain\Product\ProductQuantityUpdateService; use Illuminate\Contracts\Mail\Mailer; use Illuminate\Database\DatabaseManager; use Illuminate\Support\Collection; @@ -27,7 +27,7 @@ class OrderCancelServiceTest extends TestCase private EventRepositoryInterface $eventRepository; private OrderRepositoryInterface $orderRepository; private DatabaseManager $databaseManager; - private TicketQuantityUpdateService $ticketQuantityService; + private ProductQuantityUpdateService $ticketQuantityService; private OrderCancelService $service; protected function setUp(): void @@ -39,7 +39,7 @@ protected function setUp(): void $this->eventRepository = m::mock(EventRepositoryInterface::class); $this->orderRepository = m::mock(OrderRepositoryInterface::class); $this->databaseManager = m::mock(DatabaseManager::class); - $this->ticketQuantityService = m::mock(TicketQuantityUpdateService::class); + $this->ticketQuantityService = m::mock(ProductQuantityUpdateService::class); $this->service = new OrderCancelService( mailer: $this->mailer, diff --git a/backend/tests/Unit/Services/Handlers/Event/GetPublicEventHandlerTest.php b/backend/tests/Unit/Services/Handlers/Event/GetPublicEventHandlerTest.php index a96224f20f..f0b90dd8b7 100644 --- a/backend/tests/Unit/Services/Handlers/Event/GetPublicEventHandlerTest.php +++ b/backend/tests/Unit/Services/Handlers/Event/GetPublicEventHandlerTest.php @@ -7,7 +7,7 @@ use HiEvents\Repository\Interfaces\EventRepositoryInterface; use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface; use HiEvents\Services\Domain\Event\EventPageViewIncrementService; -use HiEvents\Services\Domain\Ticket\TicketFilterService; +use HiEvents\Services\Domain\Product\ProductFilterService; use HiEvents\Services\Handlers\Event\DTO\GetPublicEventDTO; use HiEvents\Services\Handlers\Event\GetPublicEventHandler; use Mockery as m; @@ -17,7 +17,7 @@ class GetPublicEventHandlerTest extends TestCase { private EventRepositoryInterface $eventRepository; private PromoCodeRepositoryInterface $promoCodeRepository; - private TicketFilterService $ticketFilterService; + private ProductFilterService $ticketFilterService; private EventPageViewIncrementService $eventPageViewIncrementService; private GetPublicEventHandler $handler; @@ -27,7 +27,7 @@ protected function setUp(): void $this->eventRepository = m::mock(EventRepositoryInterface::class); $this->promoCodeRepository = m::mock(PromoCodeRepositoryInterface::class); - $this->ticketFilterService = m::mock(TicketFilterService::class); + $this->ticketFilterService = m::mock(ProductFilterService::class); $this->eventPageViewIncrementService = m::mock(EventPageViewIncrementService::class); $this->handler = new GetPublicEventHandler( diff --git a/frontend/scripts/rename.sh b/frontend/scripts/rename.sh new file mode 100755 index 0000000000..84c92580c3 --- /dev/null +++ b/frontend/scripts/rename.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +replace_content() { + local file="$1" + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS version + sed -i '' -e 's/ticket/product/g; s/Ticket/Product/g; s/TICKET/PRODUCT/g' "$file" + else + # Linux version + sed -i 's/ticket/product/g; s/Ticket/Product/g; s/TICKET/PRODUCT/g' "$file" + fi +} + +rename_item() { + local item="$1" + local dir=$(dirname "$item") + local base=$(basename "$item") + local newbase=$(echo "$base" | sed 's/ticket/product/g; s/Ticket/Product/g; s/TICKET/PRODUCT/g') + + if [ "$base" != "$newbase" ]; then + mv "$item" "$dir/$newbase" + echo "Renamed: $item -> $dir/$newbase" + fi +} + +process_directory() { + local dir="$1" + + # First, rename directories (bottom-up to avoid path issues) + find "$dir" -depth -type d | while read -r item; do + if echo "$item" | grep -qi "ticket"; then + rename_item "$item" + fi + done + + # Then, find all files in the directory and its subdirectories + find "$dir" -type f | while read -r file; do + # Check if the file name contains "ticket" (case insensitive) + if echo "$file" | grep -qi "ticket"; then + rename_item "$file" + fi + + # Check if the file content contains "ticket" (case insensitive) + if grep -qi "ticket" "$file"; then + replace_content "$file" + echo "Modified content: $file" + fi + done +} + +if [ $# -eq 0 ]; then + echo "Usage: $0 " + exit 1 +fi + +if [ ! -d "$1" ]; then + echo "Error: $1 is not a directory" + exit 1 +fi + +process_directory "$1" + +# Remove any leftover -e files (backup files created by sed on some systems) +find "$1" -name "*-e" -type f -delete + +echo "Renaming and replacement complete." +echo "Removed any leftover -e backup files." diff --git a/frontend/src/api/attendee.client.ts b/frontend/src/api/attendee.client.ts index c420ea5084..b4902b3010 100644 --- a/frontend/src/api/attendee.client.ts +++ b/frontend/src/api/attendee.client.ts @@ -8,8 +8,8 @@ export interface EditAttendeeRequest { first_name: string; last_name: string; email: string; - ticket_id?: IdParam; - ticket_price_id?: IdParam; + product_id?: IdParam; + product_price_id?: IdParam; status?: string; } @@ -62,8 +62,8 @@ export const attendeesClient = { return new Blob([response.data]); }, - resendTicket: async (eventId: IdParam, attendeeId: IdParam) => { - return await api.post(`events/${eventId}/attendees/${attendeeId}/resend-ticket`); + resendProduct: async (eventId: IdParam, attendeeId: IdParam) => { + return await api.post(`events/${eventId}/attendees/${attendeeId}/resend-product`); }, } diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 67342e3c6e..eae8e0b763 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -22,7 +22,7 @@ const ALLOWED_UNAUTHENTICATED_PATHS = [ 'print', '/order/', 'widget', - '/ticket/', + '/product/', 'check-in', ]; diff --git a/frontend/src/api/order.client.ts b/frontend/src/api/order.client.ts index 9ee16ec560..a639ada6f7 100644 --- a/frontend/src/api/order.client.ts +++ b/frontend/src/api/order.client.ts @@ -17,7 +17,7 @@ export interface OrderDetails { } export interface AttendeeDetails extends OrderDetails { - ticket_id: number, + product_id: number, } export interface FinaliseOrderPayload { @@ -26,19 +26,19 @@ export interface FinaliseOrderPayload { } -export interface TicketPriceQuantityFormValue { +export interface ProductPriceQuantityFormValue { price?: number, quantity: number, price_id: number, } -export interface TicketFormValue { - ticket_id: number, - quantities: TicketPriceQuantityFormValue[], +export interface ProductFormValue { + product_id: number, + quantities: ProductPriceQuantityFormValue[], } -export interface TicketFormPayload { - tickets?: TicketFormValue[], +export interface ProductFormPayload { + products?: ProductFormValue[], promo_code: string | null, session_identifier?: string, } @@ -88,7 +88,7 @@ export const orderClient = { } export const orderClientPublic = { - create: async (eventId: number, createOrderPayload: TicketFormPayload) => { + create: async (eventId: number, createOrderPayload: ProductFormPayload) => { const response = await publicApi.post>('events/' + eventId + '/order', createOrderPayload); return response.data; }, diff --git a/frontend/src/api/ticket.client.ts b/frontend/src/api/ticket.client.ts deleted file mode 100644 index 3d1f6e1aa9..0000000000 --- a/frontend/src/api/ticket.client.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {api} from "./client"; -import { - GenericDataResponse, - GenericPaginatedResponse, - IdParam, - QueryFilters, SortableItem, - Ticket, -} from "../types"; -import {queryParamsHelper} from "../utilites/queryParamsHelper.ts"; -import {publicApi} from "./public-client.ts"; - -export const ticketClient = { - findById: async (eventId: IdParam, ticketId: IdParam) => { - const response = await api.get>(`/events/${eventId}/tickets/${ticketId}`); - return response.data; - }, - all: async (eventId: IdParam, pagination: QueryFilters) => { - const response = await api.get>( - `/events/${eventId}/tickets` + queryParamsHelper.buildQueryString(pagination) - ); - return response.data; - }, - create: async (eventId: IdParam, ticket: Ticket) => { - const response = await api.post>(`events/${eventId}/tickets`, ticket); - return response.data; - }, - update: async (eventId: IdParam, ticketId: IdParam, ticket: Ticket) => { - const response = await api.put>(`events/${eventId}/tickets/${ticketId}`, ticket); - return response.data; - }, - delete: async (eventId: IdParam, ticketId: IdParam) => { - const response = await api.delete>(`/events/${eventId}/tickets/${ticketId}`); - return response.data; - }, - sortTickets: async (eventId: IdParam, ticketSort: SortableItem[]) => { - return await api.post(`/events/${eventId}/tickets/sort`, ticketSort); - } -} - -export const ticketClientPublic = { - findByEventId: async (eventId: IdParam) => { - const response = await publicApi.get>(`/events/${eventId}/tickets`); - return response.data; - }, -} - diff --git a/frontend/src/components/common/AttendeeCheckInTable/QrScanner.tsx b/frontend/src/components/common/AttendeeCheckInTable/QrScanner.tsx index c904651739..a39b3509f2 100644 --- a/frontend/src/components/common/AttendeeCheckInTable/QrScanner.tsx +++ b/frontend/src/components/common/AttendeeCheckInTable/QrScanner.tsx @@ -55,7 +55,7 @@ export const QRScannerComponent = (props: QRScannerComponentProps) => { const alreadyScanned = latestProcessedAttendeeIds.includes(debouncedAttendeeId); if (alreadyScanned) { - showError(t`You already scanned this ticket`); + showError(t`You already scanned this product`); return; } diff --git a/frontend/src/components/common/AttendeeCheckInTable/index.tsx b/frontend/src/components/common/AttendeeCheckInTable/index.tsx index 37f5a004ec..2eca08757a 100644 --- a/frontend/src/components/common/AttendeeCheckInTable/index.tsx +++ b/frontend/src/components/common/AttendeeCheckInTable/index.tsx @@ -22,7 +22,7 @@ export const AttendeesCheckInTable = () => { const [searchQuery, setSearchQuery] = useState(''); const [searchQueryDebounced] = useDebouncedValue(searchQuery, 200); const [qrScannerOpen, setQrScannerOpen] = useState(false); - const {data: {tickets} = {}} = useGetEvent(eventId); + const {data: {products} = {}} = useGetEvent(eventId); const queryFilters: QueryFilters = { pageNumber: 1, query: searchQueryDebounced, @@ -86,7 +86,7 @@ export const AttendeesCheckInTable = () => { const Attendees = () => { const Container = () => { - if (attendeesQuery.isFetching || !attendees || !tickets) { + if (attendeesQuery.isFetching || !attendees || !products) { return (
@@ -115,7 +115,7 @@ export const AttendeesCheckInTable = () => { {attendee.public_id}
- {tickets.find(ticket => ticket.id === attendee.ticket_id)?.title} + {products.find(product => product.id === attendee.product_id)?.title}
diff --git a/frontend/src/components/common/AttendeeDetails/index.tsx b/frontend/src/components/common/AttendeeDetails/index.tsx index b15acff754..a4674a9e6d 100644 --- a/frontend/src/components/common/AttendeeDetails/index.tsx +++ b/frontend/src/components/common/AttendeeDetails/index.tsx @@ -3,7 +3,7 @@ import {Card} from "../Card"; import {Attendee} from "../../../types.ts"; import classes from "./AttendeeDetails.module.scss"; import {t} from "@lingui/macro"; -import {getAttendeeTicketTitle} from "../../../utilites/tickets.ts"; +import {getAttendeeProductTitle} from "../../../utilites/products.ts"; import {getLocaleName, SupportedLocales} from "../../../locales.ts"; export const AttendeeDetails = ({attendee}: { attendee: Attendee }) => { @@ -43,10 +43,10 @@ export const AttendeeDetails = ({attendee}: { attendee: Attendee }) => {
- {t`Ticket`} + {t`Product`}
- {getAttendeeTicketTitle(attendee)} + {getAttendeeProductTitle(attendee)}
diff --git a/frontend/src/components/common/AttendeeList/AttendeeList.module.scss b/frontend/src/components/common/AttendeeList/AttendeeList.module.scss index 008a7faa8a..3cb228aada 100644 --- a/frontend/src/components/common/AttendeeList/AttendeeList.module.scss +++ b/frontend/src/components/common/AttendeeList/AttendeeList.module.scss @@ -16,7 +16,7 @@ .attendeeName { margin-left: 10px; - .ticketName { + .productName { color: #9ca3af; font-size: .8em; } diff --git a/frontend/src/components/common/AttendeeList/index.tsx b/frontend/src/components/common/AttendeeList/index.tsx index 5e1f0276a4..813293287a 100644 --- a/frontend/src/components/common/AttendeeList/index.tsx +++ b/frontend/src/components/common/AttendeeList/index.tsx @@ -4,10 +4,10 @@ import Truncate from "../Truncate"; import {NavLink} from "react-router-dom"; import {IconEye} from "@tabler/icons-react"; import classes from './AttendeeList.module.scss'; -import {Order, Ticket} from "../../../types.ts"; +import {Order, Product} from "../../../types.ts"; import {t} from "@lingui/macro"; -export const AttendeeList = ({order, tickets}: { order: Order, tickets: Ticket[] }) => { +export const AttendeeList = ({order, products}: { order: Order, products: Product[] }) => { return (
{order.attendees?.map(attendee => ( @@ -18,8 +18,8 @@ export const AttendeeList = ({order, tickets}: { order: Order, tickets: Ticket[]
{attendee.first_name + ' ' + attendee.last_name} -
- ticket.id === attendee.ticket_id)?.title}/> +
+ product.id === attendee.product_id)?.title}/>
diff --git a/frontend/src/components/common/AttendeeTicket/AttendeeTicket.module.scss b/frontend/src/components/common/AttendeeProduct/AttendeeProduct.module.scss similarity index 96% rename from frontend/src/components/common/AttendeeTicket/AttendeeTicket.module.scss rename to frontend/src/components/common/AttendeeProduct/AttendeeProduct.module.scss index 3dd8205026..3605fb8fe2 100644 --- a/frontend/src/components/common/AttendeeTicket/AttendeeTicket.module.scss +++ b/frontend/src/components/common/AttendeeProduct/AttendeeProduct.module.scss @@ -43,13 +43,13 @@ flex: 1; } - .ticketName { + .productName { font-size: 0.9em; font-weight: 900; margin-bottom: 5px; } - .ticketPrice { + .productPrice { .badge { background-color: #8BC34A; color: #fff; @@ -114,7 +114,7 @@ } } - .ticketButtons { + .productButtons { background: #ffffff; border-radius: 5px; margin-top: 20px; diff --git a/frontend/src/components/common/AttendeeTicket/index.tsx b/frontend/src/components/common/AttendeeProduct/index.tsx similarity index 74% rename from frontend/src/components/common/AttendeeTicket/index.tsx rename to frontend/src/components/common/AttendeeProduct/index.tsx index 823ca93246..e5802f9e42 100644 --- a/frontend/src/components/common/AttendeeTicket/index.tsx +++ b/frontend/src/components/common/AttendeeProduct/index.tsx @@ -1,23 +1,23 @@ import {Card} from "../Card"; -import {getAttendeeTicketPrice, getAttendeeTicketTitle} from "../../../utilites/tickets.ts"; +import {getAttendeeProductPrice, getAttendeeProductTitle} from "../../../utilites/products.ts"; import {Anchor, Button, CopyButton} from "@mantine/core"; import {formatCurrency} from "../../../utilites/currency.ts"; import {t} from "@lingui/macro"; import {prettyDate} from "../../../utilites/dates.ts"; import QRCode from "react-qr-code"; import {IconCopy, IconPrinter} from "@tabler/icons-react"; -import {Attendee, Event, Ticket} from "../../../types.ts"; -import classes from './AttendeeTicket.module.scss'; +import {Attendee, Event, Product} from "../../../types.ts"; +import classes from './AttendeeProduct.module.scss'; -interface AttendeeTicketProps { +interface AttendeeProductProps { event: Event; attendee: Attendee; - ticket: Ticket; + product: Product; hideButtons?: boolean; } -export const AttendeeTicket = ({attendee, ticket, event, hideButtons = false}: AttendeeTicketProps) => { - const ticketPrice = getAttendeeTicketPrice(attendee, ticket); +export const AttendeeProduct = ({attendee, product, event, hideButtons = false}: AttendeeProductProps) => { + const productPrice = getAttendeeProductPrice(attendee, product); return ( @@ -27,17 +27,17 @@ export const AttendeeTicket = ({attendee, ticket, event, hideButtons = false}: A

{attendee.first_name} {attendee.last_name}

-
- {getAttendeeTicketTitle(attendee)} +
+ {getAttendeeProductTitle(attendee)}
{attendee.email}
-
+
- {ticketPrice > 0 && formatCurrency(ticketPrice, event?.currency)} - {ticketPrice === 0 && t`Free`} + {productPrice > 0 && formatCurrency(productPrice, event?.currency)} + {productPrice === 0 && t`Free`}
@@ -65,16 +65,16 @@ export const AttendeeTicket = ({attendee, ticket, event, hideButtons = false}: A
{!hideButtons && ( -
+
- + {({copied, copy}) => ( )} @@ -94,17 +94,17 @@ export const TicketsTable = ({tickets, event, openCreateModal, enableSorting = f >
- {items.map((ticketId) => { - const ticket = tickets.find((t) => t.id === ticketId); + {items.map((productId) => { + const product = products.find((t) => t.id === productId); - if (!ticket) { + if (!product) { return null; } return ( - diff --git a/frontend/src/components/common/PromoCodeTable/index.tsx b/frontend/src/components/common/PromoCodeTable/index.tsx index 192b7a2fd0..336b29d2e3 100644 --- a/frontend/src/components/common/PromoCodeTable/index.tsx +++ b/frontend/src/components/common/PromoCodeTable/index.tsx @@ -68,7 +68,7 @@ export const PromoCodeTable = ({event, promoCodes, openCreateModal}: PromoCodeTa {t`Code`} {t`Discount`} {t`Times used`} - {t`Tickets`} + {t`Products`} {t`Expires`} @@ -126,25 +126,25 @@ export const PromoCodeTable = ({event, promoCodes, openCreateModal}: PromoCodeTa
- {code.applicable_ticket_ids?.length === 0 && ( - {t`All Tickets`} + {code.applicable_product_ids?.length === 0 && ( + {t`All Products`} )} - {Number(code.applicable_ticket_ids?.length) > 0 && ( + {Number(code.applicable_product_ids?.length) > 0 && ( - code.applicable_ticket_ids?.map(Number)?.includes(Number(ticket.id))) - .map(ticket => { + event?.products?.filter(product => + code.applicable_product_ids?.map(Number)?.includes(Number(product.id))) + .map(product => { return ( <> - {ticket.title} + {product.title}
); })}> {code.applicable_ticket_ids?.length} {t`Ticket(s)`} + color={'pink'}>{code.applicable_product_ids?.length} {t`Product(s)`}
)}
diff --git a/frontend/src/components/common/QuestionsTable/index.tsx b/frontend/src/components/common/QuestionsTable/index.tsx index bf4835e34b..058688a963 100644 --- a/frontend/src/components/common/QuestionsTable/index.tsx +++ b/frontend/src/components/common/QuestionsTable/index.tsx @@ -222,7 +222,7 @@ const DefaultQuestions = () => ( ); export const QuestionsTable = ({questions}: QuestionsTableProp) => { - const ticketQuestions = questions.filter(question => question.belongs_to === "TICKET"); + const productQuestions = questions.filter(question => question.belongs_to === "PRODUCT"); const orderQuestions = questions.filter(question => question.belongs_to === "ORDER"); const form = useForm(); const [createModalOpen, {open: openCreateModal, close: closeCreateModal}] = useDisclosure(false); @@ -306,11 +306,11 @@ export const QuestionsTable = ({questions}: QuestionsTableProp) => {

{t`Attendee questions`}

- {ticketQuestions + {productQuestions .filter(question => showHiddenQuestions || !question.is_hidden) .length === 0 && ( @@ -347,7 +347,7 @@ export const QuestionsTable = ({questions}: QuestionsTableProp) => {

{t`Attendee questions`}

- {ticketQuestions + {productQuestions .filter(question => showHiddenQuestions || !question.is_hidden) .map(question => ( { const data = [ { - number: formatNumber(eventStats?.total_tickets_sold as number), - description: t`Tickets sold`, + number: formatNumber(eventStats?.total_products_sold as number), + description: t`Products sold`, icon: }, { diff --git a/frontend/src/components/common/WidgetEditor/index.tsx b/frontend/src/components/common/WidgetEditor/index.tsx index 93a812618f..0ae959a14f 100644 --- a/frontend/src/components/common/WidgetEditor/index.tsx +++ b/frontend/src/components/common/WidgetEditor/index.tsx @@ -1,5 +1,5 @@ import classes from './WidgetEditor.module.scss'; -import SelectTickets from "../../routes/ticket-widget/SelectTickets"; +import SelectProducts from "../../routes/product-widget/SelectProducts"; import {ColorInput, Group, NumberInput, Switch, Tabs, Textarea, TextInput} from "@mantine/core"; import {t, Trans} from "@lingui/macro"; import {matches, useForm} from "@mantine/form"; @@ -308,7 +308,7 @@ export default App;

- {t`Ticket Widget Preview`} + {t`Product Widget Preview`}

@@ -332,7 +332,7 @@ export default App;
{!eventQuery.isFetched ? : - ; - tickets: Ticket[], + products: Product[], } -export const CapaciyAssigmentForm = ({form, tickets}: CapaciyAssigmentFormProps) => { +export const CapaciyAssigmentForm = ({form, products}: CapaciyAssigmentFormProps) => { const statusOptions: ItemProps[] = [ { icon: , label: t`Active`, value: 'ACTIVE', - description: t`Enable this capacity to stop ticket sales when the limit is reached`, + description: t`Enable this capacity to stop product sales when the limit is reached`, }, { icon: , label: t`Inactive`, value: 'INACTIVE', - description: t`Disable this capacity track capacity without stopping ticket sales`, + description: t`Disable this capacity track capacity without stopping product sales`, }, ]; @@ -44,17 +44,17 @@ export const CapaciyAssigmentForm = ({form, tickets}: CapaciyAssigmentFormProps) { + placeholder={t`Select products`} + data={products?.map(product => { return { - value: String(ticket.id), - label: ticket.title, + value: String(product.id), + label: product.title, } })} leftSection={} - {...form.getInputProps('ticket_ids')} + {...form.getInputProps('product_ids')} /> ; - tickets: Ticket[], + products: Product[], } -export const CheckInListForm = ({form, tickets}: CheckInListFormProps) => { +export const CheckInListForm = ({form, products}: CheckInListFormProps) => { return ( <> { /> { + placeholder={t`Select products`} + data={products?.map(product => { return { - value: String(ticket.id), - label: ticket.title, + value: String(product.id), + label: product.title, } })} required leftSection={} - {...form.getInputProps('ticket_ids')} + {...form.getInputProps('product_ids')} />