diff --git a/src/Application/UseCases/CampaignRequest/Find/FindCampaignRequest.php b/src/Application/UseCases/CampaignRequest/Find/FindCampaignRequest.php index 55c207e..0acbbef 100644 --- a/src/Application/UseCases/CampaignRequest/Find/FindCampaignRequest.php +++ b/src/Application/UseCases/CampaignRequest/Find/FindCampaignRequest.php @@ -44,9 +44,9 @@ final readonly class FindCampaignRequest implements UseCase return new CampaignRequestFound( brand: $brand, + name: $campaignRequest->name, applicant: $campaignRequest->applicant, campaignType: $campaignRequest->campaignType, - printRules: $campaignRequest->printRules, requestedCoupon: $campaignRequest->requestedCoupon, status: $campaignRequest->status, rejectionReason: $campaignRequest->rejectionReason, diff --git a/src/Application/UseCases/CampaignRequest/Find/Output/CampaignRequestFound.php b/src/Application/UseCases/CampaignRequest/Find/Output/CampaignRequestFound.php index 05af5aa..5100a7b 100644 --- a/src/Application/UseCases/CampaignRequest/Find/Output/CampaignRequestFound.php +++ b/src/Application/UseCases/CampaignRequest/Find/Output/CampaignRequestFound.php @@ -4,21 +4,21 @@ declare(strict_types=1); namespace Application\UseCases\CampaignRequest\Find\Output; +use Domain\Models\Entities\Brand; +use Domain\Models\ValueObjects\Campaign\CampaignName; +use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; -use Domain\Models\Entities\Brand; -use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; -use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; final readonly class CampaignRequestFound implements Output { public function __construct( public Brand $brand, + public CampaignName $name, public Applicant $applicant, public CampaignRequestType $campaignType, - public ?PrintRules $printRules, public RequestedCoupon $requestedCoupon, public CampaignRequestStatus $status, public ?RejectionReason $rejectionReason, diff --git a/src/Application/UseCases/CampaignRequest/List/CampaignRequestListItem.php b/src/Application/UseCases/CampaignRequest/List/CampaignRequestListItem.php index 1ae91e0..852118e 100644 --- a/src/Application/UseCases/CampaignRequest/List/CampaignRequestListItem.php +++ b/src/Application/UseCases/CampaignRequest/List/CampaignRequestListItem.php @@ -7,6 +7,7 @@ namespace Application\UseCases\CampaignRequest\List; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\Brand\BrandCode; +use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\Applicant; final readonly class CampaignRequestListItem diff --git a/src/Application/UseCases/CampaignRequest/List/ListCampaignsRequest.php b/src/Application/UseCases/CampaignRequest/List/ListCampaignsRequest.php index 1b1d3de..5c537bf 100644 --- a/src/Application/UseCases/CampaignRequest/List/ListCampaignsRequest.php +++ b/src/Application/UseCases/CampaignRequest/List/ListCampaignsRequest.php @@ -33,6 +33,7 @@ final readonly class ListCampaignsRequest implements UseCase $items = array_map( static fn (CampaignRequest $campaignRequest): CampaignRequestListItem => new CampaignRequestListItem( brandCode: $campaignRequest->brandCode, + name: $campaignRequest->name, applicant: $campaignRequest->applicant, campaignType: $campaignRequest->campaignType, status: $campaignRequest->status diff --git a/src/Domain/Models/Entities/Campaign.php b/src/Domain/Models/Entities/Campaign.php index 197a7dd..9df15bc 100644 --- a/src/Domain/Models/Entities/Campaign.php +++ b/src/Domain/Models/Entities/Campaign.php @@ -11,24 +11,23 @@ use Domain\Models\ValueObjects\Campaign\CampaignId; use Domain\Models\ValueObjects\Campaign\CampaignName; use Shared\DDD\Models\AggregateRoot; use Shared\DDD\Models\HasTimestampIdentifier; -use Shared\Enums\Country; /** * @implements \Shared\DDD\Models\AggregateRoot<\Domain\Models\ValueObjects\Campaign\CampaignId> */ final readonly class Campaign implements AggregateRoot, HasTimestampIdentifier { + /** + * @param array $coupons + */ private function __construct( public CampaignId $id, public CampaignCode $code, - public Tag $tag, public CampaignName $name, public bool $publication, public bool $printAllAtOnce, - public Country $availableIn, - /** @var array $coupons */ public array $coupons, - public BrandCode $brand, + public BrandCode $brandCode, public ?\DateTimeInterface $updatedAt = null, ) { foreach ($this->coupons as $coupon) { @@ -54,59 +53,50 @@ final readonly class Campaign implements AggregateRoot, HasTimestampIdentifier } /** - * @param array $coupons + * @param array $coupons */ public static function create( CampaignCode $code, - Tag $tag, CampaignName $name, bool $publication, bool $printAllAtOnce, - Country $availableIn, - /** @var array $coupons */ array $coupons, - BrandCode $brand, + BrandCode $brandCode, ?\DateTimeInterface $updatedAt = null, ): Campaign { return new self( id: CampaignId::generate(), code: $code, - tag: $tag, name: $name, publication: $publication, printAllAtOnce: $printAllAtOnce, - availableIn: $availableIn, coupons: $coupons, - brand: $brand, + brandCode: $brandCode, updatedAt: $updatedAt, ); } /** - * @param array $coupons + * @param array $coupons */ public static function hydrate( CampaignId $id, CampaignCode $code, - Tag $tag, CampaignName $name, bool $publication, bool $printAllAtOnce, - Country $availableIn, array $coupons, - BrandCode $brand, + BrandCode $brandCode, ?\DateTimeInterface $updatedAt = null, ): self { return new self( id: $id, code: $code, - tag: $tag, name: $name, publication: $publication, printAllAtOnce: $printAllAtOnce, - availableIn: $availableIn, coupons: $coupons, - brand: $brand, + brandCode: $brandCode, updatedAt: $updatedAt, ); } diff --git a/src/Domain/Models/Entities/CampaignRequest.php b/src/Domain/Models/Entities/CampaignRequest.php index 92be622..833b3cf 100644 --- a/src/Domain/Models/Entities/CampaignRequest.php +++ b/src/Domain/Models/Entities/CampaignRequest.php @@ -7,10 +7,10 @@ namespace Domain\Models\Entities; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\Brand\BrandCode; +use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; -use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Shared\DDD\Models\AggregateRoot; use Shared\DDD\Models\HasTimestampIdentifier; @@ -24,9 +24,9 @@ final readonly class CampaignRequest implements AggregateRoot, HasTimestampIdent private function __construct( public CampaignRequestId $id, public BrandCode $brandCode, + public CampaignName $name, public Applicant $applicant, public CampaignRequestType $campaignType, - public ?PrintRules $printRules, public RequestedCoupon $requestedCoupon, public CampaignRequestStatus $status, public ?RejectionReason $rejectionReason = null, @@ -60,9 +60,9 @@ final readonly class CampaignRequest implements AggregateRoot, HasTimestampIdent public static function hydrate( CampaignRequestId $id, BrandCode $brandCode, + CampaignName $name, Applicant $applicant, CampaignRequestType $campaignType, - ?PrintRules $printRules, RequestedCoupon $requestedCoupon, CampaignRequestStatus $status, ?RejectionReason $rejectionReason = null, @@ -71,9 +71,9 @@ final readonly class CampaignRequest implements AggregateRoot, HasTimestampIdent return new self( id: $id, brandCode: $brandCode, + name: $name, applicant: $applicant, campaignType: $campaignType, - printRules: $printRules, requestedCoupon: $requestedCoupon, status: $status, rejectionReason: $rejectionReason, @@ -90,9 +90,9 @@ final readonly class CampaignRequest implements AggregateRoot, HasTimestampIdent return new self( id: $this->id, brandCode: $this->brandCode, + name: $this->name, applicant: $this->applicant, campaignType: $this->campaignType, - printRules: $this->printRules, requestedCoupon: $this->requestedCoupon, status: CampaignRequestStatus::REJECTED, rejectionReason: $reason, diff --git a/src/Domain/Models/Entities/Coupon.php b/src/Domain/Models/Entities/Coupon.php index 33052b9..c3075f7 100644 --- a/src/Domain/Models/Entities/Coupon.php +++ b/src/Domain/Models/Entities/Coupon.php @@ -7,15 +7,16 @@ namespace Domain\Models\Entities; use Domain\Models\ValueObjects\Actor\ActorId; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar; +use Domain\Models\ValueObjects\Coupon\CouponCode; use Domain\Models\ValueObjects\Coupon\CouponDiffusion; use Domain\Models\ValueObjects\Coupon\CouponId; use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; -use Domain\Models\ValueObjects\Coupon\CouponProduct; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\ProcessingCode; use Shared\DDD\Models\AggregateRoot; @@ -26,27 +27,37 @@ use Shared\DDD\Models\HasTimestampIdentifier; */ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier { + /** + * @param array $couponValidityInformation + * @param array $couponLegalInformation + * @param array $products + */ private function __construct( public CouponId $id, + public CouponCode $code, public CouponName $name, - public bool $status, - public CouponProductName $productName, public MoneyAmount $amount, public CouponPicture $picture, - public CouponProduct $product, public CouponDiffusion $diffusion, - public CouponAvailabilityCalendar $availability, public CouponValidity $validity, - /** @var array $legalInformation */ - public array $legalInformation, - public ProcessingCode $code, + public array $couponValidityInformation, + public array $couponLegalInformation, + public ProcessingCode $processingCode, public BrandCode $brandCode, public ActorId $createdBy, + public array $products = [], + public ?CouponAvailabilityCalendar $availabilityCalendar = null, public ?GS1 $gtin = null, public ?\DateTimeInterface $updatedAt = null, ) { - foreach ($this->legalInformation as $legal) { - if (!$legal instanceof CouponLegalInformation) { + foreach ($this->couponValidityInformation as $validityInformation) { + if (!$validityInformation instanceof CouponValidityInformation) { + throw new \DomainException('Coupon validity information must be CouponValidityInformation instances.'); + } + } + + foreach ($this->couponLegalInformation as $legalInformation) { + if (!$legalInformation instanceof CouponLegalInformation) { throw new \DomainException('Coupon legal information must be CouponLegalInformation instances.'); } } @@ -68,81 +79,85 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier } /** - * @param array $legalInformation + * @param array $couponValidityInformation + * @param array $couponLegalInformation + * @param array $products */ public static function create( + CouponCode $code, CouponName $name, - bool $status, - CouponProductName $productName, MoneyAmount $amount, CouponPicture $picture, - CouponProduct $product, CouponDiffusion $diffusion, - CouponAvailabilityCalendar $availability, CouponValidity $validity, - array $legalInformation, - ProcessingCode $code, + array $couponValidityInformation, + array $couponLegalInformation, + ProcessingCode $processingCode, BrandCode $brandCode, ActorId $createdBy, + array $products = [], + ?CouponAvailabilityCalendar $availabilityCalendar = null, ?GS1 $gtin = null, ?\DateTimeInterface $updatedAt = null, ): Coupon { return new self( id: CouponId::generate(), + code: $code, name: $name, - status: $status, - productName: $productName, amount: $amount, picture: $picture, - product: $product, diffusion: $diffusion, - availability: $availability, validity: $validity, - legalInformation: $legalInformation, - code: $code, + couponValidityInformation: $couponValidityInformation, + couponLegalInformation: $couponLegalInformation, + processingCode: $processingCode, brandCode: $brandCode, createdBy: $createdBy, + products: $products, + availabilityCalendar: $availabilityCalendar, gtin: $gtin, updatedAt: $updatedAt, ); } /** - * @param array $legalInformation + * @param array $couponValidityInformation + * @param array $couponLegalInformation + * @param array $products */ public static function hydrate( CouponId $id, + CouponCode $code, CouponName $name, - bool $status, - CouponProductName $productName, MoneyAmount $amount, CouponPicture $picture, - CouponProduct $product, CouponDiffusion $diffusion, - CouponAvailabilityCalendar $availability, CouponValidity $validity, - array $legalInformation, - ProcessingCode $code, + array $couponValidityInformation, + array $couponLegalInformation, + ProcessingCode $processingCode, BrandCode $brandCode, ActorId $createdBy, + array $products = [], + ?CouponAvailabilityCalendar $availabilityCalendar = null, ?GS1 $gtin = null, ?\DateTimeInterface $updatedAt = null, ): self { return new self( id: $id, + code: $code, name: $name, - status: $status, - productName: $productName, amount: $amount, picture: $picture, - product: $product, diffusion: $diffusion, - availability: $availability, validity: $validity, - legalInformation: $legalInformation, - code: $code, + couponValidityInformation: $couponValidityInformation, + couponLegalInformation: $couponLegalInformation, + processingCode: $processingCode, brandCode: $brandCode, createdBy: $createdBy, + products: $products, + availabilityCalendar: $availabilityCalendar, gtin: $gtin, updatedAt: $updatedAt, ); diff --git a/src/Domain/Models/ValueObjects/Campaign/CampaignCoupon.php b/src/Domain/Models/ValueObjects/Campaign/CampaignCoupon.php index a3ed524..713d13b 100644 --- a/src/Domain/Models/ValueObjects/Campaign/CampaignCoupon.php +++ b/src/Domain/Models/ValueObjects/Campaign/CampaignCoupon.php @@ -4,17 +4,19 @@ declare(strict_types=1); namespace Domain\Models\ValueObjects\Campaign; +use Domain\Models\ValueObjects\CountryAuthorized; +use Domain\Models\ValueObjects\Coupon\CouponQuantity; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; -use Domain\Models\ValueObjects\Stock\StockId; use Domain\Models\ValueObjects\WarningThresholdQuantity; final readonly class CampaignCoupon { public function __construct( - public ProcessingCode $code, - public StockId $qty, - public WarningThresholdQuantity $warningThresholdQty, + public ProcessingCode $processingCode, + public CouponQuantity $quantity, + public CountryAuthorized $availableIn, + public ?WarningThresholdQuantity $warningThresholdQuantity = null, public ?PrintRules $printRules = null, ) { } diff --git a/src/Domain/Models/ValueObjects/Coupon/CouponCode.php b/src/Domain/Models/ValueObjects/Coupon/CouponCode.php new file mode 100644 index 0000000..09a09c4 --- /dev/null +++ b/src/Domain/Models/ValueObjects/Coupon/CouponCode.php @@ -0,0 +1,26 @@ +legalText === $other->legalText && $this->forLocale === $other->forLocale; - } } diff --git a/src/Domain/Models/ValueObjects/Coupon/CouponProduct.php b/src/Domain/Models/ValueObjects/Coupon/CouponProduct.php deleted file mode 100644 index f297569..0000000 --- a/src/Domain/Models/ValueObjects/Coupon/CouponProduct.php +++ /dev/null @@ -1,22 +0,0 @@ -name) === '') { - throw new \DomainException('Coupon Product name cannot be empty.'); - } - } -} diff --git a/src/Domain/Models/ValueObjects/Coupon/CouponProductInfo.php b/src/Domain/Models/ValueObjects/Coupon/CouponProductInfo.php deleted file mode 100644 index 2b00b78..0000000 --- a/src/Domain/Models/ValueObjects/Coupon/CouponProductInfo.php +++ /dev/null @@ -1,46 +0,0 @@ -name); - - if ($trimmedName === '') { - throw new \DomainException('Product Info name cannot be empty.'); - } - - $trimmedRange = mb_trim($this->range); - - if ($trimmedRange === '') { - throw new \DomainException('Product Info range cannot be empty.'); - } - - $trimmedValidityText = mb_trim($this->validityText); - - if ($trimmedValidityText === '') { - throw new \DomainException('Product Info validity text cannot be empty.'); - } - } - - #[\Override] - public function equals(mixed $other): bool - { - return $other instanceof self - && $this->name === $other->name - && $this->range === $other->range - && $this->validityText === $other->validityText - && $this->forLocale === $other->forLocale; - } -} diff --git a/src/Domain/Models/ValueObjects/Coupon/CouponQuantity.php b/src/Domain/Models/ValueObjects/Coupon/CouponQuantity.php new file mode 100644 index 0000000..d2f5bb0 --- /dev/null +++ b/src/Domain/Models/ValueObjects/Coupon/CouponQuantity.php @@ -0,0 +1,16 @@ +quantity <= 0) { + throw new \DomainException('CouponQuantity quantity must be positive.'); + } + } +} diff --git a/src/Domain/Models/ValueObjects/Coupon/CouponValidityInformation.php b/src/Domain/Models/ValueObjects/Coupon/CouponValidityInformation.php new file mode 100644 index 0000000..517b714 --- /dev/null +++ b/src/Domain/Models/ValueObjects/Coupon/CouponValidityInformation.php @@ -0,0 +1,19 @@ +validityText) === '') { + throw new \DomainException('Validity text cannot be empty.'); + } + } +} diff --git a/src/Domain/Models/ValueObjects/CouponPicture.php b/src/Domain/Models/ValueObjects/CouponPicture.php index ec29916..a85f12f 100644 --- a/src/Domain/Models/ValueObjects/CouponPicture.php +++ b/src/Domain/Models/ValueObjects/CouponPicture.php @@ -4,19 +4,12 @@ declare(strict_types=1); namespace Domain\Models\ValueObjects; -use Shared\DDD\Models\Equatable; - -final readonly class CouponPicture implements Equatable +final readonly class CouponPicture { public function __construct( - public PicturePath $product, - public PicturePath $thumbnail + public PicturePath $decoration, + public PicturePath $thumbnail, + public PicturePath $inactiveThumbnail ) { } - - #[\Override] - public function equals(mixed $other): bool - { - return $other instanceof self && $this->product->equals($other->product) && $this->thumbnail->equals($other->thumbnail); - } } diff --git a/src/Domain/Models/ValueObjects/CouponProductName.php b/src/Domain/Models/ValueObjects/CouponProductName.php deleted file mode 100644 index 826389d..0000000 --- a/src/Domain/Models/ValueObjects/CouponProductName.php +++ /dev/null @@ -1,16 +0,0 @@ -value) === '') { - throw new \DomainException('Coupon product name cannot be empty.'); - } - } -} diff --git a/src/Domain/Models/ValueObjects/RequestedCoupon/RequestedCoupon.php b/src/Domain/Models/ValueObjects/RequestedCoupon/RequestedCoupon.php index 306e68b..683cd5c 100644 --- a/src/Domain/Models/ValueObjects/RequestedCoupon/RequestedCoupon.php +++ b/src/Domain/Models/ValueObjects/RequestedCoupon/RequestedCoupon.php @@ -5,28 +5,41 @@ declare(strict_types=1); namespace Domain\Models\ValueObjects\RequestedCoupon; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; +use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\WarningThresholdQuantity; final readonly class RequestedCoupon { + /** + * @param array $couponValidityInformation + * @param array $products + */ public function __construct( public CouponName $name, - public CouponProductName $productName, public RequestedCouponQuantity $quantity, - public WarningThresholdQuantity $warningThresholdQuantity, - public Period $period, + public Period $diffusion, public MoneyAmount $moneyAmount, public ProcessingCode $processingCode, public CouponPicture $couponPicture, - public ?GS1 $gs1, - public CouponValidity $couponValidity + public CouponValidity $couponValidity, + public array $couponValidityInformation, + public array $products = [], + public ?PrintRules $printRules = null, + public ?GS1 $gs1 = null, + public ?WarningThresholdQuantity $warningThresholdQuantity = null, ) { + foreach ($this->couponValidityInformation as $validityInformation) { + if (!$validityInformation instanceof CouponValidityInformation) { + throw new \DomainException('Coupon validity information must be CouponValidityInformation instances.'); + } + } } } diff --git a/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php index 200ab32..9103821 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php @@ -9,36 +9,39 @@ use Application\UseCases\CampaignRequest\Find\Input; use Application\UseCases\CampaignRequest\Find\Output\CampaignRequestFound; use Application\UseCases\CampaignRequest\Find\Output\CampaignRequestNotFound; use Application\UseCases\CampaignRequest\Find\Output\Output; +use DateTime; use Domain\Enums\BarcodeFormat; -use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; -use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; +use Domain\Enums\Country; use Domain\Exceptions\BrandNotFoundException; use Domain\Models\Entities\Brand; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Brand\BrandId; use Domain\Models\ValueObjects\Brand\BrandName; +use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; +use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; +use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; +use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Models\ValueObjects\WarningThresholdQuantity; use Domain\Ports\Repositories\BrandRepository; use Domain\Ports\Repositories\CampaignRequestRepository; use Shared\DDD\UseCases\Input as BaseInput; use Shared\Enums\Currency; -use DateTime; -use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Shared\ValueObjects\Email; use Shared\ValueObjects\Money; use Shared\ValueObjects\Rcs; @@ -49,15 +52,21 @@ describe('find campaign request use case', function (): void { beforeEach(function (): void { $this->requestedCoupon = new RequestedCoupon( new CouponName('Coupon A'), - new CouponProductName('Product A'), new RequestedCouponQuantity(100), - new WarningThresholdQuantity(10), new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), new MoneyAmount(new Money(Currency::EUR, 300)), new ProcessingCode('PC1'), - new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')), + new CouponPicture( + new PicturePath('coupons/product.jpg'), + new PicturePath('coupons/thumbnail.jpg'), + new PicturePath('coupons/inactive_thumbnail.jpg') + ), + new CouponValidity(30, new DateTime('2026-02-01')), + [new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], + [new IncoCode(35784), new IncoCode(15522)], + new PrintRules(maxPerCustomer: 3), new GS1('01234567890128', BarcodeFormat::EAN13), - new CouponValidity(30, new DateTime()), + new WarningThresholdQuantity(10) ); }); @@ -68,9 +77,9 @@ describe('find campaign request use case', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, $brandCode, + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, ); @@ -97,9 +106,9 @@ describe('find campaign request use case', function (): void { // THEN expect($result)->toBeInstanceOf(CampaignRequestFound::class) ->and($result->brand)->toBe($brand) + ->and($result->name)->toBe($campaignRequest->name) ->and($result->applicant)->toBe($campaignRequest->applicant) ->and($result->campaignType)->toBe($campaignRequest->campaignType) - ->and($result->printRules)->toBe($campaignRequest->printRules) ->and($result->requestedCoupon)->toBe($campaignRequest->requestedCoupon) ->and($result->status)->toBe($campaignRequest->status) ->and($result->rejectionReason)->toBeNull(); @@ -113,9 +122,9 @@ describe('find campaign request use case', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, $code, + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::REJECTED, $rejectionReason, @@ -173,9 +182,9 @@ describe('find campaign request use case', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, $brandCode, + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, ); diff --git a/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php index cc93d70..11da86f 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php @@ -4,11 +4,13 @@ declare(strict_types=1); namespace Application\UseCases\CampaignRequest\List; +use DateTime; use Domain\Enums\BarcodeFormat; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; +use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestFilters; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; @@ -16,21 +18,21 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortClause; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortCriteria; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortField; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; +use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Models\ValueObjects\WarningThresholdQuantity; use Domain\Ports\Repositories\CampaignRequestRepository; use Shared\Enums\Currency; -use DateTime; -use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Shared\ValueObjects\Email; use Shared\ValueObjects\Money; use Shared\ValueObjects\Pagination\Paginated; @@ -42,28 +44,40 @@ describe('list campaigns request use case', function (): void { beforeEach(function (): void { $this->couponA = new RequestedCoupon( new CouponName('Coupon A'), - new CouponProductName('Product A'), new RequestedCouponQuantity(100), - new WarningThresholdQuantity(10), new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), new MoneyAmount(new Money(Currency::EUR, 300)), new ProcessingCode('PC1'), - new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')), + new CouponPicture( + new PicturePath('coupons/product.jpg'), + new PicturePath('coupons/thumbnail.jpg'), + new PicturePath('coupons/inactive_thumbnail.jpg') + ), + new CouponValidity(30, new DateTime('2026-02-01')), + [new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], + [new IncoCode(35784), new IncoCode(15522)], + new PrintRules(maxPerCustomer: 3), new GS1('01234567890128', BarcodeFormat::EAN13), - new CouponValidity(30, new DateTime()), + new WarningThresholdQuantity(10) ); $this->couponB = new RequestedCoupon( new CouponName('Coupon B'), - new CouponProductName('Product B'), new RequestedCouponQuantity(100), - new WarningThresholdQuantity(10), new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), new MoneyAmount(new Money(Currency::EUR, 300)), new ProcessingCode('PC2'), - new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')), + new CouponPicture( + new PicturePath('coupons/product.jpg'), + new PicturePath('coupons/thumbnail.jpg'), + new PicturePath('coupons/inactive_thumbnail.jpg') + ), + new CouponValidity(30, new DateTime('2026-02-01')), + [new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], + [new IncoCode(24514), new IncoCode(46259)], + new PrintRules(maxPerCustomer: 3), new GS1('01234567890128', BarcodeFormat::EAN13), - new CouponValidity(30, new DateTime()), + new WarningThresholdQuantity(10) ); }); @@ -73,18 +87,18 @@ describe('list campaigns request use case', function (): void { $campaignRequest1 = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->couponA, CampaignRequestStatus::CREATED, ); $campaignRequest2 = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA02'), + new CampaignName('Campaign B'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->couponB, CampaignRequestStatus::CREATED, ); @@ -117,6 +131,7 @@ describe('list campaigns request use case', function (): void { expect($items)->toHaveCount(2) ->and($items[0])->toBeInstanceOf(CampaignRequestListItem::class) ->and($items[0]->brandCode)->toBe($campaignRequest1->brandCode) + ->and($items[0]->name)->toBe($campaignRequest1->name) ->and($items[0]->applicant)->toBe($campaignRequest1->applicant) ->and($items[0]->campaignType)->toBe($campaignRequest1->campaignType) ->and($items[0]->status)->toBe($campaignRequest1->status) @@ -130,9 +145,9 @@ describe('list campaigns request use case', function (): void { $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->couponA, CampaignRequestStatus::CREATED, ); @@ -157,6 +172,7 @@ describe('list campaigns request use case', function (): void { expect($items)->toHaveCount(1) ->and($items[0])->toBeInstanceOf(CampaignRequestListItem::class) ->and($items[0]->brandCode)->toBe($campaignRequest->brandCode) + ->and($items[0]->name)->toBe($campaignRequest->name) ->and($items[0]->status)->toBe($campaignRequest->status) ->and($result->campaignsRequest->getTotal())->toBe(1) ->and($result->campaignsRequest->getPagination())->toBe($pagination); @@ -168,18 +184,18 @@ describe('list campaigns request use case', function (): void { $campaignRequest1 = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->couponA, CampaignRequestStatus::CREATED, ); $campaignRequest2 = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA02'), + new CampaignName('Campaign B'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->couponB, CampaignRequestStatus::CREATED, ); diff --git a/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php index 9ba1a97..0d95d6b 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php @@ -10,55 +10,64 @@ use Application\UseCases\CampaignRequest\Reject\Output\CampaignRequestRejected; use Application\UseCases\CampaignRequest\Reject\Output\Output; use Application\UseCases\CampaignRequest\Reject\RejectCampaignRequest; use Carbon\CarbonImmutable; +use DateTime; use Domain\Enums\BarcodeFormat; -use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; -use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; +use Domain\Enums\Country; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; +use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; +use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; +use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; +use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Models\ValueObjects\WarningThresholdQuantity; use Domain\Ports\Repositories\CampaignRequestRepository; use Shared\Enums\Currency; -use DateTime; -use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Shared\ValueObjects\Email; use Shared\ValueObjects\Money; covers(RejectCampaignRequest::class, Input::class, CampaignRequestRejected::class, CampaignRequestNotFound::class); describe('reject campaign request use case', function (): void { - afterEach(function (): void { - CarbonImmutable::setTestNow(); - }); - beforeEach(function (): void { $this->requestedCoupon = new RequestedCoupon( new CouponName('Coupon A'), - new CouponProductName('Product A'), new RequestedCouponQuantity(100), - new WarningThresholdQuantity(10), new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), new MoneyAmount(new Money(Currency::EUR, 300)), new ProcessingCode('PC1'), - new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')), + new CouponPicture( + new PicturePath('coupons/product.jpg'), + new PicturePath('coupons/thumbnail.jpg'), + new PicturePath('coupons/inactive_thumbnail.jpg') + ), + new CouponValidity(30, new DateTime('2026-02-01')), + [new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], + [new IncoCode(35784), new IncoCode(15522)], + new PrintRules(maxPerCustomer: 3), new GS1('01234567890128', BarcodeFormat::EAN13), - new CouponValidity(30, new DateTime()), + new WarningThresholdQuantity(10) ); }); + afterEach(function (): void { + CarbonImmutable::setTestNow(); + }); + it('rejects a campaign request and persists it', function (): void { // GIVEN CarbonImmutable::setTestNow('2026-06-15 12:00:00'); @@ -66,9 +75,9 @@ describe('reject campaign request use case', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, ); @@ -118,9 +127,9 @@ describe('reject campaign request use case', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::TRANSFORMED, ); diff --git a/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php b/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php index d9dc6fb..a978935 100644 --- a/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php +++ b/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php @@ -4,33 +4,37 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\Entities; +use Carbon\CarbonImmutable; +use DateTime; use Domain\Enums\BarcodeFormat; -use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; -use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; +use Domain\Enums\Country; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; +use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; +use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; +use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; +use Domain\Models\ValueObjects\CountryAuthorized; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; -use Ramsey\Uuid\Uuid; -use Shared\Enums\Currency; -use DateTime; -use Shared\ValueObjects\Email; -use Shared\ValueObjects\Money; -use Carbon\CarbonImmutable; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Models\ValueObjects\WarningThresholdQuantity; +use Ramsey\Uuid\Uuid; +use Shared\Enums\Currency; +use Shared\ValueObjects\Email; +use Shared\ValueObjects\Money; covers(CampaignRequest::class); @@ -42,15 +46,21 @@ describe('campaign request', function (): void { beforeEach(function (): void { $this->requestedCoupon = new RequestedCoupon( new CouponName('Coupon A'), - new CouponProductName('Product A'), new RequestedCouponQuantity(100), - new WarningThresholdQuantity(10), new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), new MoneyAmount(new Money(Currency::EUR, 300)), new ProcessingCode('PC1'), - new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')), + new CouponPicture( + new PicturePath('coupons/product.jpg'), + new PicturePath('coupons/thumbnail.jpg'), + new PicturePath('coupons/inactive_thumbnail.jpg') + ), + new CouponValidity(30, new DateTime('2026-02-01')), + [new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], + [new IncoCode(35784), new IncoCode(15522)], + new PrintRules(maxPerCustomer: 3), new GS1('01234567890128', BarcodeFormat::EAN13), - new CouponValidity(30, new DateTime()), + new WarningThresholdQuantity(10) ); }); @@ -58,9 +68,9 @@ describe('campaign request', function (): void { // GIVEN $id = CampaignRequestId::generate(); $code = new BrandCode('AAA01'); + $name = new CampaignName('Campaign A'); $applicant = new Applicant('John', 'Doe', new Email('john.doe@example.com')); $campaignType = CampaignRequestType::CLUB; - $printRules = new PrintRules(maxPerCustomer: 3); $requestedCoupon = $this->requestedCoupon; $status = CampaignRequestStatus::CREATED; @@ -68,9 +78,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, $code, + $name, $applicant, $campaignType, - $printRules, $requestedCoupon, $status, ); @@ -78,31 +88,15 @@ describe('campaign request', function (): void { // THEN expect($campaignRequest->id())->toBe($id) ->and($campaignRequest->brandCode)->toBe($code) + ->and($campaignRequest->name)->toBe($name) ->and($campaignRequest->applicant)->toBe($applicant) ->and($campaignRequest->campaignType)->toBe($campaignType) - ->and($campaignRequest->printRules)->toBe($printRules) ->and($campaignRequest->requestedCoupon)->toBe($requestedCoupon) ->and($campaignRequest->status)->toBe($status) ->and($campaignRequest->updatedAt)->toBeNull() ->and($campaignRequest->updatedAt())->toBeNull(); }); - it('hydrates a campaign request with a null printRules', function (): void { - // WHEN - $campaignRequest = CampaignRequest::hydrate( - CampaignRequestId::generate(), - new BrandCode('AAA01'), - new Applicant('John', 'Doe', new Email('john.doe@example.com')), - CampaignRequestType::CLUB, - null, - $this->requestedCoupon, - CampaignRequestStatus::CREATED, - ); - - // THEN - expect($campaignRequest->printRules)->toBeNull(); - }); - it('hydrates a campaign request with an explicit updatedAt date', function (): void { // GIVEN $updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC')); @@ -111,9 +105,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, null, @@ -133,9 +127,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::REJECTED, $rejectionReason, @@ -151,9 +145,9 @@ describe('campaign request', function (): void { $action = fn (): CampaignRequest => CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::REJECTED, ); @@ -170,9 +164,9 @@ describe('campaign request', function (): void { $action = fn (): CampaignRequest => CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, new RejectionReason('Missing legal documents.'), @@ -191,9 +185,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, ); @@ -207,9 +201,9 @@ describe('campaign request', function (): void { ->and($rejected->rejectionReason)->toBe($reason) ->and($rejected->id())->toBe($campaignRequest->id()) ->and($rejected->brandCode)->toBe($campaignRequest->brandCode) + ->and($rejected->name)->toBe($campaignRequest->name) ->and($rejected->applicant)->toBe($campaignRequest->applicant) ->and($rejected->campaignType)->toBe($campaignRequest->campaignType) - ->and($rejected->printRules)->toBe($campaignRequest->printRules) ->and($rejected->requestedCoupon)->toBe($campaignRequest->requestedCoupon) ->and($rejected->updatedAt)->not->toBeNull() ->and($rejected->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 12:00:00'); @@ -220,9 +214,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::TRANSFORMED, ); @@ -239,9 +233,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::REJECTED, new RejectionReason('First reason.'), @@ -261,9 +255,9 @@ describe('campaign request', function (): void { $campaignRequest = CampaignRequest::hydrate( $id, new BrandCode('AAA01'), + new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - new PrintRules(maxPerCustomer: 3), $this->requestedCoupon, CampaignRequestStatus::CREATED, ); diff --git a/tests/Unit/Domain/Models/Entities/CampaignTest.php b/tests/Unit/Domain/Models/Entities/CampaignTest.php index 25e96ba..d81b195 100644 --- a/tests/Unit/Domain/Models/Entities/CampaignTest.php +++ b/tests/Unit/Domain/Models/Entities/CampaignTest.php @@ -7,30 +7,21 @@ namespace Tests\Unit\Domain\Models\Entities; use Carbon\CarbonImmutable; use Shared\Enums\Country; use Domain\Models\Entities\Campaign; -use Domain\Models\Entities\Tag; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Campaign\CampaignCode; use Domain\Models\ValueObjects\Campaign\CampaignCoupon; use Domain\Models\ValueObjects\Campaign\CampaignId; use Domain\Models\ValueObjects\Campaign\CampaignName; +use Domain\Models\ValueObjects\CountryAuthorized; +use Domain\Models\ValueObjects\Coupon\CouponQuantity; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; -use Domain\Models\ValueObjects\Stock\StockId; -use Domain\Models\ValueObjects\Tag\TagId; -use Domain\Models\ValueObjects\Tag\TagName; use Domain\Models\ValueObjects\WarningThresholdQuantity; use Ramsey\Uuid\Uuid; covers(Campaign::class); describe('campaign', function (): void { - beforeEach(function (): void { - $this->tag = Tag::hydrate( - TagId::generate(), - new TagName('Tag A'), - ); - }); - afterEach(function (): void { CarbonImmutable::setTestNow(); }); @@ -42,40 +33,36 @@ describe('campaign', function (): void { $name = new CampaignName('Name'); $publication = \boolval(0); $printAllAtOnce = \boolval(1); - $availableIn = Country::FRANCE; $coupons = [ new CampaignCoupon( new ProcessingCode('PC1'), - StockId::generate(), + new CouponQuantity(100), + new CountryAuthorized(Country::FRANCE), new WarningThresholdQuantity(100), new PrintRules(maxPerCustomer: 3) ) ]; - $brand = new BrandCode('AAA01'); + $brandCode = new BrandCode('AAA01'); // WHEN $campaign = Campaign::hydrate( $id, $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand, + $brandCode, ); // THEN expect($campaign->id())->toBe($id) ->and($campaign->code)->toBe($code) - ->and($campaign->tag)->toBe($this->tag) ->and($campaign->name)->toBe($name) ->and($campaign->publication)->toBe($publication) ->and($campaign->printAllAtOnce)->toBe($printAllAtOnce) - ->and($campaign->availableIn)->toBe($availableIn) ->and($campaign->coupons)->toBe($coupons) - ->and($campaign->brand)->toBe($brand) + ->and($campaign->brandCode)->toBe($brandCode) ->and($campaign->updatedAt)->toBeNull() ->and($campaign->updatedAt())->toBeNull(); }); @@ -87,21 +74,18 @@ describe('campaign', function (): void { $name = new CampaignName('Name'); $publication = \boolval(0); $printAllAtOnce = \boolval(1); - $availableIn = Country::FRANCE; $coupons = []; - $brand = new BrandCode('AAA01'); + $brandCode = new BrandCode('AAA01'); // WHEN $campaign = Campaign::hydrate( $id, $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand, + $brandCode, ); // THEN @@ -115,29 +99,27 @@ describe('campaign', function (): void { $name = new CampaignName('Name'); $publication = \boolval(0); $printAllAtOnce = \boolval(1); - $availableIn = Country::FRANCE; $coupons = [ new CampaignCoupon( new ProcessingCode('PC1'), - StockId::generate(), + new CouponQuantity(100), + new CountryAuthorized(Country::FRANCE), new WarningThresholdQuantity(100), new PrintRules(maxPerCustomer: 3) ) ]; - $brand = new BrandCode('AAA01'); + $brandCode = new BrandCode('AAA01'); $updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC')); // WHEN $campaign = Campaign::hydrate( $id, $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand, + $brandCode, $updatedAt ); @@ -152,39 +134,35 @@ describe('campaign', function (): void { $name = new CampaignName('Name'); $publication = \boolval(0); $printAllAtOnce = \boolval(1); - $availableIn = Country::FRANCE; $coupons = [ new CampaignCoupon( new ProcessingCode('PC1'), - StockId::generate(), + new CouponQuantity(100), + new CountryAuthorized(Country::FRANCE), new WarningThresholdQuantity(100), new PrintRules(maxPerCustomer: 3) ) ]; - $brand = new BrandCode('AAA01'); + $brandCode = new BrandCode('AAA01'); // WHEN $campaign = Campaign::create( $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand + $brandCode ); // THEN expect($campaign->id())->toBeInstanceOf(CampaignId::class) ->and($campaign->code)->toBe($code) - ->and($campaign->tag)->toBe($this->tag) ->and($campaign->name)->toBe($name) ->and($campaign->publication)->toBe($publication) ->and($campaign->printAllAtOnce)->toBe($printAllAtOnce) - ->and($campaign->availableIn)->toBe($availableIn) ->and($campaign->coupons)->toBe($coupons) - ->and($campaign->brand)->toBe($brand) + ->and($campaign->brandCode)->toBe($brandCode) ->and($campaign->updatedAt)->toBeNull() ->and($campaign->updatedAt())->toBeNull(); }); @@ -195,37 +173,33 @@ describe('campaign', function (): void { $name = new CampaignName('Name'); $publication = \boolval(0); $printAllAtOnce = \boolval(1); - $availableIn = Country::FRANCE; $coupons = [ new CampaignCoupon( new ProcessingCode('PC1'), - StockId::generate(), + new CouponQuantity(100), + new CountryAuthorized(Country::FRANCE), new WarningThresholdQuantity(100), new PrintRules(maxPerCustomer: 3) ) ]; - $brand = new BrandCode('AAA01'); + $brandCode = new BrandCode('AAA01'); // WHEN $firstCampaign = Campaign::create( $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand + $brandCode ); $secondCampaign = Campaign::create( $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand + $brandCode ); // THEN @@ -239,15 +213,14 @@ describe('campaign', function (): void { $campaign = Campaign::hydrate( $id, new CampaignCode('Camp100'), - $this->tag, new CampaignName('Name'), \boolval(0), \boolval(1), - Country::FRANCE, [ new CampaignCoupon( new ProcessingCode('PC1'), - StockId::generate(), + new CouponQuantity(100), + new CountryAuthorized(Country::FRANCE), new WarningThresholdQuantity(100), new PrintRules(maxPerCustomer: 3) ) @@ -269,21 +242,18 @@ describe('campaign', function (): void { $name = new CampaignName('Name'); $publication = \boolval(0); $printAllAtOnce = \boolval(1); - $availableIn = Country::FRANCE; $coupons = ['PC1']; - $brand = new BrandCode('AAA01'); + $brandCode = new BrandCode('AAA01'); // WHEN / THEN expect(fn (): Campaign => Campaign::hydrate( $id, $code, - $this->tag, $name, $publication, $printAllAtOnce, - $availableIn, $coupons, - $brand + $brandCode ))->toThrow(\DomainException::class, 'Campaign coupons must be CampaignCoupon instances.'); }); }); diff --git a/tests/Unit/Domain/Models/Entities/CouponTest.php b/tests/Unit/Domain/Models/Entities/CouponTest.php index 97c243c..2ca903e 100644 --- a/tests/Unit/Domain/Models/Entities/CouponTest.php +++ b/tests/Unit/Domain/Models/Entities/CouponTest.php @@ -5,199 +5,323 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\Entities; use Carbon\CarbonImmutable; +use DateTime; use Domain\Enums\BarcodeFormat; use Shared\Enums\Country; use Domain\Models\Entities\Coupon; use Domain\Models\ValueObjects\Actor\ActorId; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar; +use Domain\Models\ValueObjects\Coupon\CouponCode; use Domain\Models\ValueObjects\Coupon\CouponDiffusion; use Domain\Models\ValueObjects\Coupon\CouponId; use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; -use Domain\Models\ValueObjects\Coupon\CouponProduct; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\ProcessingCode; use Ramsey\Uuid\Uuid; use Shared\Enums\Currency; -use DateTime; use Shared\ValueObjects\Money; covers(Coupon::class); describe('coupon', function (): void { + beforeEach(function (): void { + $this->code = new CouponCode('COU01'); + $this->name = new CouponName('Name'); + $this->amount = new MoneyAmount(new Money(Currency::EUR, 1)); + $this->picture = new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/inactive_thumbnail.jpg') + ); + $this->diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]); + $this->validity = new CouponValidity(30, new DateTime('2026-02-01')); + $this->couponValidityInformation = [ + new CouponValidityInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + new CountryAuthorized(Country::FRANCE) + ) + ]; + $this->couponLegalInformation = [ + new CouponLegalInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + new CountryAuthorized(Country::FRANCE) + ) + ]; + $this->processingCode = new ProcessingCode('5425003944130'); + $this->brandCode = new BrandCode('AAA01'); + $this->createdBy = ActorId::generate(); + $this->products = [new IncoCode(35784), new IncoCode(15522)]; + $this->availabilityCalendar = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); + $this->gtin = new GS1('110101010', BarcodeFormat::EAN13); + }); + afterEach(function (): void { CarbonImmutable::setTestNow(); }); it('creates a coupon', function (): void { // GIVEN - $id = CouponId::generate(); - $name = new CouponName('Name'); - $status = \boolval(0); - $productName = new CouponProductName('Lorem ipsum'); - $amount = new MoneyAmount(new Money(Currency::EUR, 1)); - $picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')); - $product = new CouponProduct('Lorem ipsum', [], []); - $diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]); - $availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); - $validity = new CouponValidity(30, new DateTime('2026-11-30')); - $legalInformation = [ - new CouponLegalInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $code = new ProcessingCode('5425003944130'); - $brandCode = new BrandCode('AAA01'); - $createdBy = ActorId::generate(); - $gtin = new GS1('110101010', BarcodeFormat::EAN13); + $id = CouponId::generate(); // WHEN $coupon = Coupon::hydrate( $id, - $name, - $status, - $productName, - $amount, - $picture, - $product, - $diffusion, - $availability, - $validity, - $legalInformation, - $code, - $brandCode, - $createdBy, - $gtin, + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + $this->couponValidityInformation, + $this->couponLegalInformation, + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, ); // THEN expect($coupon->id())->toBe($id) - ->and($coupon->name)->toBe($name) - ->and($coupon->status)->toBe($status) - ->and($coupon->productName)->toBe($productName) - ->and($coupon->amount)->toBe($amount) - ->and($coupon->picture)->toBe($picture) - ->and($coupon->product)->toBe($product) - ->and($coupon->diffusion)->toBe($diffusion) - ->and($coupon->availability)->toBe($availability) - ->and($coupon->validity)->toBe($validity) - ->and($coupon->legalInformation)->toBe($legalInformation) - ->and($coupon->code)->toBe($code) - ->and($coupon->brandCode)->toBe($brandCode) - ->and($coupon->createdBy)->toBe($createdBy) - ->and($coupon->gtin)->toBe($gtin) + ->and($coupon->code)->toBe($this->code) + ->and($coupon->name)->toBe($this->name) + ->and($coupon->amount)->toBe($this->amount) + ->and($coupon->picture)->toBe($this->picture) + ->and($coupon->diffusion)->toBe($this->diffusion) + ->and($coupon->validity)->toBe($this->validity) + ->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation) + ->and($coupon->couponLegalInformation)->toBe($this->couponLegalInformation) + ->and($coupon->processingCode)->toBe($this->processingCode) + ->and($coupon->brandCode)->toBe($this->brandCode) + ->and($coupon->createdBy)->toBe($this->createdBy) + ->and($coupon->products)->toBe($this->products) + ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) + ->and($coupon->gtin)->toBe($this->gtin) ->and($coupon->updatedAt)->toBeNull() ->and($coupon->updatedAt())->toBeNull(); }); - it('creates a coupon without gs1', function (): void { + it('hydrates a coupon without gs1', function (): void { // GIVEN - $id = CouponId::generate(); - $name = new CouponName('Name'); - $status = \boolval(0); - $productName = new CouponProductName('Lorem ipsum'); - $amount = new MoneyAmount(new Money(Currency::EUR, 1)); - $picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')); - $product = new CouponProduct('Lorem ipsum', [], []); - $diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]); - $availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); - $validity = new CouponValidity(30, new DateTime('2026-11-30')); - $legalInformation = [ - new CouponLegalInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $code = new ProcessingCode('5425003944130'); - $brandCode = new BrandCode('AAA01'); - $createdBy = ActorId::generate(); + $id = CouponId::generate(); // WHEN $coupon = Coupon::hydrate( - $id, - $name, - $status, - $productName, - $amount, - $picture, - $product, - $diffusion, - $availability, - $validity, - $legalInformation, - $code, - $brandCode, - $createdBy, + id: $id, + code: $this->code, + name: $this->name, + amount: $this->amount, + picture: $this->picture, + diffusion: $this->diffusion, + validity: $this->validity, + couponValidityInformation: $this->couponValidityInformation, + couponLegalInformation: $this->couponLegalInformation, + processingCode: $this->processingCode, + brandCode: $this->brandCode, + createdBy: $this->createdBy, + products: $this->products, + availabilityCalendar: $this->availabilityCalendar + ); + + // THEN + expect($coupon->products)->toBe($this->products) + ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) + ->and($coupon->gtin)->toBeNull(); + }); + + it('hydrates a coupon without availability calendar', function (): void { + // GIVEN + $id = CouponId::generate(); + + // WHEN + $coupon = Coupon::hydrate( + id: $id, + code: $this->code, + name: $this->name, + amount: $this->amount, + picture: $this->picture, + diffusion: $this->diffusion, + validity: $this->validity, + couponValidityInformation: $this->couponValidityInformation, + couponLegalInformation: $this->couponLegalInformation, + processingCode: $this->processingCode, + brandCode: $this->brandCode, + createdBy: $this->createdBy, + products: $this->products, + gtin: $this->gtin + ); + + // THEN + expect($coupon->availabilityCalendar)->toBeNull() + ->and($coupon->products)->toBe($this->products) + ->and($coupon->gtin)->toBe($this->gtin); + }); + + it('hydrates a coupon without products', function (): void { + // GIVEN + $id = CouponId::generate(); + + // WHEN + $coupon = Coupon::hydrate( + id: $id, + code: $this->code, + name: $this->name, + amount: $this->amount, + picture: $this->picture, + diffusion: $this->diffusion, + validity: $this->validity, + couponValidityInformation: $this->couponValidityInformation, + couponLegalInformation: $this->couponLegalInformation, + processingCode: $this->processingCode, + brandCode: $this->brandCode, + createdBy: $this->createdBy, + availabilityCalendar: $this->availabilityCalendar, + gtin: $this->gtin + ); + + // THEN + expect($coupon->products)->toBe([]) + ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) + ->and($coupon->gtin)->toBe($this->gtin); + }); + + it('hydrates a coupon with only required fields', function (): void { + // GIVEN + $id = CouponId::generate(); + + // WHEN + $coupon = Coupon::hydrate( + id: $id, + code: $this->code, + name: $this->name, + amount: $this->amount, + picture: $this->picture, + diffusion: $this->diffusion, + validity: $this->validity, + couponValidityInformation: $this->couponValidityInformation, + couponLegalInformation: $this->couponLegalInformation, + processingCode: $this->processingCode, + brandCode: $this->brandCode, + createdBy: $this->createdBy, ); // THEN expect($coupon->id())->toBe($id) - ->and($coupon->name)->toBe($name) - ->and($coupon->status)->toBe($status) - ->and($coupon->productName)->toBe($productName) - ->and($coupon->amount)->toBe($amount) - ->and($coupon->picture)->toBe($picture) - ->and($coupon->product)->toBe($product) - ->and($coupon->diffusion)->toBe($diffusion) - ->and($coupon->availability)->toBe($availability) - ->and($coupon->validity)->toBe($validity) - ->and($coupon->legalInformation)->toBe($legalInformation) - ->and($coupon->code)->toBe($code) - ->and($coupon->brandCode)->toBe($brandCode) - ->and($coupon->createdBy)->toBe($createdBy) + ->and($coupon->products)->toBe([]) + ->and($coupon->availabilityCalendar)->toBeNull() + ->and($coupon->gtin)->toBeNull() ->and($coupon->updatedAt)->toBeNull() ->and($coupon->updatedAt())->toBeNull(); }); - it('creates a coupon with an explicit updatedAt date', function (): void { + it('accepts empty validity and legal information lists', function (): void { // GIVEN - $id = CouponId::generate(); - $name = new CouponName('Name'); - $status = \boolval(0); - $productName = new CouponProductName('Lorem ipsum'); - $amount = new MoneyAmount(new Money(Currency::EUR, 1)); - $picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')); - $product = new CouponProduct('Lorem ipsum', [], []); - $diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]); - $availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); - $validity = new CouponValidity(30, new DateTime('2026-11-30')); - $legalInformation = [ - new CouponLegalInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $code = new ProcessingCode('5425003944130'); - $brandCode = new BrandCode('AAA01'); - $gtin = new GS1('110101010', BarcodeFormat::EAN13); - $createdBy = ActorId::generate(); - $updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC')); + $id = CouponId::generate(); // WHEN $coupon = Coupon::hydrate( $id, - $name, - $status, - $productName, - $amount, - $picture, - $product, - $diffusion, - $availability, - $validity, - $legalInformation, - $code, - $brandCode, - $createdBy, - $gtin, + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + [], + [], + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, + ); + + // THEN + expect($coupon->couponValidityInformation)->toBe([]) + ->and($coupon->couponLegalInformation)->toBe([]); + }); + + it('throws when validity information contains an invalid item', function (): void { + // GIVEN + $id = CouponId::generate(); + + // WHEN / THEN + expect(fn () => Coupon::hydrate( + $id, + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + ['Not a CouponValidityInformation'], + $this->couponLegalInformation, + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, + ))->toThrow(\DomainException::class, 'Coupon validity information must be CouponValidityInformation instances.'); + }); + + it('throws when legal information contains an invalid item', function (): void { + // GIVEN + $id = CouponId::generate(); + + // WHEN / THEN + expect(fn () => Coupon::hydrate( + $id, + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + $this->couponValidityInformation, + ['Not a CouponLegalInformation'], + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, + ))->toThrow(\DomainException::class, 'Coupon legal information must be CouponLegalInformation instances.'); + }); + + it('hydrates a coupon with an explicit updatedAt date', function (): void { + // GIVEN + $id = CouponId::generate(); + $updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC')); + + // WHEN + $coupon = Coupon::hydrate( + $id, + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + $this->couponValidityInformation, + $this->couponLegalInformation, + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, $updatedAt ); @@ -207,119 +331,77 @@ describe('coupon', function (): void { }); it('creates a new coupon with a freshly generated id', function (): void { - // GIVEN - $name = new CouponName('Name'); - $status = \boolval(0); - $productName = new CouponProductName('Lorem ipsum'); - $amount = new MoneyAmount(new Money(Currency::EUR, 1)); - $picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')); - $product = new CouponProduct('Lorem ipsum', [], []); - $diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]); - $availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); - $validity = new CouponValidity(30, new DateTime('2026-11-30')); - $legalInformation = [ - new CouponLegalInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $code = new ProcessingCode('5425003944130'); - $brandCode = new BrandCode('AAA01'); - $createdBy = ActorId::generate(); - $gtin = new GS1('110101010', BarcodeFormat::EAN13); - - // WHEN + // GIVEN / WHEN $coupon = Coupon::create( - $name, - $status, - $productName, - $amount, - $picture, - $product, - $diffusion, - $availability, - $validity, - $legalInformation, - $code, - $brandCode, - $createdBy, - $gtin, + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + $this->couponValidityInformation, + $this->couponLegalInformation, + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, ); // THEN expect($coupon->id())->toBeInstanceOf(CouponId::class) - ->and($coupon->name)->toBe($name) - ->and($coupon->status)->toBe($status) - ->and($coupon->productName)->toBe($productName) - ->and($coupon->amount)->toBe($amount) - ->and($coupon->picture)->toBe($picture) - ->and($coupon->product)->toBe($product) - ->and($coupon->diffusion)->toBe($diffusion) - ->and($coupon->availability)->toBe($availability) - ->and($coupon->validity)->toBe($validity) - ->and($coupon->legalInformation)->toBe($legalInformation) - ->and($coupon->code)->toBe($code) - ->and($coupon->brandCode)->toBe($brandCode) - ->and($coupon->createdBy)->toBe($createdBy) - ->and($coupon->gtin)->toBe($gtin) + ->and($coupon->code)->toBe($this->code) + ->and($coupon->name)->toBe($this->name) + ->and($coupon->amount)->toBe($this->amount) + ->and($coupon->picture)->toBe($this->picture) + ->and($coupon->diffusion)->toBe($this->diffusion) + ->and($coupon->validity)->toBe($this->validity) + ->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation) + ->and($coupon->couponLegalInformation)->toBe($this->couponLegalInformation) + ->and($coupon->processingCode)->toBe($this->processingCode) + ->and($coupon->brandCode)->toBe($this->brandCode) + ->and($coupon->createdBy)->toBe($this->createdBy) + ->and($coupon->products)->toBe($this->products) + ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) + ->and($coupon->gtin)->toBe($this->gtin) ->and($coupon->updatedAt)->toBeNull() ->and($coupon->updatedAt())->toBeNull(); }); it('generates a different id for each created coupon', function (): void { - // GIVEN - $name = new CouponName('Name'); - $status = \boolval(0); - $productName = new CouponProductName('Lorem ipsum'); - $amount = new MoneyAmount(new Money(Currency::EUR, 1)); - $picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')); - $product = new CouponProduct('Lorem ipsum', [], []); - $diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]); - $availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); - $validity = new CouponValidity(30, new DateTime('2026-11-30')); - $legalInformation = [ - new CouponLegalInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $code = new ProcessingCode('5425003944130'); - $brandCode = new BrandCode('AAA01'); - $createdBy = ActorId::generate(); - $gtin = new GS1('110101010', BarcodeFormat::EAN13); - - // WHEN + // GIVEN / WHEN $firstCoupon = Coupon::create( - $name, - $status, - $productName, - $amount, - $picture, - $product, - $diffusion, - $availability, - $validity, - $legalInformation, - $code, - $brandCode, - $createdBy, - $gtin + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + $this->couponValidityInformation, + $this->couponLegalInformation, + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, ); $secondCoupon = Coupon::create( - $name, - $status, - $productName, - $amount, - $picture, - $product, - $diffusion, - $availability, - $validity, - $legalInformation, - $code, - $brandCode, - $createdBy, - $gtin + $this->code, + $this->name, + $this->amount, + $this->picture, + $this->diffusion, + $this->validity, + $this->couponValidityInformation, + $this->couponLegalInformation, + $this->processingCode, + $this->brandCode, + $this->createdBy, + $this->products, + $this->availabilityCalendar, + $this->gtin, ); // THEN @@ -332,19 +414,23 @@ describe('coupon', function (): void { $id = new CouponId(Uuid::uuid7($expected)); $coupon = Coupon::hydrate( $id, + new CouponCode('COU01'), new CouponName('Name'), - \boolval(0), - new CouponProductName('Lorem ipsum'), new MoneyAmount(new Money(Currency::EUR, 1)), - new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')), - new CouponProduct('Lorem ipsum', [], []), + new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/inactive_thumbnail.jpg') + ), new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]), - new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]), - new CouponValidity(30, new DateTime('2026-11-30')), - [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE)], + new CouponValidity(30, new DateTime('2026-02-01')), + [new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], + [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', new CountryAuthorized(Country::FRANCE))], new ProcessingCode('5425003944130'), new BrandCode('AAA01'), ActorId::generate(), + [new IncoCode(35784), new IncoCode(15522)], + new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]), new GS1('110101010', BarcodeFormat::EAN13), ); diff --git a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php index 340d03c..4608cca 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php @@ -4,10 +4,12 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\ValueObjects\Campaign; +use Domain\Enums\Country; use Domain\Models\ValueObjects\Campaign\CampaignCoupon; +use Domain\Models\ValueObjects\CountryAuthorized; +use Domain\Models\ValueObjects\Coupon\CouponQuantity; use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; -use Domain\Models\ValueObjects\Stock\StockId; use Domain\Models\ValueObjects\WarningThresholdQuantity; covers(CampaignCoupon::class); @@ -15,33 +17,79 @@ covers(CampaignCoupon::class); describe('campaign coupon', function (): void { it('creates a valid campaign coupon', function (): void { // GIVEN - $code = new ProcessingCode('PC1'); - $qty = StockId::generate(); - $printRules = new PrintRules(maxPerCustomer: 3); - $warningThresholdQty = new WarningThresholdQuantity(100); + $processingCode = new ProcessingCode('PC1'); + $quantity = new CouponQuantity(10); + $availableIn = new CountryAuthorized(Country::FRANCE); + $warningThresholdQuantity = new WarningThresholdQuantity(100); + $printRules = new PrintRules(maxPerCustomer: 3); // WHEN - $campaignCoupon = new CampaignCoupon($code, $qty, $warningThresholdQty, $printRules); + $campaignCoupon = new CampaignCoupon($processingCode, $quantity, $availableIn, $warningThresholdQuantity, $printRules); // THEN - expect($campaignCoupon->code)->toBe($code) - ->and($campaignCoupon->qty)->toBe($qty) - ->and($campaignCoupon->printRules)->toBe($printRules) - ->and($campaignCoupon->warningThresholdQty)->toBe($warningThresholdQty); + expect($campaignCoupon->processingCode)->toBe($processingCode) + ->and($campaignCoupon->quantity)->toBe($quantity) + ->and($campaignCoupon->availableIn)->toBe($availableIn) + ->and($campaignCoupon->warningThresholdQuantity)->toBe($warningThresholdQuantity) + ->and($campaignCoupon->printRules)->toBe($printRules); + }); + + it('creates a valid campaign coupon whitout warning threshold quantity', function (): void { + // GIVEN + $processingCode = new ProcessingCode('PC1'); + $quantity = new CouponQuantity(10); + $availableIn = new CountryAuthorized(Country::FRANCE); + $printRules = new PrintRules(maxPerCustomer: 3); + + // WHEN + $campaignCoupon = new CampaignCoupon( + processingCode: $processingCode, + quantity: $quantity, + availableIn: $availableIn, + printRules: $printRules + ); + + // THEN + expect($campaignCoupon->processingCode)->toBe($processingCode) + ->and($campaignCoupon->quantity)->toBe($quantity) + ->and($campaignCoupon->availableIn)->toBe($availableIn) + ->and($campaignCoupon->printRules)->toBe($printRules); }); it('creates a valid campaign coupon whitout print rules', function (): void { // GIVEN - $code = new ProcessingCode('PC1'); - $qty = StockId::generate(); - $warningThresholdQty = new WarningThresholdQuantity(100); + $processingCode = new ProcessingCode('PC1'); + $quantity = new CouponQuantity(10); + $availableIn = new CountryAuthorized(Country::FRANCE); + $warningThresholdQuantity = new WarningThresholdQuantity(100); // WHEN - $campaignCoupon = new CampaignCoupon($code, $qty, $warningThresholdQty); + $campaignCoupon = new CampaignCoupon( + processingCode: $processingCode, + quantity: $quantity, + availableIn: $availableIn, + warningThresholdQuantity: $warningThresholdQuantity + ); // THEN - expect($campaignCoupon->code)->toBe($code) - ->and($campaignCoupon->qty)->toBe($qty) - ->and($campaignCoupon->warningThresholdQty)->toBe($warningThresholdQty); + expect($campaignCoupon->processingCode)->toBe($processingCode) + ->and($campaignCoupon->quantity)->toBe($quantity) + ->and($campaignCoupon->availableIn)->toBe($availableIn) + ->and($campaignCoupon->warningThresholdQuantity)->toBe($warningThresholdQuantity); + }); + + it('creates a valid campaign coupon whitout warning threshold quantity and print rules', function (): void { + // GIVEN + $processingCode = new ProcessingCode('PC1'); + $quantity = new CouponQuantity(10); + $availableIn = new CountryAuthorized(Country::FRANCE); + + // WHEN + $campaignCoupon = new CampaignCoupon(processingCode: $processingCode, quantity: $quantity, availableIn: $availableIn); + + // THEN + expect($campaignCoupon->processingCode)->toBe($processingCode) + ->and($campaignCoupon->quantity)->toBe($quantity) + ->and($campaignCoupon->availableIn)->toBe($availableIn); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponCodeTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponCodeTest.php new file mode 100644 index 0000000..a139b96 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponCodeTest.php @@ -0,0 +1,75 @@ +toBeInstanceOf(CouponCode::class) + ->and($code->value)->toBe($value); + }); +}); + +describe('coupon code validation rules', function (): void { + it('rejects invalid coupon codes', function (string $invalid): void { + // WHEN / THEN + expect(fn (): CouponCode => new CouponCode($invalid))->toThrow(\DomainException::class); + + // GIVEN + })->with([ + // rejects empty strings + '', + ' ', + "\t", + "\n", + // rejects numbers only + '12345', + '67890', + // rejects letters only + 'ABCDE', + 'FGHIJ', + // rejects codes with special characters + 'AB@12', + 'CD#34', + 'EF$56', + // rejects codes that have the same digit repeated + 'ABC11', + 'DEF22', + 'GHI33', + // rejects codes with lowercase letters + 'AbC12', + 'dEf34', + 'GhI56', + ]); + + it('rejects codes with more/less than 3 letters or more/less than 2 digits', function (string $invalid): void { + // WHEN / THEN + expect(fn (): CouponCode => new CouponCode($invalid))->toThrow(\DomainException::class); + + // GIVEN + })->with([ + 'ABCD12', + 'ABC123', + 'ABCDE123', + 'AB12', + 'A1', + 'AB1', + 'A12', + 'AB123', + 'A123', + 'ABC1', + 'AB12C', + ]); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php index 77fa49e..335caad 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Unit\Domain\Models\ValueObjects; +namespace Tests\Unit\Domain\Models\ValueObjects\Coupon; use Shared\Enums\Country; use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductInfoTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductInfoTest.php deleted file mode 100644 index 293e8b6..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductInfoTest.php +++ /dev/null @@ -1,40 +0,0 @@ -name)->toBe($name) - ->and($couponProductInfo->range)->toBe($range) - ->and($couponProductInfo->validityText)->toBe($validityText) - ->and($couponProductInfo->forLocale)->toBe($forLocale); - }); - - it('rejects invalid coupon product info', function (): void { - // GIVEN - $name = ''; - $range = ''; - $validityText = ''; - $forLocale = Country::FRANCE; - - // GIVEN / THEN - expect(fn (): CouponProductInfo => new CouponProductInfo($name, $range, $validityText, $forLocale))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductTest.php deleted file mode 100644 index 16e2459..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductTest.php +++ /dev/null @@ -1,36 +0,0 @@ -name)->toBe($name) - ->and($couponProduct->incoCodes)->toBe($incoCodes) - ->and($couponProduct->labels)->toBe($labels); - }); - - it('rejects invalid coupon product', function (): void { - // GIVEN - $name = ''; - $incoCodes = []; - $labels = []; - - // GIVEN / THEN - expect(fn (): CouponProduct => new CouponProduct($name, $incoCodes, $labels))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php new file mode 100644 index 0000000..f3aa565 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php @@ -0,0 +1,39 @@ +toBeInstanceOf(CouponQuantity::class) + ->and($couponQuantity->quantity)->toBe($quantity); + }); + + it('rejects a quantity of zero', function (): void { + // WHEN + $action = fn (): CouponQuantity => new CouponQuantity(0); + + // THEN + expect($action)->toThrow(\DomainException::class, 'CouponQuantity quantity must be positive.'); + }); + + it('rejects a negative quantity', function (): void { + // WHEN + $action = fn (): CouponQuantity => new CouponQuantity(-1); + + // THEN + expect($action)->toThrow(\DomainException::class, 'CouponQuantity quantity must be positive.'); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php new file mode 100644 index 0000000..3a43b39 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php @@ -0,0 +1,35 @@ +validityText)->toBe($validityText) + ->and($couponValidityInformation->forLocale)->toBe($forLocale); + }); + + it('rejects invalid coupon validity information', function (): void { + // GIVEN + $validityText = ''; + $forLocale = new CountryAuthorized(Country::FRANCE); + + // GIVEN / THEN + expect(fn (): CouponValidityInformation => new CouponValidityInformation($validityText, $forLocale))->toThrow(\DomainException::class); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/CouponProductNameTest.php b/tests/Unit/Domain/Models/ValueObjects/CouponProductNameTest.php deleted file mode 100644 index 0c96164..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/CouponProductNameTest.php +++ /dev/null @@ -1,30 +0,0 @@ -value)->toBe($value); - }); - - it('rejects invalid coupon product name', function (): void { - // GIVEN - $value = ''; - - // GIVEN / THEN - expect(fn (): CouponProductName => new CouponProductName($value))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php index 2913dc3..267a401 100644 --- a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php @@ -5,67 +5,239 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\ValueObjects\RequestedCoupon; use Carbon\CarbonImmutable; +use DateTime; use Domain\Enums\BarcodeFormat; +use Domain\Enums\Country; +use Domain\Models\ValueObjects\CountryAuthorized; use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; use Domain\Models\ValueObjects\CouponName; use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\CouponProductName; use Domain\Models\ValueObjects\GS1; +use Domain\Models\ValueObjects\IncoCode; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; +use Domain\Models\ValueObjects\PrintRules; use Domain\Models\ValueObjects\ProcessingCode; +use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Models\ValueObjects\WarningThresholdQuantity; use Shared\Enums\Currency; -use DateTime; -use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Shared\ValueObjects\Money; covers(RequestedCoupon::class); describe('requested coupon', function (): void { + beforeEach(function (): void { + $this->name = new CouponName('Coupon A'); + $this->quantity = new RequestedCouponQuantity(100); + $this->diffusion = new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')); + $this->moneyAmount = new MoneyAmount(new Money(Currency::EUR, 300)); + $this->processingCode = new ProcessingCode('PC1'); + $this->couponPicture = new CouponPicture( + new PicturePath('coupons/product.jpg'), + new PicturePath('coupons/thumbnail.jpg'), + new PicturePath('coupons/inactive_thumbnail.jpg') + ); + $this->couponValidity = new CouponValidity(30, new DateTime('2026-02-01')); + $this->couponValidityInformation = [ + new CouponValidityInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + new CountryAuthorized(Country::FRANCE) + ), + ]; + $this->products = [new IncoCode(35784), new IncoCode(15522)]; + $this->printRules = new PrintRules(maxPerCustomer: 3); + $this->gs1 = new GS1('01234567890128', BarcodeFormat::EAN13); + $this->warningThresholdQuantity = new WarningThresholdQuantity(10); + }); + afterEach(function (): void { CarbonImmutable::setTestNow(); }); it('hydrates a requested coupon with all its fields', function (): void { - // GIVEN - $name = new CouponName('Coupon A'); - $productName = new CouponProductName('Product A'); - $quantity = new RequestedCouponQuantity(100); - $warningThresholdQuantity = new WarningThresholdQuantity(10); - $period = new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')); - $moneyAmount = new MoneyAmount(new Money(Currency::EUR, 300)); - $processingCode = new ProcessingCode('PC1'); - $couponPicture = new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')); - $gs1 = new GS1('01234567890128', BarcodeFormat::EAN13); - $couponValidity = new CouponValidity(30, new DateTime()); - // WHEN + // GIVEN / WHEN $coupon = new RequestedCoupon( - $name, - $productName, - $quantity, - $warningThresholdQuantity, - $period, - $moneyAmount, - $processingCode, - $couponPicture, - $gs1, - $couponValidity, + $this->name, + $this->quantity, + $this->diffusion, + $this->moneyAmount, + $this->processingCode, + $this->couponPicture, + $this->couponValidity, + $this->couponValidityInformation, + $this->products, + $this->printRules, + $this->gs1, + $this->warningThresholdQuantity ); // THEN - expect($coupon->name)->toBe($name) - ->and($coupon->productName)->toBe($productName) - ->and($coupon->quantity)->toBe($quantity) - ->and($coupon->warningThresholdQuantity)->toBe($warningThresholdQuantity) - ->and($coupon->period)->toBe($period) - ->and($coupon->moneyAmount)->toBe($moneyAmount) - ->and($coupon->processingCode)->toBe($processingCode) - ->and($coupon->couponPicture)->toBe($couponPicture) - ->and($coupon->gs1)->toBe($gs1) - ->and($coupon->couponValidity)->toBe($couponValidity); + expect($coupon->name)->toBe($this->name) + ->and($coupon->quantity)->toBe($this->quantity) + ->and($coupon->diffusion)->toBe($this->diffusion) + ->and($coupon->moneyAmount)->toBe($this->moneyAmount) + ->and($coupon->processingCode)->toBe($this->processingCode) + ->and($coupon->couponPicture)->toBe($this->couponPicture) + ->and($coupon->couponValidity)->toBe($this->couponValidity) + ->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation) + ->and($coupon->products)->toBe($this->products) + ->and($coupon->printRules)->toBe($this->printRules) + ->and($coupon->gs1)->toBe($this->gs1) + ->and($coupon->warningThresholdQuantity)->toBe($this->warningThresholdQuantity); + }); + + it('hydrates a requested coupon without products', function (): void { + // GIVEN / WHEN + $coupon = new RequestedCoupon( + name: $this->name, + quantity: $this->quantity, + diffusion: $this->diffusion, + moneyAmount: $this->moneyAmount, + processingCode: $this->processingCode, + couponPicture:$this->couponPicture, + couponValidity: $this->couponValidity, + couponValidityInformation: $this->couponValidityInformation, + printRules: $this->printRules, + gs1: $this->gs1, + warningThresholdQuantity: $this->warningThresholdQuantity + ); + + // THEN + expect($coupon->products)->toBe([]) + ->and($coupon->printRules)->toBe($this->printRules) + ->and($coupon->gs1)->toBe($this->gs1) + ->and($coupon->warningThresholdQuantity)->toBe($this->warningThresholdQuantity); + }); + + it('hydrates a requested coupon without print rules', function (): void { + // GIVEN / WHEN + $coupon = new RequestedCoupon( + name: $this->name, + quantity: $this->quantity, + diffusion: $this->diffusion, + moneyAmount: $this->moneyAmount, + processingCode: $this->processingCode, + couponPicture:$this->couponPicture, + couponValidity: $this->couponValidity, + couponValidityInformation: $this->couponValidityInformation, + products: $this->products, + gs1: $this->gs1, + warningThresholdQuantity: $this->warningThresholdQuantity + ); + + // THEN + expect($coupon->printRules)->toBeNull() + ->and($coupon->products)->toBe($this->products) + ->and($coupon->gs1)->toBe($this->gs1) + ->and($coupon->warningThresholdQuantity)->toBe($this->warningThresholdQuantity); + }); + + it('hydrates a requested coupon without gs1', function (): void { + // GIVEN / WHEN + $coupon = new RequestedCoupon( + name: $this->name, + quantity: $this->quantity, + diffusion: $this->diffusion, + moneyAmount: $this->moneyAmount, + processingCode: $this->processingCode, + couponPicture:$this->couponPicture, + couponValidity: $this->couponValidity, + couponValidityInformation: $this->couponValidityInformation, + products: $this->products, + printRules: $this->printRules, + warningThresholdQuantity: $this->warningThresholdQuantity + ); + + // THEN + expect($coupon->gs1)->toBeNull() + ->and($coupon->products)->toBe($this->products) + ->and($coupon->printRules)->toBe($this->printRules) + ->and($coupon->warningThresholdQuantity)->toBe($this->warningThresholdQuantity); + }); + + it('hydrates a requested coupon without warning threshold quantity', function (): void { + // GIVEN / WHEN + $coupon = new RequestedCoupon( + name: $this->name, + quantity: $this->quantity, + diffusion: $this->diffusion, + moneyAmount: $this->moneyAmount, + processingCode: $this->processingCode, + couponPicture:$this->couponPicture, + couponValidity: $this->couponValidity, + couponValidityInformation: $this->couponValidityInformation, + products: $this->products, + printRules: $this->printRules, + gs1: $this->gs1, + ); + + // THEN + expect($coupon->warningThresholdQuantity)->toBeNull() + ->and($coupon->products)->toBe($this->products) + ->and($coupon->printRules)->toBe($this->printRules) + ->and($coupon->gs1)->toBe($this->gs1); + }); + + it('hydrates a requested coupon with only required fields', function (): void { + // GIVEN / WHEN + $coupon = new RequestedCoupon( + name: $this->name, + quantity: $this->quantity, + diffusion: $this->diffusion, + moneyAmount: $this->moneyAmount, + processingCode: $this->processingCode, + couponPicture:$this->couponPicture, + couponValidity: $this->couponValidity, + couponValidityInformation: $this->couponValidityInformation, + ); + + // THEN + expect($coupon->products)->toBe([]) + ->and($coupon->printRules)->toBeNull() + ->and($coupon->gs1)->toBeNull() + ->and($coupon->warningThresholdQuantity)->toBeNull(); + }); + + it('accepts an empty validity information list', function (): void { + // GIVEN / WHEN + $coupon = new RequestedCoupon( + $this->name, + $this->quantity, + $this->diffusion, + $this->moneyAmount, + $this->processingCode, + $this->couponPicture, + $this->couponValidity, + [], + $this->products, + $this->printRules, + $this->gs1, + $this->warningThresholdQuantity + ); + + // THEN + expect($coupon->couponValidityInformation)->toBe([]); + }); + + it('throws when validity information contains an invalid item', function (): void { + // GIVEN / WHEN / THEN + expect(fn () => new RequestedCoupon( + $this->name, + $this->quantity, + $this->diffusion, + $this->moneyAmount, + $this->processingCode, + $this->couponPicture, + $this->couponValidity, + ['Not a CouponValidityInformation'], + $this->products, + $this->printRules, + $this->gs1, + $this->warningThresholdQuantity + ))->toThrow(\DomainException::class, 'Coupon validity information must be CouponValidityInformation instances.'); }); it('hydrates a requested coupon without a GS1', function (): void {