Skip to content

SaleProductAvailabilityService Documentation

Overview

The SaleProductAvailabilityService is a custom service designed to determine product availability and delivery information for sale products. This service handles the complex logic where the "dispo" (availability date) can come from two different sources:

  1. Container arrival date: When a container is assigned to a product
  2. Product availability date: The product's inherent availability field

This service is based on the original Magento logic and adapted for the Symfony/Doctrine environment.

Location

  • Service File: src/AppBundle/Services/SaleProductAvailabilityService.php
  • Test File: tests/Services/SaleProductAvailabilityServiceTest.php
  • Service Registration: src/AppBundle/Resources/config/services.yml

Service Registration

The service is registered as mz.saleproductavailability in the services configuration:

mz.saleproductavailability:
  class: "%mz.saleproductavailability.class%"
  autowire: true
  calls:
    - [ setEntityManager, [ "@doctrine.orm.entity_manager" ] ]

Key Concepts

Dispo (Availability Date)

The "dispo" represents when a product will be available. It can come from multiple sources, checked in this priority order:

  1. Container Assignment (Highest Priority): If a SaleProduct has a container assigned, the container's arrivalAt date is used as the dispo
  2. Sale Product's Dispo Client Date: The dispoClientDate field from the SaleProduct entity
  3. Sale Product's Available Field: The available field from the SaleProduct entity
  4. Sale Product's Availability Field (Lowest Priority): The availability field from the SaleProduct entity

Important: The service uses the SaleProduct's own availability fields, NOT the base Product entity's fields.

Delivery Window Calculation

The service calculates delivery date ranges based on:

  • Whether the product is in stock (dispo < current date)
  • The store ID (some stores have different delivery windows)
  • The reference date (either current date or the dispo date)

Standard Delivery Windows: - In stock: 2-9 days from reference date - Out of stock: 2-9 days from dispo date

Special Store Delivery Windows (Store IDs 29, 19): - In stock: 10-17 days from current date - Out of stock: 10-17 days from dispo date

Usage Examples

1. Injecting the Service

// In a controller
$availabilityService = $this->get('mz.saleproductavailability');

// In a service with dependency injection
use AppBundle\Services\SaleProductAvailabilityService;

class MyService
{
    private $availabilityService;

    public function __construct(SaleProductAvailabilityService $availabilityService)
    {
        $this->availabilityService = $availabilityService;
    }
}

2. Getting Product Availability Date (Dispo)

use AppBundle\Entity\SaleProduct;

// Get a sale product
$saleProduct = $saleProductRepository->find($id);

// Get the dispo (availability date in Ymd format)
$dispo = $availabilityService->getProductDispo($saleProduct);
// Returns: '20251115' or null if not available

Logic (checked in priority order): 1. If the sale product has a container: Returns the container's arrivalAt date 2. If dispoClientDate is set: Returns the dispoClientDate in Ymd format 3. If available field is set: Returns the available value 4. If availability field is set: Returns the availability value 5. Otherwise: Returns null

Note: The service uses the SaleProduct's own availability fields (dispoClientDate, available, availability), NOT the base Product entity's fields.

3. Checking if Product is In Stock

// Check if product is currently in stock
$isInStock = $availabilityService->isInStock($saleProduct);
// Returns: true if dispo < current date, false otherwise

4. Getting Delivery Information

// Get delivery information string
$deliveryInfo = $availabilityService->getOrderDeliveryInfo($saleProduct);
// Returns: "Livraison entre le 15 et le 22 novembre"

5. Getting Calculated Availability Bracket

// Get availability information with bracket
$availability = $availabilityService->getCalculatedAvailability($saleProduct);

// Returns array:
// [
//     'dispo' => '20251115',
//     'date' => '2_semaines'  // Could be: En_Stock, 1_semaines, ..., 16_semaines, >_16_semaines
// ]

Possible date values: - En_Stock: Product is in stock (dispo < today) - 1_semaines to 16_semaines: Product available within 1-16 weeks - >_16_semaines: Product available in more than 16 weeks - Fin de produit: End of product lifecycle

6. Custom Delivery Information with Phrase

// Get delivery info with custom formatting
$customPhrase = 'Livraison entre le %s et le %s';
$deliveryInfo = $availabilityService->getDeliveryInfo(
    $dispo = '20251120',
    $customPhrase,
    $isInStock = false,
    $saleProduct = null
);
// Returns: "Livraison entre le 22/11 et le 29/11"

7. Getting Container Information

// Get container details if product is assigned to a container
$containerInfo = $availabilityService->getContainerInfo($saleProduct);

// Returns array if container exists:
// [
//     'reference' => 'CONT-001',
//     'status' => 'shipped',
//     'arrival_at' => DateTime object,
//     'arrived_at' => DateTime object or null,
//     'ship_name' => 'Test Ship'
// ]

// Returns null if no container assigned

Complete Example: Display Product Availability

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;

class ProductAvailabilityController extends Controller
{
    public function getAvailabilityAction($saleProductId)
    {
        // Get the sale product
        $saleProduct = $this->getDoctrine()
            ->getRepository('AppBundle:SaleProduct')
            ->find($saleProductId);

        if (!$saleProduct) {
            return new JsonResponse(['error' => 'Sale product not found'], 404);
        }

        // Get the availability service
        $availabilityService = $this->get('mz.saleproductavailability');

        // Get all availability information
        $dispo = $availabilityService->getProductDispo($saleProduct);
        $isInStock = $availabilityService->isInStock($saleProduct);
        $deliveryInfo = $availabilityService->getOrderDeliveryInfo($saleProduct);
        $calculatedAvailability = $availabilityService->getCalculatedAvailability($saleProduct);
        $containerInfo = $availabilityService->getContainerInfo($saleProduct);

        return new JsonResponse([
            'dispo' => $dispo,
            'is_in_stock' => $isInStock,
            'delivery_info' => $deliveryInfo,
            'availability_bracket' => $calculatedAvailability['date'],
            'container' => $containerInfo,
        ]);
    }
}

Example Response:

{
  "dispo": "20251115",
  "is_in_stock": false,
  "delivery_info": "Livraison entre le 17 et le 24 novembre",
  "availability_bracket": "2_semaines",
  "container": {
    "reference": "CONT-001",
    "status": "shipped",
    "arrival_at": "2025-11-15T00:00:00+00:00",
    "arrived_at": null,
    "ship_name": "MV Ocean Star"
  }
}

API Methods Reference

getProductDispo(SaleProduct $saleProduct): ?string

Returns the availability date (dispo) for a sale product.

Priority order: 1. Container's arrivalAt date (if container is assigned) 2. SaleProduct's dispoClientDate field 3. SaleProduct's available field 4. SaleProduct's availability field

Returns: Date in Ymd format (e.g., '20251115') or null


getOrderDeliveryInfo(SaleProduct $saleProduct): string

Returns delivery information message for a sale product.

Parameters: - $saleProduct: The sale product

Returns: Formatted delivery message (e.g., "Livraison entre le 15 et le 22 novembre"). The delivery window defaults to the sale's creation timestamp when available and otherwise uses the current date.


getDeliveryInfo(?string $dispo, ?string $customPhrase, bool $isInStock, ?SaleProduct $saleProduct, ?DateTime $referenceDate = null): string

Calculate detailed delivery date information.

Parameters: - $dispo: Availability date in Ymd format - $customPhrase: Custom formatting phrase (e.g., "Livraison entre le %s et le %s") - $isInStock: Whether product is in stock - $saleProduct: Sale product for additional context (store-specific delivery times) - $referenceDate: Optional reference date to anchor the delivery window when the product is already in stock (defaults to "now")

Returns: Formatted delivery information


isInStock(SaleProduct $saleProduct): bool

Check if a sale product is currently in stock.

Returns: true if in stock, false otherwise


getCalculatedAvailability(SaleProduct $saleProduct): array

Get availability bracket information.

Returns: Array with: - dispo: Raw availability value - date: Availability bracket (e.g., 'En_Stock', '2_semaines', etc.)


getContainerInfo(SaleProduct $saleProduct): ?array

Get container information if product is assigned to a container.

Returns: Container details array or null

Differences from Magento Implementation

  1. Container Integration: Uses Doctrine ORM relationships instead of Magento's entity system
  2. Service Registration: Uses Symfony's dependency injection instead of Magento's service locator
  3. Month Localization: Currently hardcoded to French month names (can be extended with Symfony's translator)
  4. Store-specific Logic: Simplified to check store ID directly from sale
  5. Availability Source: Uses SaleProduct's own availability fields (dispoClientDate, available, availability) instead of the base Product entity

Future Enhancements

  1. Localization: Add support for multiple languages using Symfony's translator
  2. Configuration: Move delivery day ranges to configuration files
  3. Event System: Dispatch events when availability changes
  4. Caching: Add caching layer for frequently accessed availability data
  5. Store Configuration: Move store-specific delivery times to database configuration

Testing

Run the test suite:

vendor/bin/phpunit tests/Services/SaleProductAvailabilityServiceTest.php

The test suite covers: - Dispo calculation with and without containers - In-stock detection - Delivery info formatting - Availability brackets - Container information retrieval - Store-specific delivery windows

  • SaleProduct: /home/user/logidav/src/AppBundle/Entity/SaleProduct.php
  • Product: /home/user/logidav/src/AppBundle/Entity/Product.php
  • Container: /home/user/logidav/src/AppBundle/Entity/Container.php
  • Sale: /home/user/logidav/src/AppBundle/Entity/Sale.php

Support

For questions or issues with this service, please refer to: - The test suite for usage examples - The Magento migration notes for original implementation details - The service source code for detailed inline documentation