BSH Common-Doku

website·Indexed 30 days ago

https://docs.bluesafety.dev

Technical documentation for the common-api, a central API integration hub for Bluesafety applications. It covers integrations with Weclapp (Cloud-ERP), Grenke, and Mobile Field Report (MFR) using OData v4 and REST. The documentation details a four-layer architecture including the Action Pattern, Laravel Sanctum authentication, and specific service implementations such as WeclappTemplateRendererService and RequestFlowService.

Tokens
21.9K
Snippets
125
Records
146
Agent score
49%

What's inside BSH Common-Doku

  1. Overview of the common-api architectural layers

    The common-api follows a structured four-layer pattern to handle requests and external system integrations. This separation ensures that controllers remain thin and business logic is reusable and testable.
    LayerPurposeExample
    ControllerRouting, triggering validation, building responsesGrenke\RequestController::create
    FormRequestInput validationCreateGrenkeRequestRequest
    DTOTyped container for data translation (fromArray ↔ toArray)RequestPayloadData
    ActionA single atomic operation against an external systemCreateGrenkeRequestAction::execute()
    ServiceOrchestrates multiple Actions or holds helper logicRequestFlowService, RequestStateService
    ApiServiceThin HTTP client handling Auth and Error-HandlingGrenke\ApiService, WeclappApiService
  2. Overview of the Bluesafety common-api Integration Hub

    The common-api is a Laravel 12 application (PHP 8.2+) using Laravel Sanctum. It serves as a central hub connecting three external systems to the internal Common database:

    • Grenke: Equipment-leasing platform using X.509 certificates (PEM) for authentication.
    • Weclapp: Cloud-ERP system using API-Key (Bearer) authentication.
    • MFR (Mobile Field Report): Field service system using HTTP Basic authentication and OData.
  3. Overview of MFR Resource Controllers

    MFR entities are managed via resource controllers located in app/Http/Controllers/Mfr/. Each entity has a corresponding controller providing standard index, show, store, update, and destroy methods. All these controllers follow a common pattern based on the _ResourceController.php template.
  4. Grenke Integration Enums Overview

    The following typed enums are available in app/Enums/Grenke/ to avoid the use of magic strings:
    EnumValues
    RequestStateReadyToSign, MissingInfo, ContractPrinted, Cancelled, Declined, RequestToGrenke, RunningContract
    ProductTypeClassicLease, ...
    PaymentMethodDirectDebit, Invoice, ...
    PaymentFrequencyMonthly, Quarterly, Annually, ...
    ThirdPartyIdentifierTypeBased on identifier type
  5. Authenticate with the common-api using Laravel Sanctum

    The common-api uses Laravel Sanctum (Personal Access Tokens) for authentication. To access protected endpoints, you must first obtain a Bearer token via the login endpoint and include it in the Authorization header of subsequent requests.
  6. CommonDB Database Overview

    CommonDB serves as a BigData database used for merging and handling aggregated data from various sources. It currently synchronizes with BLUEJECTS and WDS-APP, and receives data (push) from DENTAL.SHOP.
  7. Grenke API DTO Architecture and Naming Conventions

    All data flowing between Controllers, Actions, Services, and the Grenke API is handled via readonly DTOs located in app/DataTransferObjects/Grenke/. These DTOs act as the translation layer between the external wire format and internal PHP logic: external data uses PascalCase, while internal PHP properties use camelCase.
  8. Inject WeclappMailTemplateService via Dependency Injection

    The WeclappMailTemplateService is used to load mail templates from Weclapp (via the mailTemplate/id/{id} endpoint) and provide them as typed MailTemplateData DTOs.
    use App\Services\WeclappMailTemplateService;
    
    public function __construct(
        protected WeclappMailTemplateService $weclappMailTemplateService
    ) {}
  9. Control bidirectional data flow with the $toMfr flag

    MFR controllers can write to both the local Common-DB and the MFR API. This behavior is controlled by the $toMfr boolean flag in the store, update, and destroy methods:

    • $toMfr = true (Default): Updates the MFR API via GuzzleClient first, then updates the local Common-DB.
    • $toMfr = false: Updates only the local database. This is typically used when a webhook (e.g., from Weclapp) triggers the update and MFR is the intended recipient.
    public function store(Request $request, bool $toMfr = true) { ... }
    public function update(Request $request, Companies $company, bool $toMfr = true) { ... }
    public function destroy(Companies $company, bool $toMfr = true) { ... }
  10. Implement a Grenke API DTO

    When creating a new DTO in app/DataTransferObjects/Grenke/<Name>Data.php, follow these structural requirements:

    1. Constructor: Use only public readonly properties.
    2. fromArray(array $data): Implement this static method to map PascalCase external keys to camelCase properties. Use defensive defaults (e.g., ?? null, ?? '', ?? 0) because the Grenke API may omit fields depending on the status.
    3. toArray(): Implement this method to convert the DTO back into the PascalCase wire format.
    4. Nesting: For complex objects, recursively call fromArray() and toArray() on nested DTOs.
    namespace App\DataTransferObjects\Grenke;
    
    class RequestPayloadData
    {
        public function __construct(
            public readonly string $financingId,
            public readonly string $productType,
            public readonly float  $financingAmount,
            public readonly LesseeData $lessee,
            public readonly array  $financingObjects,    // FinancingObjectData[]
        ) {}
    
        public static function fromArray(array $data): self
        {
            return new self(
                financingId: (string) ($data['FinancingId'] ?? ''),
                productType: (string) ($data['ProductType'] ?? ''),
                financingAmount: (float) ($data['FinancingAmount'] ?? 0),
                lessee: LesseeData::fromArray($data['Lessee'] ?? []),
                financingObjects: array_map(
                    fn (array $i) => FinancingObjectData::fromArray($i),
                    $data['FinancingObjects'] ?? []
                ),
            );
        }
    
        public function toArray(): array
        {
            return [
                'FinancingId'      => $this->financingId,
                'ProductType'      => $this->productType,
                'FinancingAmount'  => $this->financingAmount,
                'Lessee'           => $this->lessee->toArray(),
                'FinancingObjects' => array_map(
                    fn (FinancingObjectData $i) => $i->toArray(),
                    $this->financingObjects
                ),
            ];
        }
    }
  11. Render Weclapp templates using WeclappTemplateRendererService

    The App\Services\WeclappTemplateRendererService converts Weclapp mail template syntax into Laravel Blade and renders the final HTML. The rendering pipeline follows these steps:

    1. Normalizes line endings (\r\n or \r to \n).
    2. Converts [[if|ifnot|endif]] blocks to Blade @if/@endif directives.
    3. Converts ..varName.. placeholders to {{ $varName ?? "" }}.
    4. Applies nl2br() to preserve line breaks in HTML.
    5. Executes the final string via Blade::render().
    $html = $this->weclappTemplateRendererService->render(
        $template->body,
        [
            'companyName'       => 'Acme GmbH',
            'orderDate'         => '12.04.2026',
            'technicianAppointment'     => true,
            'technicianAppointmentDate' => '15.04.2026',
            'technicianAppointmentTime' => '10:00',
        ]
    );
  12. Inject and use WeclappApiService

    The App\Services\WeclappApiService is a wrapper around the internal App\Http\Api\Weclapp\ClientController. It handles Guzzle requests with the necessary AuthenticationToken headers and provides a simplified interface for REST verbs and custom attribute management.
    use App\Services\WeclappApiService;
    
    public function __construct(
        protected WeclappApiService $weclappApiService
    ) {}