270 lines
8.4 KiB
PHP
270 lines
8.4 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Services;
|
||
|
|
|
||
|
|
use App\Models\Customer;
|
||
|
|
use App\Models\Order;
|
||
|
|
use App\Models\PargarSyncLog;
|
||
|
|
use Illuminate\Support\Facades\Log;
|
||
|
|
use Illuminate\Support\Facades\Schema;
|
||
|
|
|
||
|
|
class PargarSyncService
|
||
|
|
{
|
||
|
|
public const STATUS_PENDING = 'pending';
|
||
|
|
public const STATUS_SUCCESS = 'success';
|
||
|
|
public const STATUS_FAILED = 'failed';
|
||
|
|
|
||
|
|
public function buildLog(mixed $response = null, ?string $extra = null): string
|
||
|
|
{
|
||
|
|
$parts = array_filter([
|
||
|
|
$extra,
|
||
|
|
is_array($response) ? json_encode($response, JSON_UNESCAPED_UNICODE) : (is_string($response) ? $response : null),
|
||
|
|
]);
|
||
|
|
|
||
|
|
return implode("\n", $parts);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function markSuccess(PargarSyncLog $log, ?string $ref = null, mixed $response = null, ?array $payload = null): void
|
||
|
|
{
|
||
|
|
$update = [
|
||
|
|
'pargar_ref' => $ref,
|
||
|
|
'res' => is_array($response)
|
||
|
|
? json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||
|
|
: (is_string($response) ? $response : null),
|
||
|
|
'sync_status' => self::STATUS_SUCCESS,
|
||
|
|
'sync_log' => null,
|
||
|
|
];
|
||
|
|
|
||
|
|
if ($payload !== null) {
|
||
|
|
$update['data'] = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||
|
|
}
|
||
|
|
|
||
|
|
PargarSyncLog::where('id', $log->id)->update($update);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function markFailed(PargarSyncLog $log, mixed $response = null, ?string $extra = null): void
|
||
|
|
{
|
||
|
|
PargarSyncLog::where('id', $log->id)->update([
|
||
|
|
'res' => is_array($response)
|
||
|
|
? json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||
|
|
: (is_string($response) ? $response : null),
|
||
|
|
'sync_status' => self::STATUS_FAILED,
|
||
|
|
'sync_log' => $this->buildLog($response, $extra),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isFailed(PargarSyncLog $log): bool
|
||
|
|
{
|
||
|
|
return ($log->sync_status ?? null) === self::STATUS_FAILED
|
||
|
|
|| (empty($log->pargar_ref) && ($log->sync_status ?? null) !== self::STATUS_SUCCESS);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function registerOnCreate(Customer $customer): void
|
||
|
|
{
|
||
|
|
if (! empty($customer->pargar_account_id) || ! $this->isSyncEnabled()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$customerId = (int) $customer->id;
|
||
|
|
|
||
|
|
dispatch(function () use ($customerId) {
|
||
|
|
$service = app(self::class);
|
||
|
|
if (! $service->isSyncEnabled()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$customer = Customer::find($customerId);
|
||
|
|
if ($customer === null || ! empty($customer->pargar_account_id)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$service->queueCustomerExport($customer);
|
||
|
|
} catch (\Throwable $e) {
|
||
|
|
Log::warning('Pargar auto-queue on customer create failed', [
|
||
|
|
'customer_id' => $customerId,
|
||
|
|
'error' => $e->getMessage(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
})->afterResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function queueCustomerExport(Customer $customer): PargarSyncLog
|
||
|
|
{
|
||
|
|
$existing = PargarSyncLog::query()
|
||
|
|
->where('customer_id', $customer->id)
|
||
|
|
->where('type', 'customer')
|
||
|
|
->where('sync_status', self::STATUS_PENDING)
|
||
|
|
->latest('id')
|
||
|
|
->first();
|
||
|
|
|
||
|
|
if ($existing) {
|
||
|
|
return $existing;
|
||
|
|
}
|
||
|
|
|
||
|
|
return PargarSyncLog::create([
|
||
|
|
'customer_id' => $customer->id,
|
||
|
|
'desc' => 'در انتظار دریافت توسط پرگار',
|
||
|
|
'type' => 'customer',
|
||
|
|
'sync_status' => self::STATUS_PENDING,
|
||
|
|
'data' => json_encode(['handler' => 'exportCustomer'], JSON_UNESCAPED_UNICODE),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function registerCustomer(Customer $customer): array
|
||
|
|
{
|
||
|
|
$customer->refresh();
|
||
|
|
|
||
|
|
if (! empty($customer->pargar_account_id)) {
|
||
|
|
return [
|
||
|
|
'ok' => true,
|
||
|
|
'message' => 'مشتری قبلاً در پرگار ثبت شده است',
|
||
|
|
'pargar_account_id' => $customer->pargar_account_id,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->queueCustomerExport($customer);
|
||
|
|
|
||
|
|
return [
|
||
|
|
'ok' => true,
|
||
|
|
'message' => 'مشتری در صف دریافت پرگار قرار گرفت',
|
||
|
|
'pargar_account_id' => null,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
public function recordTrade(?Order $order, bool $fromCart = false): ?PargarSyncLog
|
||
|
|
{
|
||
|
|
if (! $order || ! $order->id || ! $this->isSyncEnabled()) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
$existing = PargarSyncLog::where('order_id', $order->id)->first();
|
||
|
|
if ($existing) {
|
||
|
|
return $existing;
|
||
|
|
}
|
||
|
|
|
||
|
|
$direction = (int) ($order->direction ?? 0);
|
||
|
|
$desc = $fromCart
|
||
|
|
? 'سبد خرید'
|
||
|
|
: ($direction === 1 ? 'خرید آبشده' : 'فروش آبشده');
|
||
|
|
|
||
|
|
return PargarSyncLog::create([
|
||
|
|
'customer_id' => $order->customer_id,
|
||
|
|
'amount' => $order->amount,
|
||
|
|
'desc' => $desc,
|
||
|
|
'order_id' => $order->id,
|
||
|
|
'date' => $order->pay_at ?? $order->created_at,
|
||
|
|
'type' => 'order',
|
||
|
|
'sync_status' => self::STATUS_PENDING,
|
||
|
|
'data' => json_encode([
|
||
|
|
'handler' => 'exportOrder',
|
||
|
|
'direction' => $direction,
|
||
|
|
'price' => $order->price,
|
||
|
|
'total_price' => $order->total_price,
|
||
|
|
], JSON_UNESCAPED_UNICODE),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function confirmOrders(array $orderIds): array
|
||
|
|
{
|
||
|
|
$confirmed = 0;
|
||
|
|
|
||
|
|
foreach ($orderIds as $orderId) {
|
||
|
|
$order = Order::find($orderId);
|
||
|
|
if (! $order) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$order->pargar_confirmed_at = now();
|
||
|
|
$order->save();
|
||
|
|
|
||
|
|
$log = PargarSyncLog::where('order_id', $order->id)->latest('id')->first();
|
||
|
|
if ($log) {
|
||
|
|
$this->markSuccess($log, (string) $order->id, ['OrderId' => (string) $order->id]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$confirmed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ['confirmed' => $confirmed];
|
||
|
|
}
|
||
|
|
|
||
|
|
public function retry(PargarSyncLog $log): array
|
||
|
|
{
|
||
|
|
if (! $this->isFailed($log) && ! empty($log->pargar_ref)) {
|
||
|
|
return ['ok' => false, 'message' => 'این سند قبلاً در پرگار ثبت شده است'];
|
||
|
|
}
|
||
|
|
|
||
|
|
$payload = json_decode((string) $log->data, true) ?: [];
|
||
|
|
$handler = $payload['handler'] ?? $this->inferHandler($log);
|
||
|
|
|
||
|
|
if ($handler === 'exportOrder' && $log->order_id) {
|
||
|
|
Order::where('id', $log->order_id)->update(['pargar_confirmed_at' => null]);
|
||
|
|
PargarSyncLog::where('id', $log->id)->update([
|
||
|
|
'sync_status' => self::STATUS_PENDING,
|
||
|
|
'sync_log' => null,
|
||
|
|
'pargar_ref' => null,
|
||
|
|
]);
|
||
|
|
$log->refresh();
|
||
|
|
|
||
|
|
return [
|
||
|
|
'ok' => true,
|
||
|
|
'message' => 'سفارش دوباره در صف ارسال به پرگار قرار گرفت',
|
||
|
|
'pargar' => $log,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($handler === 'exportCustomer' && $log->customer_id) {
|
||
|
|
PargarSyncLog::where('id', $log->id)->update([
|
||
|
|
'sync_status' => self::STATUS_PENDING,
|
||
|
|
'sync_log' => null,
|
||
|
|
'pargar_ref' => null,
|
||
|
|
]);
|
||
|
|
$log->refresh();
|
||
|
|
|
||
|
|
return [
|
||
|
|
'ok' => true,
|
||
|
|
'message' => 'مشتری دوباره در صف دریافت پرگار قرار گرفت',
|
||
|
|
'pargar' => $log,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
'ok' => false,
|
||
|
|
'message' => $log->sync_log ?: 'ارسال مجدد به پرگار ناموفق بود',
|
||
|
|
'pargar' => $log,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
private function inferHandler(PargarSyncLog $log): string
|
||
|
|
{
|
||
|
|
if ($log->order_id) {
|
||
|
|
return 'exportOrder';
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($log->type === 'customer') {
|
||
|
|
return 'exportCustomer';
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($log->type === 'mande') {
|
||
|
|
return 'importMande';
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'importDocument';
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isSyncEnabled(): bool
|
||
|
|
{
|
||
|
|
$config = app(ConfigService::class);
|
||
|
|
|
||
|
|
return $config->isPargarAccounting()
|
||
|
|
&& trim($config->getString('pargar_username', '')) !== ''
|
||
|
|
&& trim($config->getString('pargar_password', '')) !== '';
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hasSyncTable(): bool
|
||
|
|
{
|
||
|
|
return Schema::hasTable('pargar_sync_logs');
|
||
|
|
}
|
||
|
|
}
|