Skip to content

Commit

Permalink
Showing 18 changed files with 92 additions and 47 deletions.
4 changes: 2 additions & 2 deletions backend/app/DomainObjects/Enums/ProductType.php
Original file line number Diff line number Diff line change
@@ -6,6 +6,6 @@ enum ProductType
{
use BaseEnum;

case PRODUCT;
case PRODUCT;
case TICKET;
case GENERAL;
}
2 changes: 2 additions & 0 deletions backend/app/Http/Request/Product/UpsertProductRequest.php
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
namespace HiEvents\Http\Request\Product;

use HiEvents\DomainObjects\Enums\ProductPriceType;
use HiEvents\DomainObjects\Enums\ProductType;
use HiEvents\Http\Request\BaseRequest;
use HiEvents\Validators\Rules\RulesHelper;
use Illuminate\Validation\Rule;
@@ -36,6 +37,7 @@ public function rules(): array
'show_quantity_remaining' => 'boolean',
'is_hidden_without_promo_code' => 'boolean',
'type' => ['required', Rule::in(ProductPriceType::valuesArray())],
'product_type' => ['required', Rule::in(ProductType::valuesArray())],
'tax_and_fee_ids' => 'array',
];
}
1 change: 1 addition & 0 deletions backend/app/Resources/Product/ProductResource.php
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ public function toArray(Request $request): array
'id' => $this->getId(),
'title' => $this->getTitle(),
'type' => $this->getType(),
'product_type' => $this->getProductType(),
'order' => $this->getOrder(),
'description' => $this->getDescription(),
'price' => $this->when(
Original file line number Diff line number Diff line change
@@ -6,13 +6,13 @@
use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\DomainObjects\EventSettingDomainObject;
use HiEvents\DomainObjects\OrganizerDomainObject;
use HiEvents\Mail\Attendee\AttendeeProductMail;
use HiEvents\Mail\Attendee\AttendeeTicketMail;
use Illuminate\Contracts\Mail\Mailer;

readonly class SendAttendeeProductService
class SendAttendeeTicketService
{
public function __construct(
private Mailer $mailer
private readonly Mailer $mailer
)
{
}
@@ -27,7 +27,7 @@ public function send(
$this->mailer
->to($attendee->getEmail())
->locale($attendee->getLocale())
->send(new AttendeeProductMail(
->send(new AttendeeTicketMail(
attendee: $attendee,
event: $event,
eventSettings: $eventSettings,
4 changes: 2 additions & 2 deletions backend/app/Services/Domain/Mail/SendOrderDetailsService.php
Original file line number Diff line number Diff line change
@@ -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\SendAttendeeProductService;
use HiEvents\Services\Domain\Attendee\SendAttendeeTicketService;
use Illuminate\Mail\Mailer;

readonly class SendOrderDetailsService
@@ -23,7 +23,7 @@ public function __construct(
private EventRepositoryInterface $eventRepository,
private OrderRepositoryInterface $orderRepository,
private Mailer $mailer,
private SendAttendeeProductService $sendAttendeeProductService,
private SendAttendeeTicketService $sendAttendeeProductService,
)
{
}
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@ private function persistProduct(ProductDomainObject $productsData): ProductDomai
return $this->productRepository->create([
'title' => $productsData->getTitle(),
'type' => $productsData->getType(),
'product_type' => $productsData->getProductType(),
'order' => $productsData->getOrder(),
'sale_start_date' => $productsData->getSaleStartDate()
? DateHelper::convertToUTC($productsData->getSaleStartDate(), $event->getTimezone())
Original file line number Diff line number Diff line change
@@ -9,15 +9,15 @@
use HiEvents\Repository\Eloquent\Value\Relationship;
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
use HiEvents\Repository\Interfaces\EventRepositoryInterface;
use HiEvents\Services\Domain\Attendee\SendAttendeeProductService;
use HiEvents\Services\Domain\Attendee\SendAttendeeTicketService;
use HiEvents\Services\Handlers\Attendee\DTO\ResendAttendeeTicketDTO;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;

readonly class ResendAttendeeTicketHandler
{
public function __construct(
private SendAttendeeProductService $sendAttendeeProductService,
private SendAttendeeTicketService $sendAttendeeProductService,
private AttendeeRepositoryInterface $attendeeRepository,
private EventRepositoryInterface $eventRepository,
private LoggerInterface $logger,
15 changes: 7 additions & 8 deletions backend/app/Services/Handlers/Order/CompleteOrderHandler.php
Original file line number Diff line number Diff line change
@@ -37,14 +37,14 @@
/**
* @todo - Tidy this up
*/
readonly class CompleteOrderHandler
class CompleteOrderHandler
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private AttendeeRepositoryInterface $attendeeRepository,
private QuestionAnswerRepositoryInterface $questionAnswersRepository,
private ProductQuantityUpdateService $productQuantityUpdateService,
private ProductPriceRepositoryInterface $productPriceRepository,
private readonly OrderRepositoryInterface $orderRepository,
private readonly AttendeeRepositoryInterface $attendeeRepository,
private readonly QuestionAnswerRepositoryInterface $questionAnswersRepository,
private readonly ProductQuantityUpdateService $productQuantityUpdateService,
private readonly ProductPriceRepositoryInterface $productPriceRepository,
)
{
}
@@ -89,8 +89,7 @@ public function handle(string $orderShortId, CompleteOrderDTO $orderData): Order
private function createAttendees(Collection $attendees, OrderDomainObject $order): void
{
$inserts = [];
$publicIdIndex = 1;


$productsPrices = $this->productPriceRepository->findWhereIn(
field: ProductPriceDomainObjectAbstract::ID,
values: $attendees->pluck('product_price_id')->toArray(),
Original file line number Diff line number Diff line change
@@ -52,7 +52,8 @@ public function handle(UpsertProductDTO $productsData): ProductDomainObject
->setShowQuantityRemaining($productsData->show_quantity_remaining)
->setIsHiddenWithoutPromoCode($productsData->is_hidden_without_promo_code)
->setProductPrices($productPrices)
->setEventId($productsData->event_id),
->setEventId($productsData->event_id)
->setProductType($productsData->product_type->name),
accountId: $productsData->account_id,
taxAndFeeIds: $productsData->tax_and_fee_ids,
);
20 changes: 11 additions & 9 deletions backend/app/Services/Handlers/Product/DTO/UpsertProductDTO.php
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
use HiEvents\DataTransferObjects\Attributes\CollectionOf;
use HiEvents\DataTransferObjects\BaseDTO;
use HiEvents\DomainObjects\Enums\ProductPriceType;
use HiEvents\DomainObjects\Enums\ProductType;
use HiEvents\Services\Domain\Product\DTO\ProductPriceDTO;
use Illuminate\Support\Collection;

@@ -15,6 +16,7 @@ public function __construct(
public readonly int $event_id,
public readonly string $title,
public readonly ProductPriceType $type,
public readonly ProductType $product_type,
#[CollectionOf(ProductPriceDTO::class)]
public readonly ?Collection $prices = null,
public readonly ?float $price = 0.00,
@@ -25,15 +27,15 @@ public function __construct(
public readonly ?string $sale_end_date = null,
public readonly ?int $max_per_order = 100,
public readonly ?string $description = null,
public readonly ?int $min_per_order = 0,
public readonly ?bool $is_hidden = false,
public readonly ?bool $hide_before_sale_start_date = false,
public readonly ?bool $hide_after_sale_end_date = false,
public readonly ?bool $hide_when_sold_out = false,
public readonly ?bool $show_quantity_remaining = false,
public readonly ?bool $is_hidden_without_promo_code = false,
public readonly ?array $tax_and_fee_ids = [],
public readonly ?int $product_id = null,
public readonly ?int $min_per_order = 0,
public readonly ?bool $is_hidden = false,
public readonly ?bool $hide_before_sale_start_date = false,
public readonly ?bool $hide_after_sale_end_date = false,
public readonly ?bool $hide_when_sold_out = false,
public readonly ?bool $show_quantity_remaining = false,
public readonly ?bool $is_hidden_without_promo_code = false,
public readonly ?array $tax_and_fee_ids = [],
public readonly ?int $product_id = null,
)
{
}
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ private function updateProduct(UpsertProductDTO $productsData, array $where): Pr
'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,
'product_type' => $productsData->product_type->name,
],
where: $where
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {IdParam, MessageType, Product, ProductPrice, ProductType} from "../../../../types.ts";
import {IdParam, MessageType, Product, ProductPrice, ProductPriceType} from "../../../../types.ts";
import {useSortable} from "@dnd-kit/sortable";
import {useDisclosure} from "@mantine/hooks";
import {useState} from "react";
@@ -83,7 +83,7 @@ export const SortableProduct = ({product, enableSorting, currencyCode}: {product
const getPriceRange = (product: Product) => {
const productPrices: ProductPrice[] = product.prices as ProductPrice[];

if (product.type !== ProductType.Tiered) {
if (product.type !== ProductPriceType.Tiered) {
if (productPrices[0].price <= 0) {
return t`Free`;
}
50 changes: 40 additions & 10 deletions frontend/src/components/forms/ProductForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {t, Trans} from "@lingui/macro";
import {UseFormReturnType} from "@mantine/form";
import {Event, TaxAndFee, TaxAndFeeCalculationType, TaxAndFeeType, Product, ProductType} from "../../../types.ts";
import {Event, TaxAndFee, TaxAndFeeCalculationType, TaxAndFeeType, Product, ProductPriceType} from "../../../types.ts";
import {
ActionIcon,
Alert,
@@ -19,7 +19,7 @@ import {
IconCoinOff,
IconCoins,
IconHeartDollar,
IconInfoCircle,
IconInfoCircle, IconShirt, IconTicket,
IconTrash,
IconTrashOff,
} from "@tabler/icons-react";
@@ -121,7 +121,22 @@ const ProductPriceTierForm = ({form, product, event}: ProductFormProps) => {
}

export const ProductForm = ({form, product}: ProductFormProps) => {
const productOptions: ItemProps[] = [
const productTypeOptions: ItemProps[] = [
{
icon: <IconTicket/>,
label: t`Ticket`,
value: 'TICKET',
description: t`This product is a ticket. Buyers will be issued a ticket upon purchase`,
},
{
icon: <IconShirt/>,
label: t`General`,
value: 'GENERAL',
description: t`This is a general product, like a t-shirt or a mug. No ticket will be issued`,
},
];

const productPriceOptions: ItemProps[] = [
{
icon: <IconCash/>,
label: t`Paid Product`,
@@ -167,7 +182,7 @@ export const ProductForm = ({form, product}: ProductFormProps) => {
}

useEffect(() => {
if (form.values.type === ProductType.Free) {
if (form.values.type === ProductPriceType.Free) {
form.setFieldValue('price', 0.00);
}
}, [form, form.values.type]);
@@ -184,21 +199,36 @@ export const ProductForm = ({form, product}: ProductFormProps) => {
{t`You cannot change the product type as there are attendees associated with this product.`}
</Alert>
)}

<CustomSelect
disabled={Number(product?.quantity_sold) > 0}
label={t`Product Type`}
required
form={form}
name={'product_type'}
optionList={productTypeOptions}
/>
{form.errors.product_type && (
<Alert title={t`Product Type`} mb={20} color={'red'}>
{form.errors.product_type}
</Alert>
)}

<CustomSelect
disabled={Number(product?.quantity_sold) > 0}
label={t`Price Type`}
required
form={form}
name={'type'}
optionList={productOptions}
optionList={productPriceOptions}
/>
{form.errors.type && (
<Alert title={t`Product Type`} mb={20} color={'red'}>
<Alert title={t`Product Price Type`} mb={20} color={'red'}>
{form.errors.type}
</Alert>
)}

{form.values.type === ProductType.Tiered && (
{form.values.type === ProductPriceType.Tiered && (
<Alert title={t`What are Tiered Products?`} mb={20}>
{t`Tiered products allow you to offer multiple price options for the same product.
This is perfect for early bird products, or offering different price
@@ -218,7 +248,7 @@ export const ProductForm = ({form, product}: ProductFormProps) => {
onChange={(value) => form.setFieldValue('description', value)}
/>

{form.values.type !== ProductType.Tiered && (
{form.values.type !== ProductPriceType.Tiered && (
<InputGroup>
<NumberInput decimalScale={2}
min={0}
@@ -263,7 +293,7 @@ export const ProductForm = ({form, product}: ProductFormProps) => {
)}
</div>

{form.values.type === ProductType.Tiered && (
{form.values.type === ProductPriceType.Tiered && (
<Fieldset legend={t`Price tiers`} mt={20} mb={20}>
<ProductPriceTierForm product={product} form={form} event={event}/>
<Group>
@@ -313,7 +343,7 @@ export const ProductForm = ({form, product}: ProductFormProps) => {
}]}
/>

{(form.values.type === ProductType.Free && !!form.values.tax_and_fee_ids?.length) && (
{(form.values.type === ProductPriceType.Free && !!form.values.tax_and_fee_ids?.length) && (
<Alert mb={20}>
<p>
{t`You have taxes and fees added to a Free Product. Would you like to remove or obscure them?`}
5 changes: 3 additions & 2 deletions frontend/src/components/modals/CreateProductModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Button} from "@mantine/core";
import {GenericModalProps, TaxAndFee, Product, ProductType} from "../../../types.ts";
import {GenericModalProps, Product, ProductPriceType, ProductType, TaxAndFee} from "../../../types.ts";
import {useForm} from "@mantine/form";
import {useMutation, useQueryClient} from "@tanstack/react-query";
import {notifications} from "@mantine/notifications";
@@ -29,7 +29,8 @@ export const CreateProductModal = ({onClose}: GenericModalProps) => {
show_quantity_remaining: false,
hide_when_sold_out: false,
is_hidden_without_promo_code: false,
type: ProductType.Paid,
type: ProductPriceType.Paid,
product_type: ProductType.Ticket,
tax_and_fee_ids: undefined,
prices: [{
price: 0,
4 changes: 2 additions & 2 deletions frontend/src/components/modals/EditProductModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Button} from "@mantine/core";
import {GenericModalProps, IdParam, Product, ProductType} from "../../../types.ts";
import {GenericModalProps, IdParam, Product, ProductPriceType} from "../../../types.ts";
import {useForm} from "@mantine/form";
import {useParams} from "react-router-dom";
import {useEffect} from "react";
@@ -32,7 +32,7 @@ export const EditProductModal = ({onClose, productId}: GenericModalProps & { pro
show_quantity_remaining: undefined,
hide_when_sold_out: undefined,
is_hidden_without_promo_code: undefined,
type: ProductType.Paid,
type: ProductPriceType.Paid,
tax_and_fee_ids: [],
prices: []
},
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Currency, ProductPriceDisplay} from "../../../../../common/Currency";
import {Event, Product, ProductType} from "../../../../../../types.ts";
import {Event, Product, ProductPriceType} from "../../../../../../types.ts";
import {Group, TextInput} from "@mantine/core";
import {NumberSelector, SharedValues} from "../../../../../common/NumberSelector";
import {UseFormReturnType} from "@mantine/form";
11 changes: 9 additions & 2 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
@@ -256,13 +256,18 @@ export interface GenericPaginatedResponse<T> {
meta: PaginationData;
}

export enum ProductType {
export enum ProductPriceType {
Paid = 'PAID',
Donation = 'DONATION',
Free = 'FREE',
Tiered = 'TIERED',
}

export enum ProductType {
Ticket = 'TICKET',
General = 'GENERAL',
}

export enum ProductStatus {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
@@ -294,7 +299,9 @@ export interface Product {
order?: number;
title: string;
event_id?: IdParam;
type: ProductType;
// todo - rename to price_type
type: ProductPriceType;
product_type: ProductType;
description?: string;
price?: number;
prices?: ProductPrice[];

0 comments on commit 8871867

Please sign in to comment.