first commit
This commit is contained in:
101
laravel/app/Console/Commands/SyncAllCommand.php
Normal file
101
laravel/app/Console/Commands/SyncAllCommand.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\SyncStatus;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Enums\WbEndpoint;
|
||||
use App\Models\SyncState;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
final class SyncAllCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:all';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->syncEndpoint(WbEndpoint::ORDERS, 'sync:orders');
|
||||
$this->syncEndpoint(WbEndpoint::SALES, 'sync:sales');
|
||||
$this->syncEndpoint(WbEndpoint::INCOMES, 'sync:incomes');
|
||||
|
||||
$this->syncStocks();
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function syncEndpoint(WbEndpoint $endpoint, string $command): void
|
||||
{
|
||||
$yesterday = Carbon::yesterday();
|
||||
|
||||
$lastSuccess = SyncState::query()
|
||||
->where('entity', $endpoint->name)
|
||||
->where('status', SyncStatus::SUCCESS)
|
||||
->orderByDesc('date_to')
|
||||
->first();
|
||||
|
||||
if (!$lastSuccess) {
|
||||
$dateFrom = Carbon::now()->subYears(2);
|
||||
$dateTo = $yesterday;
|
||||
} else {
|
||||
$dateFrom = Carbon::parse($lastSuccess->date_to);
|
||||
$dateTo = $dateFrom->copy()->addDay();
|
||||
|
||||
if ($dateTo->gt($yesterday)) {
|
||||
$dateTo = $yesterday->copy();
|
||||
}
|
||||
}
|
||||
|
||||
if ($dateFrom->gte($yesterday)) {
|
||||
$this->info("$endpoint->name already up-to-date");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Syncing $endpoint->name from {$dateFrom->toDateString()} to {$dateTo->toDateString()}");
|
||||
|
||||
Artisan::call($command, [
|
||||
'--dateFrom' => $dateFrom->toDateString(),
|
||||
'--dateTo' => $dateTo->toDateString(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
private function syncStocks(): void
|
||||
{
|
||||
$today = Carbon::today()->toDateString();
|
||||
|
||||
$lastSuccess = SyncState::query()
|
||||
->where('entity', WbEndpoint::STOCKS->name)
|
||||
->where('status', SyncStatus::SUCCESS)
|
||||
->where('date_from', $today)
|
||||
->first();
|
||||
|
||||
if ($lastSuccess) {
|
||||
$this->info("STOCKS already synced for today");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Syncing STOCKS for today");
|
||||
|
||||
Artisan::call('sync:stocks', [
|
||||
'--date' => $today,
|
||||
]);
|
||||
}
|
||||
}
|
||||
45
laravel/app/Console/Commands/SyncIncomesCommand.php
Normal file
45
laravel/app/Console/Commands/SyncIncomesCommand.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\Enums\WbEndpoint;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
final class SyncIncomesCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:incomes {--dateFrom=} {--dateTo=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sync incomes from WB API';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(WbSyncManagerInterface $manager): int
|
||||
{
|
||||
$dateFrom = $this->option('dateFrom');
|
||||
$dateTo = $this->option('dateTo');
|
||||
|
||||
$dateFrom = $dateFrom ?: Carbon::now()->subYear()->toDateString();
|
||||
$dateTo = $dateTo ?: Carbon::yesterday()->toDateString();
|
||||
|
||||
$manager->start(WbEndpoint::INCOMES, $dateFrom, $dateTo);
|
||||
|
||||
$this->info('Sync started');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
46
laravel/app/Console/Commands/SyncOrdersCommand.php
Normal file
46
laravel/app/Console/Commands/SyncOrdersCommand.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\Enums\WbEndpoint;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
final class SyncOrdersCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:orders {--dateFrom=} {--dateTo=}';
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sync orders from API';
|
||||
|
||||
public function handle(WbSyncManagerInterface $manager): int
|
||||
{
|
||||
$dateFrom = $this->option('dateFrom')
|
||||
?: Carbon::now()->subYears(2)->toDateString();
|
||||
|
||||
$dateTo = $this->option('dateTo')
|
||||
?: Carbon::yesterday()->toDateString();
|
||||
|
||||
if ($dateFrom >= $dateTo) {
|
||||
$this->info('Nothing to sync');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info("Syncing ORDERS from $dateFrom to $dateTo");
|
||||
|
||||
$manager->start(WbEndpoint::ORDERS, $dateFrom, $dateTo);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
45
laravel/app/Console/Commands/SyncSalesCommand.php
Normal file
45
laravel/app/Console/Commands/SyncSalesCommand.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\Enums\WbEndpoint;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
final class SyncSalesCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:sales {--dateFrom=} {--dateTo=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sync sales from WB API';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(WbSyncManagerInterface $manager): int
|
||||
{
|
||||
$dateFrom = $this->option('dateFrom');
|
||||
$dateTo = $this->option('dateTo');
|
||||
|
||||
$dateFrom = $dateFrom ?: Carbon::now()->subYear()->toDateString();
|
||||
$dateTo = $dateTo ?: Carbon::yesterday()->toDateString();
|
||||
|
||||
$manager->start(WbEndpoint::SALES, $dateFrom, $dateTo);
|
||||
|
||||
$this->info('Sales sync started');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
40
laravel/app/Console/Commands/SyncStocksCommand.php
Normal file
40
laravel/app/Console/Commands/SyncStocksCommand.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\Enums\WbEndpoint;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
final class SyncStocksCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:stocks {--date=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sync stocks from WB test API';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(WbSyncManagerInterface $manager): int
|
||||
{
|
||||
$date = (string)($this->option('date') ?? now()->toDateString());
|
||||
|
||||
$manager->start(WbEndpoint::STOCKS, $date);
|
||||
|
||||
$this->info('Sync started');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
17
laravel/app/Contracts/WbClientInterface.php
Normal file
17
laravel/app/Contracts/WbClientInterface.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Exceptions\WbServiceException;
|
||||
|
||||
interface WbClientInterface
|
||||
{
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function fetch(WbRequestDTO $requestDTO): array;
|
||||
}
|
||||
12
laravel/app/Contracts/WbSyncFactoryInterface.php
Normal file
12
laravel/app/Contracts/WbSyncFactoryInterface.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use App\Enums\WbEndpoint;
|
||||
|
||||
interface WbSyncFactoryInterface
|
||||
{
|
||||
public function make(WbEndpoint $type): WbSyncInterface;
|
||||
}
|
||||
14
laravel/app/Contracts/WbSyncInterface.php
Normal file
14
laravel/app/Contracts/WbSyncInterface.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use App\DTO\WbRequestDTO;
|
||||
|
||||
interface WbSyncInterface
|
||||
{
|
||||
public function sync(WbRequestDTO $dto): array;
|
||||
|
||||
public function save(array $data): void;
|
||||
}
|
||||
41
laravel/app/Contracts/WbSyncManagerInterface.php
Normal file
41
laravel/app/Contracts/WbSyncManagerInterface.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use App\Enums\WbEndpoint;
|
||||
|
||||
interface WbSyncManagerInterface
|
||||
{
|
||||
public function start(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo = null
|
||||
): void;
|
||||
|
||||
public function handleFirstPage(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo,
|
||||
int $lastPage
|
||||
): void;
|
||||
|
||||
public function incrementProgress(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): void;
|
||||
|
||||
public function markSuccess(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): void;
|
||||
|
||||
public function markFailed(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): void;
|
||||
}
|
||||
19
laravel/app/DTO/WbRequestDTO.php
Normal file
19
laravel/app/DTO/WbRequestDTO.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\DTO;
|
||||
|
||||
use App\Enums\WbEndpoint;
|
||||
|
||||
final readonly class WbRequestDTO
|
||||
{
|
||||
public function __construct(
|
||||
public WbEndpoint $endpoint,
|
||||
public string $dateFrom,
|
||||
public ?string $dateTo,
|
||||
public int $page = 1,
|
||||
public int $limit = 500,
|
||||
) {
|
||||
}
|
||||
}
|
||||
12
laravel/app/Enums/SyncStatus.php
Normal file
12
laravel/app/Enums/SyncStatus.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SyncStatus: string
|
||||
{
|
||||
case PENDING = 'pending';
|
||||
case SUCCESS = 'success';
|
||||
case FAILED = 'failed';
|
||||
}
|
||||
23
laravel/app/Enums/WbEndpoint.php
Normal file
23
laravel/app/Enums/WbEndpoint.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum WbEndpoint: string
|
||||
{
|
||||
case STOCKS = '/api/stocks';
|
||||
case SALES = '/api/sales';
|
||||
case ORDERS = '/api/orders';
|
||||
case INCOMES = '/api/incomes';
|
||||
|
||||
public function url(): string
|
||||
{
|
||||
return config('wb-sync.url') . $this->value;
|
||||
}
|
||||
|
||||
public function key(): string
|
||||
{
|
||||
return config('wb-sync.key');
|
||||
}
|
||||
}
|
||||
12
laravel/app/Exceptions/WbServiceException.php
Normal file
12
laravel/app/Exceptions/WbServiceException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class WbServiceException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
8
laravel/app/Http/Controllers/Controller.php
Normal file
8
laravel/app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
69
laravel/app/Jobs/WbSyncJob.php
Normal file
69
laravel/app/Jobs/WbSyncJob.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Contracts\WbSyncFactoryInterface;
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Enums\WbEndpoint;
|
||||
use Illuminate\Bus\Batchable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Log;
|
||||
|
||||
final class WbSyncJob implements ShouldQueue
|
||||
{
|
||||
use Queueable, Batchable;
|
||||
|
||||
public int $tries = 3;
|
||||
public array $backoff = [10, 30, 60];
|
||||
|
||||
public function __construct(
|
||||
private readonly WbEndpoint $endpoint,
|
||||
private readonly string $dateFrom,
|
||||
private readonly ?string $dateTo,
|
||||
private readonly int $page,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(
|
||||
WbSyncFactoryInterface $factory,
|
||||
WbSyncManagerInterface $manager
|
||||
): void {
|
||||
$service = $factory->make($this->endpoint);
|
||||
|
||||
$dto = new WbRequestDTO(
|
||||
endpoint: $this->endpoint,
|
||||
dateFrom: $this->dateFrom,
|
||||
dateTo: $this->dateTo,
|
||||
page: $this->page,
|
||||
);
|
||||
|
||||
$response = $service->sync($dto);
|
||||
|
||||
$service->save($response['data']);
|
||||
|
||||
if ($this->page === 1) {
|
||||
$lastPage = (int)data_get($response, 'meta.last_page', 1);
|
||||
$manager->handleFirstPage(
|
||||
$this->endpoint,
|
||||
$this->dateFrom,
|
||||
$this->dateTo,
|
||||
$lastPage,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
Log::info(
|
||||
"WB sync page $this->page for {$this->endpoint->name} date form: $this->dateFrom, to: $this->dateTo processed"
|
||||
);
|
||||
|
||||
$manager->incrementProgress(
|
||||
$this->endpoint,
|
||||
$this->dateFrom,
|
||||
$this->dateTo
|
||||
);
|
||||
}
|
||||
}
|
||||
70
laravel/app/Models/Income.php
Normal file
70
laravel/app/Models/Income.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $income_id
|
||||
* @property string|null $number
|
||||
* @property \Illuminate\Support\Carbon $date
|
||||
* @property \Illuminate\Support\Carbon|null $last_change_date
|
||||
* @property string|null $supplier_article
|
||||
* @property string|null $tech_size
|
||||
* @property int|null $barcode
|
||||
* @property int $quantity
|
||||
* @property string|null $total_price
|
||||
* @property \Illuminate\Support\Carbon|null $date_close
|
||||
* @property string|null $warehouse_name
|
||||
* @property int|null $nm_id
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereBarcode($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereDateClose($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereIncomeId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereLastChangeDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereNmId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereNumber($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereQuantity($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereSupplierArticle($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereTechSize($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereTotalPrice($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Income whereWarehouseName($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Income extends Model
|
||||
{
|
||||
protected $table = 'incomes';
|
||||
|
||||
protected $fillable = [
|
||||
'income_id',
|
||||
'number',
|
||||
'date',
|
||||
'last_change_date',
|
||||
'supplier_article',
|
||||
'tech_size',
|
||||
'barcode',
|
||||
'quantity',
|
||||
'total_price',
|
||||
'date_close',
|
||||
'warehouse_name',
|
||||
'nm_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date',
|
||||
'last_change_date' => 'date',
|
||||
'date_close' => 'date',
|
||||
];
|
||||
}
|
||||
}
|
||||
77
laravel/app/Models/Order.php
Normal file
77
laravel/app/Models/Order.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $g_number
|
||||
* @property string $date
|
||||
* @property string|null $last_change_date
|
||||
* @property string|null $supplier_article
|
||||
* @property string|null $tech_size
|
||||
* @property int|null $barcode
|
||||
* @property string|null $total_price
|
||||
* @property int|null $discount_percent
|
||||
* @property string|null $warehouse_name
|
||||
* @property string|null $oblast
|
||||
* @property int|null $income_id
|
||||
* @property int|null $odid
|
||||
* @property int|null $nm_id
|
||||
* @property string|null $subject
|
||||
* @property string|null $category
|
||||
* @property string|null $brand
|
||||
* @property int $is_cancel
|
||||
* @property string|null $cancel_dt
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereBarcode($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereBrand($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereCancelDt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereCategory($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereDiscountPercent($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereGNumber($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereIncomeId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereIsCancel($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereLastChangeDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereNmId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereOblast($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereOdid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereSubject($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereSupplierArticle($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereTechSize($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereTotalPrice($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Order whereWarehouseName($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Order extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'g_number',
|
||||
'date',
|
||||
'last_change_date',
|
||||
'supplier_article',
|
||||
'tech_size',
|
||||
'barcode',
|
||||
'total_price',
|
||||
'discount_percent',
|
||||
'warehouse_name',
|
||||
'oblast',
|
||||
'income_id',
|
||||
'odid',
|
||||
'nm_id',
|
||||
'subject',
|
||||
'category',
|
||||
'brand',
|
||||
'is_cancel',
|
||||
'cancel_dt',
|
||||
];
|
||||
}
|
||||
117
laravel/app/Models/Sale.php
Normal file
117
laravel/app/Models/Sale.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $g_number
|
||||
* @property \Illuminate\Support\Carbon $date
|
||||
* @property \Illuminate\Support\Carbon|null $last_change_date
|
||||
* @property string|null $supplier_article
|
||||
* @property string|null $tech_size
|
||||
* @property int|null $barcode
|
||||
* @property string|null $total_price
|
||||
* @property int|null $discount_percent
|
||||
* @property bool $is_supply
|
||||
* @property bool $is_realization
|
||||
* @property string|null $promo_code_discount
|
||||
* @property string|null $warehouse_name
|
||||
* @property string|null $country_name
|
||||
* @property string|null $oblast_okrug_name
|
||||
* @property string|null $region_name
|
||||
* @property int|null $income_id
|
||||
* @property string|null $sale_id
|
||||
* @property int|null $odid
|
||||
* @property string|null $spp
|
||||
* @property numeric|null $for_pay
|
||||
* @property numeric|null $finished_price
|
||||
* @property numeric|null $price_with_disc
|
||||
* @property int|null $nm_id
|
||||
* @property string|null $subject
|
||||
* @property string|null $category
|
||||
* @property string|null $brand
|
||||
* @property bool|null $is_storno
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereBarcode($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereBrand($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereCategory($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereCountryName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereDiscountPercent($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereFinishedPrice($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereForPay($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereGNumber($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereIncomeId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereIsRealization($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereIsStorno($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereIsSupply($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereLastChangeDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereNmId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereOblastOkrugName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereOdid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale wherePriceWithDisc($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale wherePromoCodeDiscount($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereRegionName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereSaleId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereSpp($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereSubject($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereSupplierArticle($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereTechSize($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereTotalPrice($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Sale whereWarehouseName($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Sale extends Model
|
||||
{
|
||||
protected $table = 'sales';
|
||||
|
||||
protected $fillable = [
|
||||
'g_number',
|
||||
'date',
|
||||
'last_change_date',
|
||||
'supplier_article',
|
||||
'tech_size',
|
||||
'barcode',
|
||||
'total_price',
|
||||
'discount_percent',
|
||||
'is_supply',
|
||||
'is_realization',
|
||||
'promo_code_discount',
|
||||
'warehouse_name',
|
||||
'country_name',
|
||||
'oblast_okrug_name',
|
||||
'region_name',
|
||||
'income_id',
|
||||
'sale_id',
|
||||
'odid',
|
||||
'spp',
|
||||
'for_pay',
|
||||
'finished_price',
|
||||
'price_with_disc',
|
||||
'nm_id',
|
||||
'subject',
|
||||
'category',
|
||||
'brand',
|
||||
'is_storno',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date',
|
||||
'last_change_date' => 'date',
|
||||
'is_supply' => 'boolean',
|
||||
'is_realization' => 'boolean',
|
||||
'is_storno' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
97
laravel/app/Models/Stock.php
Normal file
97
laravel/app/Models/Stock.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property \Illuminate\Support\Carbon $date
|
||||
* @property \Illuminate\Support\Carbon|null $last_change_date
|
||||
* @property string|null $supplier_article
|
||||
* @property string|null $tech_size
|
||||
* @property string|null $barcode
|
||||
* @property int $quantity
|
||||
* @property bool|null $is_supply
|
||||
* @property bool|null $is_realization
|
||||
* @property int|null $quantity_full
|
||||
* @property string|null $warehouse_name
|
||||
* @property int|null $in_way_to_client
|
||||
* @property int|null $in_way_from_client
|
||||
* @property int|null $nm_id
|
||||
* @property string|null $subject
|
||||
* @property string|null $category
|
||||
* @property string|null $brand
|
||||
* @property string|null $sc_code
|
||||
* @property string|null $price
|
||||
* @property string|null $discount
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereBarcode($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereBrand($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereCategory($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereDiscount($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereInWayFromClient($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereInWayToClient($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereIsRealization($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereIsSupply($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereLastChangeDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereNmId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock wherePrice($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereQuantity($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereQuantityFull($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereScCode($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereSubject($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereSupplierArticle($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereTechSize($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|Stock whereWarehouseName($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Stock extends Model
|
||||
{
|
||||
protected $table = 'stocks';
|
||||
|
||||
protected $fillable = [
|
||||
'date',
|
||||
'last_change_date',
|
||||
'supplier_article',
|
||||
'tech_size',
|
||||
'barcode',
|
||||
'quantity',
|
||||
'is_supply',
|
||||
'is_realization',
|
||||
'quantity_full',
|
||||
'warehouse_name',
|
||||
'in_way_to_client',
|
||||
'in_way_from_client',
|
||||
'nm_id',
|
||||
'subject',
|
||||
'category',
|
||||
'brand',
|
||||
'sc_code',
|
||||
'price',
|
||||
'discount',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date',
|
||||
'last_change_date' => 'date',
|
||||
'is_supply' => 'boolean',
|
||||
'is_realization' => 'boolean',
|
||||
'quantity' => 'integer',
|
||||
'quantity_full' => 'integer',
|
||||
'in_way_to_client' => 'integer',
|
||||
'in_way_from_client' => 'integer',
|
||||
'nm_id' => 'integer',
|
||||
];
|
||||
}
|
||||
}
|
||||
66
laravel/app/Models/SyncState.php
Normal file
66
laravel/app/Models/SyncState.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SyncStatus;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $entity
|
||||
* @property \Illuminate\Support\Carbon|null $started_at
|
||||
* @property \Illuminate\Support\Carbon $date_from
|
||||
* @property \Illuminate\Support\Carbon|null $date_to
|
||||
* @property SyncStatus $status
|
||||
* @property string|null $batch_id
|
||||
* @property int|null $total_pages
|
||||
* @property int $processed_pages
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereBatchId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereDateFrom($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereDateTo($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereEntity($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereProcessedPages($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereStartedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereStatus($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereTotalPages($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SyncState whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class SyncState extends Model
|
||||
{
|
||||
protected $table = 'sync_states';
|
||||
protected $fillable = [
|
||||
'entity',
|
||||
'date_from',
|
||||
'date_to',
|
||||
'status',
|
||||
'batch_id',
|
||||
'total_pages',
|
||||
'processed_pages',
|
||||
'started_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'date_from' => 'date',
|
||||
'date_to' => 'date',
|
||||
'started_at' => 'datetime',
|
||||
'status' => SyncStatus::class,
|
||||
'total_pages' => 'integer',
|
||||
'processed_pages' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
public function incrementProcessedPages(): void
|
||||
{
|
||||
$this->increment('processed_pages');
|
||||
}
|
||||
}
|
||||
73
laravel/app/Models/User.php
Normal file
73
laravel/app/Models/User.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $email
|
||||
* @property \Illuminate\Support\Carbon|null $email_verified_at
|
||||
* @property string $password
|
||||
* @property string|null $remember_token
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
|
||||
* @property-read int|null $notifications_count
|
||||
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
49
laravel/app/Providers/AppServiceProvider.php
Normal file
49
laravel/app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Contracts\WbClientInterface;
|
||||
use App\Contracts\WbSyncFactoryInterface;
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\Services\WbService\WbClient;
|
||||
use App\Services\WbService\WbSyncFactory;
|
||||
use App\Services\WbService\WbSyncManager;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->registerSyncServices();
|
||||
$this->app->bind(WbSyncManagerInterface::class, WbSyncManager::class);
|
||||
$this->app->bind(WbClientInterface::class, WbClient::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
private function registerSyncServices(): void
|
||||
{
|
||||
$syncServices = config('wb-sync.services');
|
||||
|
||||
foreach ($syncServices as $serviceClass) {
|
||||
$this->app->singleton($serviceClass);
|
||||
}
|
||||
|
||||
$this->app->singleton(WbSyncFactoryInterface::class, function ($app) use ($syncServices) {
|
||||
$services = array_map(function ($serviceClass) use ($app) {
|
||||
return $app->make($serviceClass);
|
||||
}, $syncServices);
|
||||
|
||||
return new WbSyncFactory($services);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService\SyncServices;
|
||||
|
||||
use App\Contracts\WbClientInterface;
|
||||
use App\Contracts\WbSyncInterface;
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Exceptions\WbServiceException;
|
||||
use App\Models\Income;
|
||||
|
||||
final readonly class IncomesSyncService implements WbSyncInterface
|
||||
{
|
||||
public function __construct(
|
||||
private WbClientInterface $client,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function sync(WbRequestDTO $dto): array
|
||||
{
|
||||
return $this->client->fetch($dto);
|
||||
}
|
||||
|
||||
public function save(array $data): void
|
||||
{
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($data)
|
||||
->chunk(500)
|
||||
->each(function ($chunk) {
|
||||
$rows = $chunk->map(function (array $row) {
|
||||
return [
|
||||
'income_id' => $row['income_id'],
|
||||
'number' => $row['number'],
|
||||
'date' => $row['date'],
|
||||
'last_change_date' => $row['last_change_date'],
|
||||
'supplier_article' => $row['supplier_article'],
|
||||
'tech_size' => $row['tech_size'],
|
||||
'barcode' => (string)$row['barcode'],
|
||||
'quantity' => $row['quantity'],
|
||||
'total_price' => $row['total_price'],
|
||||
'date_close' => $row['date_close'],
|
||||
'warehouse_name' => $row['warehouse_name'],
|
||||
'nm_id' => $row['nm_id'],
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
Income::insert($rows);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService\SyncServices;
|
||||
|
||||
use App\Contracts\WbClientInterface;
|
||||
use App\Contracts\WbSyncInterface;
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Exceptions\WbServiceException;
|
||||
use App\Models\Order;
|
||||
|
||||
final readonly class OrdersSyncService implements WbSyncInterface
|
||||
{
|
||||
public function __construct(
|
||||
private WbClientInterface $client,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function sync(WbRequestDTO $dto): array
|
||||
{
|
||||
return $this->client->fetch($dto);
|
||||
}
|
||||
|
||||
public function save(array $data): void
|
||||
{
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($data)
|
||||
->chunk(500)
|
||||
->each(function ($chunk) {
|
||||
$rows = $chunk->map(function (array $row) {
|
||||
return [
|
||||
'g_number' => $row['g_number'],
|
||||
'date' => !empty($row['date']) ? $row['date'] : now(),
|
||||
'last_change_date' => $row['last_change_date'],
|
||||
'supplier_article' => $row['supplier_article'],
|
||||
'tech_size' => $row['tech_size'],
|
||||
'barcode' => (int)$row['barcode'],
|
||||
'total_price' => $row['total_price'],
|
||||
'discount_percent' => $row['discount_percent'],
|
||||
'warehouse_name' => $row['warehouse_name'],
|
||||
'oblast' => $row['oblast'],
|
||||
'income_id' => $row['income_id'],
|
||||
'odid' => (int)$row['odid'],
|
||||
'nm_id' => $row['nm_id'],
|
||||
'subject' => $row['subject'],
|
||||
'category' => $row['category'],
|
||||
'brand' => $row['brand'],
|
||||
'is_cancel' => $row['is_cancel'],
|
||||
'cancel_dt' => $row['cancel_dt'],
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
try {
|
||||
Order::insert($rows);
|
||||
} catch (\Throwable $e) {
|
||||
\Log::error('Insert failed', [
|
||||
'error' => $e->getMessage(),
|
||||
'row_sample' => $rows[0] ?? null,
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService\SyncServices;
|
||||
|
||||
use App\Contracts\WbClientInterface;
|
||||
use App\Contracts\WbSyncInterface;
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Exceptions\WbServiceException;
|
||||
use App\Models\Sale;
|
||||
|
||||
final readonly class SalesSyncService implements WbSyncInterface
|
||||
{
|
||||
public function __construct(
|
||||
private WbClientInterface $client,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function sync(WbRequestDTO $dto): array
|
||||
{
|
||||
return $this->client->fetch($dto);
|
||||
}
|
||||
|
||||
public function save(array $data): void
|
||||
{
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($data)
|
||||
->chunk(500)
|
||||
->each(function ($chunk) {
|
||||
$rows = $chunk->map(function (array $row) {
|
||||
return [
|
||||
'g_number' => $row['g_number'],
|
||||
'date' => $row['date'],
|
||||
'last_change_date' => $row['last_change_date'],
|
||||
'supplier_article' => $row['supplier_article'],
|
||||
'tech_size' => $row['tech_size'],
|
||||
'barcode' => (string)$row['barcode'],
|
||||
'total_price' => $row['total_price'],
|
||||
'discount_percent' => $row['discount_percent'],
|
||||
'is_supply' => $row['is_supply'],
|
||||
'is_realization' => $row['is_realization'],
|
||||
'promo_code_discount' => $row['promo_code_discount'],
|
||||
'warehouse_name' => $row['warehouse_name'],
|
||||
'country_name' => $row['country_name'],
|
||||
'oblast_okrug_name' => $row['oblast_okrug_name'],
|
||||
'region_name' => $row['region_name'],
|
||||
'income_id' => $row['income_id'],
|
||||
'sale_id' => $row['sale_id'],
|
||||
'odid' => $row['odid'],
|
||||
'spp' => $row['spp'],
|
||||
'for_pay' => $row['for_pay'],
|
||||
'finished_price' => $row['finished_price'],
|
||||
'price_with_disc' => $row['price_with_disc'],
|
||||
'nm_id' => $row['nm_id'],
|
||||
'subject' => $row['subject'],
|
||||
'category' => $row['category'],
|
||||
'brand' => $row['brand'],
|
||||
'is_storno' => $row['is_storno'],
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
Sale::insert($rows);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService\SyncServices;
|
||||
|
||||
use App\Contracts\WbClientInterface;
|
||||
use App\Contracts\WbSyncInterface;
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Exceptions\WbServiceException;
|
||||
use App\Models\Stock;
|
||||
|
||||
final readonly class StocksSyncService implements WbSyncInterface
|
||||
{
|
||||
public function __construct(
|
||||
private WbClientInterface $client,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function sync(WbRequestDTO $dto): array
|
||||
{
|
||||
return $this->client->fetch($dto);
|
||||
}
|
||||
|
||||
public function save(array $data): void
|
||||
{
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($data)
|
||||
->chunk(500)
|
||||
->each(function ($chunk) {
|
||||
$rows = $chunk->map(function (array $row) {
|
||||
return [
|
||||
'date' => $row['date'],
|
||||
'last_change_date' => $row['last_change_date'],
|
||||
'supplier_article' => $row['supplier_article'],
|
||||
'tech_size' => $row['tech_size'],
|
||||
'barcode' => (string)$row['barcode'],
|
||||
'quantity' => $row['quantity'],
|
||||
'is_supply' => $row['is_supply'],
|
||||
'is_realization' => $row['is_realization'],
|
||||
'quantity_full' => $row['quantity_full'],
|
||||
'warehouse_name' => $row['warehouse_name'],
|
||||
'in_way_to_client' => $row['in_way_to_client'],
|
||||
'in_way_from_client' => $row['in_way_from_client'],
|
||||
'nm_id' => $row['nm_id'],
|
||||
'subject' => $row['subject'],
|
||||
'category' => $row['category'],
|
||||
'brand' => $row['brand'],
|
||||
'sc_code' => (string)$row['sc_code'],
|
||||
'price' => $row['price'],
|
||||
'discount' => $row['discount'],
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
Stock::insert($rows);// todo upsert
|
||||
});
|
||||
}
|
||||
}
|
||||
50
laravel/app/Services/WbService/WbClient.php
Normal file
50
laravel/app/Services/WbService/WbClient.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService;
|
||||
|
||||
use App\Contracts\WbClientInterface;
|
||||
use App\DTO\WbRequestDTO;
|
||||
use App\Exceptions\WbServiceException;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Throwable;
|
||||
|
||||
final class WbClient implements WbClientInterface
|
||||
{
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function fetch(WbRequestDTO $requestDTO): array
|
||||
{
|
||||
$query = [
|
||||
'dateFrom' => $requestDTO->dateFrom,
|
||||
'page' => $requestDTO->page,
|
||||
'limit' => $requestDTO->limit,
|
||||
'key' => $requestDTO->endpoint->key(),
|
||||
];
|
||||
|
||||
if ($requestDTO->dateTo !== null) {
|
||||
$query['dateTo'] = $requestDTO->dateTo;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::timeout(30)
|
||||
->retry(3, 500)
|
||||
->acceptJson()
|
||||
->get(
|
||||
$requestDTO->endpoint->url(),
|
||||
$query
|
||||
);
|
||||
|
||||
$response->throw();
|
||||
} catch (Throwable $e) {
|
||||
throw new WbServiceException(
|
||||
'WB API request failed: ' . $e->getMessage(),
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
}
|
||||
28
laravel/app/Services/WbService/WbSyncFactory.php
Normal file
28
laravel/app/Services/WbService/WbSyncFactory.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService;
|
||||
|
||||
use App\Contracts\WbSyncFactoryInterface;
|
||||
use App\Contracts\WbSyncInterface;
|
||||
use App\Enums\WbEndpoint;
|
||||
use App\Exceptions\WbServiceException;
|
||||
|
||||
final readonly class WbSyncFactory implements WbSyncFactoryInterface
|
||||
{
|
||||
public function __construct(
|
||||
/** @var array<string, WbSyncInterface> */
|
||||
private array $services
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WbServiceException
|
||||
*/
|
||||
public function make(WbEndpoint $type): WbSyncInterface
|
||||
{
|
||||
return $this->services[$type->name]
|
||||
?? throw new WbServiceException("unknown sync type $type->name");
|
||||
}
|
||||
}
|
||||
213
laravel/app/Services/WbService/WbSyncManager.php
Normal file
213
laravel/app/Services/WbService/WbSyncManager.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WbService;
|
||||
|
||||
use App\Contracts\WbSyncManagerInterface;
|
||||
use App\Enums\SyncStatus;
|
||||
use App\Enums\WbEndpoint;
|
||||
use App\Jobs\WbSyncJob;
|
||||
use App\Models\SyncState;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
|
||||
final readonly class WbSyncManager implements WbSyncManagerInterface
|
||||
{
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function start(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo = null
|
||||
): void {
|
||||
$now = now();
|
||||
|
||||
DB::transaction(function () use ($endpoint, $dateFrom, $dateTo, $now) {
|
||||
$sync = SyncState::query()
|
||||
->where('entity', $endpoint->name)
|
||||
->where('date_from', $dateFrom)
|
||||
->when(
|
||||
$dateTo === null,
|
||||
fn($q) => $q->whereNull('date_to'),
|
||||
fn($q) => $q->where('date_to', $dateTo)
|
||||
)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if (!$sync) {
|
||||
$sync = SyncState::create([
|
||||
'entity' => $endpoint->name,
|
||||
'date_from' => $dateFrom,
|
||||
'date_to' => $dateTo,
|
||||
'status' => SyncStatus::PENDING,
|
||||
'started_at' => $now,
|
||||
]);
|
||||
|
||||
Log::info('WB sync created', [
|
||||
'id' => $sync->id,
|
||||
'entity' => $endpoint->name,
|
||||
'date_from' => $dateFrom,
|
||||
]);
|
||||
|
||||
WbSyncJob::dispatch($endpoint, $dateFrom, $dateTo, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sync->status === SyncStatus::SUCCESS) {
|
||||
Log::info('WB sync skipped, already SUCCESS', [
|
||||
'id' => $sync->id,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sync->status === SyncStatus::PENDING) {
|
||||
if ($sync->started_at && $sync->started_at->diffInMinutes($now) < config('wb-sync.timeout')) {
|
||||
Log::info('WB sync already PENDING, skipping', [
|
||||
'id' => $sync->id,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
Log::warning('WB sync stuck > wb-sync.timeout, restart', [
|
||||
'id' => $sync->id,
|
||||
'started_at' => $sync->started_at,
|
||||
]);
|
||||
}
|
||||
|
||||
$sync->update([
|
||||
'status' => SyncStatus::PENDING,
|
||||
'started_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
Log::info('WB sync started', ['id' => $sync->id]);
|
||||
|
||||
WbSyncJob::dispatch($endpoint, $dateFrom, $dateTo, 1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function handleFirstPage(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo,
|
||||
int $lastPage
|
||||
): void {
|
||||
DB::transaction(function () use ($endpoint, $dateFrom, $dateTo, $lastPage) {
|
||||
$sync = $this->getSyncForUpdate($endpoint, $dateFrom, $dateTo);
|
||||
|
||||
if ($sync->batch_id) {
|
||||
Log::info('WB sync batch already created', [
|
||||
'id' => $sync->id,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$sync->update([
|
||||
'total_pages' => $lastPage,
|
||||
'processed_pages' => 1,
|
||||
]);
|
||||
|
||||
if ($lastPage <= 1) {
|
||||
$this->markSuccess($endpoint, $dateFrom, $dateTo);
|
||||
return;
|
||||
}
|
||||
|
||||
$jobs = [];
|
||||
|
||||
for ($page = 2; $page <= $lastPage; $page++) {
|
||||
$jobs[] = new WbSyncJob(
|
||||
$endpoint,
|
||||
$dateFrom,
|
||||
$dateTo,
|
||||
$page
|
||||
);
|
||||
}
|
||||
|
||||
$batch = Bus::batch($jobs)
|
||||
->then(fn() => $this->markSuccess($endpoint, $dateFrom, $dateTo))
|
||||
->catch(fn() => $this->markFailed($endpoint, $dateFrom, $dateTo))
|
||||
->name("sync-$endpoint->name-$dateFrom")
|
||||
->dispatch();
|
||||
|
||||
$sync->update([
|
||||
'batch_id' => $batch->id,
|
||||
]);
|
||||
|
||||
Log::info('WB batch dispatched', [
|
||||
'batch_id' => $batch->id,
|
||||
'pages' => $lastPage,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function incrementProgress(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): void {
|
||||
$sync = $this->getSyncForUpdate($endpoint, $dateFrom, $dateTo);
|
||||
|
||||
$sync->incrementProcessedPages();
|
||||
|
||||
Log::info('WB progress incremented', [
|
||||
'id' => $sync->id,
|
||||
'processed_pages' => $sync->processed_pages + 1,
|
||||
]);
|
||||
}
|
||||
|
||||
public function markSuccess(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): void {
|
||||
$sync = $this->getSyncForUpdate($endpoint, $dateFrom, $dateTo);
|
||||
|
||||
$sync->update([
|
||||
'status' => SyncStatus::SUCCESS,
|
||||
]);
|
||||
|
||||
Log::info('WB sync success', [
|
||||
'id' => $sync->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function markFailed(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): void {
|
||||
$sync = $this->getSyncForUpdate($endpoint, $dateFrom, $dateTo);
|
||||
|
||||
$sync->update([
|
||||
'status' => SyncStatus::FAILED,
|
||||
]);
|
||||
|
||||
Log::error('WB sync failed', [
|
||||
'id' => $sync->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getSyncForUpdate(
|
||||
WbEndpoint $endpoint,
|
||||
string $dateFrom,
|
||||
?string $dateTo
|
||||
): SyncState {
|
||||
return SyncState::query()
|
||||
->where('entity', $endpoint->name)
|
||||
->where('date_from', $dateFrom)
|
||||
->when(
|
||||
$dateTo === null,
|
||||
fn($q) => $q->whereNull('date_to'),
|
||||
fn($q) => $q->where('date_to', $dateTo)
|
||||
)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user