485 lines
16 KiB
PHP
485 lines
16 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Pargar;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Middleware\PargarAuth;
|
||
use App\Models\Cart;
|
||
use App\Models\Customer;
|
||
use App\Models\Order;
|
||
use App\Models\PargarSyncLog;
|
||
use App\Models\Product;
|
||
use App\Services\ConfigService;
|
||
use App\Services\PargarSyncService;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Schema;
|
||
use Morilog\Jalali\Jalalian;
|
||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||
|
||
class PargarInboundController extends Controller
|
||
{
|
||
protected function sync(): PargarSyncService
|
||
{
|
||
return app(PargarSyncService::class);
|
||
}
|
||
|
||
public function login(Request $request)
|
||
{
|
||
if (! app(ConfigService::class)->isPargarAccounting()) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'data' => null,
|
||
'message' => 'Pargar integration is disabled',
|
||
], 503);
|
||
}
|
||
|
||
[$username, $password] = $this->pargarLoginCredentials($request);
|
||
|
||
$expectedUser = $this->normalizeCredential(trim((string) $this->Config('pargar_username')));
|
||
$expectedPass = $this->normalizeCredential(trim((string) $this->Config('pargar_password')));
|
||
|
||
if ($username !== $expectedUser || $password !== $expectedPass || $expectedUser === '') {
|
||
return response()->json([
|
||
'success' => false,
|
||
'data' => null,
|
||
'message' => 'Invalid credentials!',
|
||
]);
|
||
}
|
||
|
||
$token = PargarAuth::issueToken($username);
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'data' => ['token' => $token],
|
||
'message' => 'You are logged in successfully.',
|
||
]);
|
||
}
|
||
|
||
public function upsertCustomers(Request $request)
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
$customers = $payload['Customers'] ?? [];
|
||
if (! is_array($customers) || $customers === []) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'Customers list is required',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
$messages = [];
|
||
|
||
foreach ($customers as $row) {
|
||
if (! is_array($row)) {
|
||
continue;
|
||
}
|
||
|
||
$accountId = trim((string) ($row['AccountId'] ?? ''));
|
||
$fullName = trim((string) ($row['FullName'] ?? ''));
|
||
$mobile = Customer::normalizeMobile((string) ($row['CellPhone'] ?? ''));
|
||
|
||
if ($accountId === '' || $mobile === null) {
|
||
$messages[] = 'ردیف نامعتبر: AccountId یا CellPhone خالی است';
|
||
continue;
|
||
}
|
||
|
||
$byAccount = Customer::whereNull('deleted_at')
|
||
->where('pargar_account_id', $accountId)
|
||
->first();
|
||
|
||
if ($byAccount && Customer::normalizeMobile($byAccount->mobile) !== $mobile) {
|
||
$messages[] = "کد حساب {$accountId} قبلاً برای مشتری دیگری ثبت شده است";
|
||
continue;
|
||
}
|
||
|
||
$customer = Customer::findActiveByMobile($mobile);
|
||
|
||
if ($customer) {
|
||
$currentAccount = (string) ($customer->pargar_account_id ?? '');
|
||
|
||
if ($currentAccount === $accountId) {
|
||
$messages[] = 'کد حساب مشتری وجود دارد';
|
||
continue;
|
||
}
|
||
|
||
if ($currentAccount !== '' && $currentAccount !== $accountId) {
|
||
$messages[] = "شماره {$mobile} قبلاً به حساب پرگار دیگری متصل است";
|
||
continue;
|
||
}
|
||
|
||
$customer->pargar_account_id = $accountId;
|
||
$this->applyPargarCustomerName($customer, $fullName);
|
||
$customer->save();
|
||
$messages[] = "کد حسابداری مشتری {$fullName} ویرایش شد";
|
||
continue;
|
||
}
|
||
|
||
if ($byAccount) {
|
||
if (Customer::isMobileTaken($mobile, (int) $byAccount->id)) {
|
||
$messages[] = "شماره {$mobile} تکراری است";
|
||
continue;
|
||
}
|
||
|
||
if ($fullName !== '') {
|
||
$this->applyPargarCustomerName($byAccount, $fullName);
|
||
}
|
||
$byAccount->mobile = $mobile;
|
||
if (empty($byAccount->password)) {
|
||
$byAccount->password = $mobile;
|
||
}
|
||
$byAccount->save();
|
||
$messages[] = "مشتری با کد حسابداری {$accountId} بهروزرسانی شد";
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
$customer = new Customer();
|
||
$this->applyPargarCustomerName($customer, $fullName !== '' ? $fullName : 'مشتری');
|
||
$customer->mobile = $mobile;
|
||
$customer->pargar_account_id = $accountId;
|
||
$customer->active = true;
|
||
$customer->type = 2;
|
||
$customer->password = $mobile;
|
||
$customer->referral_id = $this->uniqueReferralId();
|
||
$customer->save();
|
||
|
||
$messages[] = "مشتری با کد حسابداری {$accountId} ایجاد شد";
|
||
} catch (\Throwable $e) {
|
||
$messages[] = "خطا برای {$mobile}: " . (
|
||
$e instanceof UnprocessableEntityHttpException
|
||
? $e->getMessage()
|
||
: 'ثبت مشتری ناموفق بود'
|
||
);
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => implode(' | ', $messages),
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
public function listOrders(Request $request)
|
||
{
|
||
$orders = Order::query()
|
||
->where('sts', 2)
|
||
->whereNull('pargar_confirmed_at')
|
||
->limit(100)
|
||
->get();
|
||
|
||
$documents = $orders->map(function (Order $order) {
|
||
$customer = Customer::find((int) ($order->customer_id ?? 0));
|
||
|
||
return [
|
||
'OrderId' => (string) $order->id,
|
||
'OrderType' => (int) ($order->direction ?? 1) === 1 ? 1 : -1,
|
||
'PriceType' => 1,
|
||
'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0),
|
||
'ItemId' => 11,
|
||
'ItemName' => $this->pargarItemName($order),
|
||
'OrderDate' => $order->pay_at
|
||
? Jalalian::fromDateTime($order->pay_at)->format('Y/m/d-H:i:s')
|
||
: Jalalian::fromDateTime($order->created_at)->format('Y/m/d-H:i:s'),
|
||
'Fee' => (float) ($order->price ?? 0),
|
||
'Count' => 1,
|
||
'Weight' => (float) ($order->amount ?? 0),
|
||
'Price' => (float) ($order->total_price ?? 0),
|
||
'Carat' => 750,
|
||
'Description' => (string) ($order->description ?? ''),
|
||
];
|
||
})->values();
|
||
|
||
return response()->json([
|
||
'state' => 'true',
|
||
'msg' => '',
|
||
'data' => [
|
||
'documents' => $documents,
|
||
'duplicated' => 'false',
|
||
'result' => 'OK',
|
||
],
|
||
]);
|
||
}
|
||
|
||
public function confirmOrders(Request $request)
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
$orderIds = $payload['OrderIds'] ?? [];
|
||
if (! is_array($orderIds)) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'OrderIds must be an array',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
$this->sync()->confirmOrders($orderIds);
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'تاییدیه اسناد با موفقیت دریافت شد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
public function storeDocuments(Request $request)
|
||
{
|
||
$data = $this->pargarPayload($request);
|
||
$accountId = trim((string) ($data['accountId'] ?? ''));
|
||
$transactions = $data['transactions'] ?? [];
|
||
|
||
$customer = $accountId !== ''
|
||
? Customer::where('pargar_account_id', $accountId)->first()
|
||
: null;
|
||
|
||
PargarSyncLog::create([
|
||
'customer_id' => $customer?->id,
|
||
'desc' => 'دریافت اسناد از پرگار',
|
||
'type' => 'document',
|
||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||
'pargar_ref' => $accountId,
|
||
'data' => json_encode($data, JSON_UNESCAPED_UNICODE),
|
||
]);
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'اسناد مشتری با موفقیت دریافت شد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
public function storeMande(Request $request)
|
||
{
|
||
$data = $this->pargarPayload($request);
|
||
$accountId = trim((string) ($data['AccountId'] ?? ''));
|
||
|
||
$customer = Customer::where('pargar_account_id', $accountId)->first();
|
||
if (! $customer) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'مشتری یافت نشد',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
$goldInv = isset($data['GoldInv']) ? (float) str_replace('/', '.', (string) $data['GoldInv']) : 0;
|
||
$rialInv = isset($data['RialInv']) ? (float) str_replace('/', '.', (string) $data['RialInv']) : 0;
|
||
|
||
$wallet = $customer->wallet->where('fund_id', null)->first();
|
||
if ($wallet) {
|
||
$goldDiff = $goldInv - (float) $wallet->gold;
|
||
$rialDiff = $rialInv - (float) $wallet->cash;
|
||
} else {
|
||
$goldDiff = $goldInv;
|
||
$rialDiff = $rialInv;
|
||
}
|
||
|
||
if (abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1) {
|
||
$walletInterface = new WalletInterface($customer->id, 2);
|
||
new TransactionInterface($walletInterface, [
|
||
'cash' => $rialDiff,
|
||
'gold' => $goldDiff,
|
||
'expire_at' => now(),
|
||
'active' => true,
|
||
'start_at' => now(),
|
||
], 22, $goldDiff > 0, $rialDiff > 0);
|
||
}
|
||
|
||
PargarSyncLog::create([
|
||
'customer_id' => $customer->id,
|
||
'desc' => 'دریافت مانده از پرگار',
|
||
'type' => 'mande',
|
||
'amount' => $goldInv,
|
||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||
'pargar_ref' => $accountId,
|
||
'data' => json_encode($data, JSON_UNESCAPED_UNICODE),
|
||
]);
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'مانده مشتری با موفقیت دریافت شد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
public function listNewCustomers(Request $request)
|
||
{
|
||
$customers = Customer::query()
|
||
->whereNull('deleted_at')
|
||
->whereNull('pargar_account_id')
|
||
->limit(100)
|
||
->get(['id', 'fname', 'lname', 'name', 'mobile']);
|
||
|
||
if ($customers->isEmpty()) {
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'مشتری جدید وجود ندارد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
$rows = $customers->map(function (Customer $customer) {
|
||
$fname = trim((string) ($customer->fname ?? ''));
|
||
$lname = trim((string) ($customer->lname ?? ''));
|
||
|
||
if ($fname === '' && $lname === '') {
|
||
$nameParts = preg_split('/\s+/', trim((string) ($customer->name ?? '')), 2) ?: ['', ''];
|
||
$fname = trim((string) ($nameParts[0] ?? ''));
|
||
$lname = trim((string) ($nameParts[1] ?? ''));
|
||
}
|
||
|
||
return [
|
||
'id' => (string) $customer->id,
|
||
'name' => $fname,
|
||
'family' => $lname,
|
||
'mobile' => (string) $customer->mobile,
|
||
];
|
||
});
|
||
|
||
return response()->json([
|
||
'state' => 'true',
|
||
'msg' => '',
|
||
'customer' => $rows,
|
||
]);
|
||
}
|
||
|
||
public function confirmCustomers(Request $request)
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
$rows = $payload['customers'] ?? [];
|
||
if (! is_array($rows)) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'customers must be an array',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
foreach ($rows as $row) {
|
||
if (! is_array($row)) {
|
||
continue;
|
||
}
|
||
|
||
$customerId = $row['customer_id'] ?? $row['id_customer'] ?? null;
|
||
$accountId = $row['accounting_id'] ?? null;
|
||
if ($customerId === null || $accountId === null) {
|
||
continue;
|
||
}
|
||
|
||
$customer = Customer::find($customerId);
|
||
if ($customer) {
|
||
$customer->pargar_account_id = (string) $accountId;
|
||
$customer->save();
|
||
|
||
$log = PargarSyncLog::query()
|
||
->where('customer_id', $customer->id)
|
||
->where('type', 'customer')
|
||
->where('sync_status', PargarSyncService::STATUS_PENDING)
|
||
->latest('id')
|
||
->first();
|
||
|
||
if ($log) {
|
||
$this->sync()->markSuccess($log, (string) $accountId);
|
||
}
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'تاییدیه ثبت مشتری با موفقیت دریافت شد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @return array{0: string, 1: string}
|
||
*/
|
||
private function pargarLoginCredentials(Request $request): array
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
|
||
return [
|
||
$this->normalizeCredential((string) ($payload['username'] ?? '')),
|
||
$this->normalizeCredential((string) ($payload['password'] ?? '')),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* @return array<string, mixed>
|
||
*/
|
||
private function pargarPayload(Request $request): array
|
||
{
|
||
$payload = $request->all();
|
||
if ($payload !== []) {
|
||
return $payload;
|
||
}
|
||
|
||
$decoded = json_decode((string) $request->getContent(), true);
|
||
|
||
return is_array($decoded) ? $decoded : [];
|
||
}
|
||
|
||
private function normalizeCredential(string $value): string
|
||
{
|
||
$persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
||
$arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
|
||
$latin = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||
|
||
return trim(str_replace(array_merge($persian, $arabic), array_merge($latin, $latin), $value));
|
||
}
|
||
|
||
private function applyPargarCustomerName(Customer $customer, string $fullName): void
|
||
{
|
||
$fullName = trim($fullName);
|
||
if ($fullName === '') {
|
||
return;
|
||
}
|
||
|
||
[$fname, $lname] = $this->splitPargarFullName($fullName);
|
||
$customer->fname = $fname;
|
||
$customer->lname = $lname;
|
||
// mutator مدل Customer مقدار name را از fname + lname میسازد
|
||
$customer->name = trim("$fname $lname");
|
||
}
|
||
|
||
/**
|
||
* @return array{0: string, 1: string}
|
||
*/
|
||
private function splitPargarFullName(string $fullName): array
|
||
{
|
||
$parts = preg_split('/\s+/u', trim($fullName), 2) ?: ['', ''];
|
||
|
||
return [
|
||
trim((string) ($parts[0] ?? '')),
|
||
trim((string) ($parts[1] ?? '')),
|
||
];
|
||
}
|
||
|
||
private function uniqueReferralId(): string
|
||
{
|
||
do {
|
||
$referral = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 0, 4);
|
||
} while (Customer::where('referral_id', $referral)->exists());
|
||
|
||
return $referral;
|
||
}
|
||
|
||
private function pargarItemName(Order $order): string
|
||
{
|
||
return $this->isCartOrder($order) ? 'سبد خرید' : 'آبشده';
|
||
}
|
||
|
||
private function isCartOrder(Order $order): bool
|
||
{
|
||
if (Schema::hasTable('cart') && Schema::hasColumn('cart', 'order_id')) {
|
||
return Cart::where('order_id', $order->id)->exists();
|
||
}
|
||
|
||
return PargarSyncLog::where('order_id', $order->id)
|
||
->where('desc', 'سبد خرید')
|
||
->exists();
|
||
}
|
||
}
|