appcc-digital/laravel-appcc

A fluent, type-safe PHP client for the APPCC Digital API

Maintainers

Package info

github.com/BorahLabs/laravel-appcc

pkg:composer/appcc-digital/laravel-appcc

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-03-10 10:54 UTC

This package is auto-updated.

Last update: 2026-03-10 10:59:01 UTC


README

A fluent, type-safe PHP client for the APPCC Digital API. Record food safety logs (temperature, cleaning, reception, etc.) against an APPCC Digital tenant from any Laravel application.

Requirements

  • PHP 8.2+
  • Laravel 11 or 12

Installation

composer require appcc-digital/laravel-appcc

Auto-discovery registers the service provider. Publish the config file:

php artisan vendor:publish --tag=appcc-config

Add these to your .env:

APPCC_URL=https://mi-restaurante.appcc.digital
APPCC_TOKEN=your-bearer-token
APPCC_TIMEOUT=30

The APPCC_URL is your tenant's subdomain URL — the SDK appends /api/v1 automatically.

Verify Connection

php artisan appcc:test-connection

Usage

All calls start from the Appcc facade:

use AppccDigital\LaravelAppcc\Facades\Appcc;

Temperature Logs

$result = Appcc::temperatureLogs()
    ->for($equipmentId)
    ->record([
        'temperature' => 3.5,
        'corrective_action' => null,
    ], photo: '/path/to/photo.jpg');

$result->successful();           // bool
$result->data()->id;             // string (ULID)
$result->data()->temperature;    // float
$result->data()->isWithinLimits; // bool

Cleaning Logs

$result = Appcc::cleaningLogs()->record([
    'zone_id' => $zoneId,
    'type' => 'daily',              // daily|preventive|verification
    'evaluation' => 'correct',      // clean|correct|incorrect|very_dirty
    'cleaning_date' => '2026-03-10',
    'observations' => null,
    'corrective_action' => null,    // required if incorrect/very_dirty
    'items' => [],                  // required if type=verification
]);

Incident Logs

$result = Appcc::incidentLogs()->record([
    'element_involved' => 'Horno industrial',
    'incident_description' => 'Fallo eléctrico en resistencia superior',
    'corrective_action' => 'Se reemplazó la resistencia',
    'incident_date' => '2026-03-10',
    'resolution_date' => '2026-03-11',
    'observations' => null,
]);

Vacuum Packaging Logs

$result = Appcc::vacuumPackagingLogs()->record([
    'product_temp_before' => 4.0,
    'seal_integrity_ok' => true,
    'labeled_correctly' => true,
    'product_id' => $productId,
    'lot_reference' => 'LOT-2026-001',
]);
// expiry_date is auto-calculated server-side

Reception Logs

$result = Appcc::receptionLogs()->record([
    'supplier_id' => $supplierId,
    'product_name' => 'Pollo fresco',
    'visual_evaluation' => 'ok',       // ok|rejected
    'temperature' => 2.5,
    'packaging_ok' => true,
    'labeling_ok' => true,
    'allergens_ok' => true,
    'rejection_reason' => null,        // required if rejected
    'corrective_action' => null,       // required if rejected
], photo: $request->file('photo'));

Blast Chiller Logs

Multi-step process:

// 1. Start cycle
$result = Appcc::blastChillerLogs()->record([
    'entry_temp' => 65.0,
    'product_id' => $productId,
    'equipment_id' => $equipmentId,
]);
$logId = $result->data()->id;

// 2. Record midpoint
$result = Appcc::blastChillerLogs()->for($logId)->update([
    'mid_temp' => 30.0,
]);

// 3. Record exit
$result = Appcc::blastChillerLogs()->for($logId)->update([
    'exit_temp' => 3.0,
    'corrective_action' => null, // required if out of limits
]);

Water Control Logs

$result = Appcc::waterControlLogs()->record([
    'sample_location' => 'Cocina principal',
    'chlorine_level' => 0.5,   // OK: 0.2–1.0 mg/L
    'ph_level' => 7.2,         // OK: 6.5–9.5
    'corrective_action' => null,
]);

Vegetable Disinfection Logs

$result = Appcc::vegetableDisinfectionLogs()->record([
    'product_name' => 'Lechuga',
    'start_time' => '10:00',
    'end_time' => '10:15',
    'chlorine_concentration_ppm' => 100,
]);

Sample Storage Logs

$result = Appcc::sampleStorageLogs()->record([
    'product_name' => 'Paella',
    'quantity_grams' => 150,
    'storage_temp' => -18.0,
    'container_type' => 'Bolsa hermética',
]);
// destruction_date is auto-calculated (sample_date + 7 days)

Result Object

All API calls return an AppccResult instance:

$result->successful();  // bool — true for 2xx
$result->failed();      // bool — true for non-2xx
$result->status();      // int — HTTP status code
$result->data();        // Typed DTO on success, null on failure
$result->errors();      // Validation errors on 422, empty otherwise
$result->message();     // Error message from API
$result->toArray();     // Raw response array

No exceptions are thrown — the caller always gets an AppccResult and decides how to handle failures.

Queued Dispatch

Fire-and-forget any API call via Laravel's queue system:

// Synchronous (default)
$result = Appcc::temperatureLogs()->for($eq)->record([...]);

// Queued
Appcc::temperatureLogs()->for($eq)->record([...])->dispatch();

// Custom queue
Appcc::temperatureLogs()->for($eq)->record([...])->dispatch(queue: 'sensors');

Configure the default queue in config/appcc.php or via APPCC_QUEUE env var.

File Uploads

Endpoints that accept photos (temperature-logs, reception-logs) support file uploads:

// File path
Appcc::temperatureLogs()->for($eq)->record($data, photo: '/tmp/photo.jpg');

// Laravel UploadedFile
Appcc::temperatureLogs()->for($eq)->record($data, photo: $request->file('photo'));

// Resource/stream
Appcc::temperatureLogs()->for($eq)->record($data, photo: fopen('/tmp/photo.jpg', 'r'));

The SDK automatically switches to multipart/form-data when a file is attached.

Testing

Faking the Client

use AppccDigital\LaravelAppcc\Facades\Appcc;

beforeEach(function () {
    Appcc::fake();
});

it('records temperature after sensor read', function () {
    // ... your application code ...

    Appcc::assertSent('temperature-logs', 1);
    Appcc::assertSentWith('temperature-logs', [
        'temperature' => 3.5,
    ]);
});

Assertions

Appcc::assertSent('temperature-logs', 2);      // called N times
Appcc::assertSent('cleaning-logs');             // at least once
Appcc::assertSentWith('temperature-logs', [...]);
Appcc::assertNotSent('incident-logs');
Appcc::assertNothingSent();
Appcc::assertNothingSentTo('incident-logs');

$requests = Appcc::recorded();                  // all requests
$requests = Appcc::recorded('temperature-logs'); // filtered

Custom Fake Responses

Appcc::fake([
    'temperature-logs' => Appcc::response([
        'id' => 'fake-id',
        'temperature' => 3.5,
        'is_within_limits' => true,
    ], 201),

    'incident-logs' => Appcc::response([
        'message' => 'Validation failed',
        'errors' => ['element_involved' => ['Required']],
    ], 422),
]);

Endpoints Reference

SDK Method HTTP API Endpoint
temperatureLogs()->for($eq)->record() POST /api/v1/temperature-logs/{equipment}
cleaningLogs()->record() POST /api/v1/cleaning-logs
incidentLogs()->record() POST /api/v1/incident-logs
vacuumPackagingLogs()->record() POST /api/v1/vacuum-packaging-logs
receptionLogs()->record() POST /api/v1/reception-logs
blastChillerLogs()->record() POST /api/v1/blast-chiller-logs
blastChillerLogs()->for($id)->update() PATCH /api/v1/blast-chiller-logs/{log}
waterControlLogs()->record() POST /api/v1/water-control-logs
vegetableDisinfectionLogs()->record() POST /api/v1/vegetable-disinfection-logs
sampleStorageLogs()->record() POST /api/v1/sample-storage-logs

License

MIT