feat(accounting): document colleague trades on configured accounts
Post Montzar-covered buy/sell to Zarin/Tahesab/Pargar using montzar_account_* settings, and harden public storage URL/MIME handling for uploads. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -40,9 +40,7 @@ class FileController extends Controller
|
||||
|
||||
$path = $this->createDir();
|
||||
$originalName = $uploaded->getClientOriginalName();
|
||||
$ext = $uploaded->getClientOriginalExtension()
|
||||
?: $uploaded->extension()
|
||||
?: 'jpg';
|
||||
$ext = $this->resolveUploadExtension($uploaded);
|
||||
$title = $request->input('title') ?: $originalName;
|
||||
$file_name_src = md5(time().$originalName);
|
||||
$file_name = $file_name_src.'.'.$ext;
|
||||
@@ -70,4 +68,35 @@ class FileController extends Controller
|
||||
|
||||
return response()->json($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* پسوند واقعی فایل (MIME)، نه اسم اصلی مرورگر — آپلود بنر WebP است.
|
||||
*/
|
||||
private function resolveUploadExtension(\Illuminate\Http\UploadedFile $uploaded): string
|
||||
{
|
||||
$mime = strtolower((string) ($uploaded->getMimeType() ?: $uploaded->getClientMimeType()));
|
||||
$fromMime = match (true) {
|
||||
str_contains($mime, 'webp') => 'webp',
|
||||
str_contains($mime, 'png') => 'png',
|
||||
str_contains($mime, 'jpeg'), str_contains($mime, 'jpg') => 'jpg',
|
||||
str_contains($mime, 'gif') => 'gif',
|
||||
str_contains($mime, 'svg') => 'svg',
|
||||
default => null,
|
||||
};
|
||||
if ($fromMime) {
|
||||
return $fromMime;
|
||||
}
|
||||
|
||||
$client = strtolower((string) $uploaded->getClientOriginalExtension());
|
||||
if (in_array($client, ['webp', 'png', 'jpg', 'jpeg', 'gif', 'svg'], true)) {
|
||||
return $client === 'jpeg' ? 'jpg' : $client;
|
||||
}
|
||||
|
||||
$guessed = strtolower((string) $uploaded->extension());
|
||||
if (in_array($guessed, ['webp', 'png', 'jpg', 'jpeg', 'gif', 'svg'], true)) {
|
||||
return $guessed === 'jpeg' ? 'jpg' : $guessed;
|
||||
}
|
||||
|
||||
return 'jpg';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,10 +453,25 @@ class TahesabController extends Controller
|
||||
}
|
||||
|
||||
$customer = Customer::where('id', $data['customer_id'])->first();
|
||||
$accountOverride = trim((string) ($data['accounting_account_code'] ?? ''));
|
||||
$isColleagueLeg = ! empty($data['colleague_leg']) && $accountOverride !== '';
|
||||
|
||||
if ($customer && ($customer->moshtari_code === null)) {
|
||||
$this->createCustomerInTahesab($customer->id);
|
||||
$customer = Customer::where('id', $data['customer_id'])->first();
|
||||
if (! $isColleagueLeg) {
|
||||
if ($customer && ($customer->moshtari_code === null)) {
|
||||
$this->createCustomerInTahesab($customer->id);
|
||||
$customer = Customer::where('id', $data['customer_id'])->first();
|
||||
}
|
||||
}
|
||||
|
||||
if (! $customer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hesabCode = $isColleagueLeg
|
||||
? (int) $accountOverride
|
||||
: (int) ($customer->moshtari_code ?? 0);
|
||||
if ($hesabCode <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$kind = $mapper->resolveKind($product);
|
||||
@@ -490,7 +505,14 @@ class TahesabController extends Controller
|
||||
|
||||
$action = $isBuy ? 'خرید' : 'فروش';
|
||||
$kindFa = $mapper->kindLabel($kind);
|
||||
$sharh = trim($title !== '' ? "{$action} {$title}" : "{$action} {$kindFa}");
|
||||
$sharh = trim((string) ($data['sharh_override'] ?? ''));
|
||||
if ($sharh === '') {
|
||||
$sharh = trim($title !== '' ? "{$action} {$title}" : "{$action} {$kindFa}");
|
||||
}
|
||||
|
||||
$orderId = is_object($data['order'] ?? null)
|
||||
? ($data['order']->id ?? null)
|
||||
: ($data['order']['id'] ?? null);
|
||||
|
||||
$data['handler'] = 'storeTrade';
|
||||
if ($existing && empty($existing->data)) {
|
||||
@@ -500,21 +522,21 @@ class TahesabController extends Controller
|
||||
'customer_id' => $customer->id,
|
||||
'desc' => $sharh,
|
||||
'amount' => $mapper->isCountDoc($product) ? $count : $vazn,
|
||||
'order_id' => $data['order']['id'] ?? null,
|
||||
'order_id' => $orderId,
|
||||
'date' => $data['pay_at'] ?? Carbon::now(),
|
||||
'type' => $mapper->syncTypeLabel($product),
|
||||
'created_at' => Carbon::now(),
|
||||
'data' => json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
||||
]);
|
||||
|
||||
if ($mapper->isCountDoc($product) && $title === '') {
|
||||
if ($mapper->isCountDoc($product) && $title === '' && ! $isColleagueLeg) {
|
||||
$this->sync()->markFailed($tahesab, null, 'نام محصول برای سند تعدادی تهحساب خالی است');
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = $this->buildBuySaleBody(
|
||||
$kind,
|
||||
(int) $customer->moshtari_code,
|
||||
$hesabCode,
|
||||
(int) $year,
|
||||
(int) $month,
|
||||
(int) $day,
|
||||
|
||||
@@ -1394,29 +1394,49 @@ class ZarinController extends Controller
|
||||
}
|
||||
|
||||
$customer = Customer::where('id', $data['customer_id'])->first();
|
||||
$accountOverride = trim((string) ($data['accounting_account_code'] ?? ''));
|
||||
$isColleagueLeg = ! empty($data['colleague_leg']) && $accountOverride !== '';
|
||||
|
||||
if ($customer && ($customer->moshtari_code === null)) {
|
||||
$this->createCustomerInZarin($customer->id);
|
||||
$customer = Customer::where('id', $data['customer_id'])->first();
|
||||
if (! $isColleagueLeg) {
|
||||
if ($customer && ($customer->moshtari_code === null)) {
|
||||
$this->createCustomerInZarin($customer->id);
|
||||
$customer = Customer::where('id', $data['customer_id'])->first();
|
||||
}
|
||||
|
||||
if (!$customer || empty($customer->moshtari_code)) {
|
||||
return false;
|
||||
}
|
||||
} elseif (! $customer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$customer || empty($customer->moshtari_code)) {
|
||||
return false;
|
||||
$accountCustomer = $customer;
|
||||
if ($isColleagueLeg) {
|
||||
$accountCustomer = clone $customer;
|
||||
$accountCustomer->moshtari_code = $accountOverride;
|
||||
}
|
||||
|
||||
$rawAmount = (float) $data['amount'];
|
||||
$count = $mapper->accountingQty($product, $rawAmount, $data);
|
||||
$vazn = $mapper->accountingWeight($product, $rawAmount, $data);
|
||||
$isBuy = (int) ($data['direction'] ?? 0) === 1;
|
||||
$orderId = $data['order']['id'] ?? ($existing->order_id ?? null);
|
||||
$orderId = is_object($data['order'] ?? null)
|
||||
? ($data['order']->id ?? null)
|
||||
: ($data['order']['id'] ?? ($existing->order_id ?? null));
|
||||
$title = $mapper->productTitle($product);
|
||||
$action = $isBuy ? 'خرید' : 'فروش';
|
||||
$sharh = trim($title !== '' ? "{$action} {$title}" : "{$action} {$mapper->kindLabel($mapper->resolveKind($product))}");
|
||||
$sharh = trim((string) ($data['sharh_override'] ?? ''));
|
||||
if ($sharh === '') {
|
||||
$sharh = trim($title !== '' ? "{$action} {$title}" : "{$action} {$mapper->kindLabel($mapper->resolveKind($product))}");
|
||||
}
|
||||
$syncAmount = $mapper->isCountDoc($product) ? $count : $vazn;
|
||||
$uidSource = $orderId ?? null;
|
||||
if (!empty($data['line'])) {
|
||||
$uidSource = ($orderId ?? '0') . 'L' . (int) $data['line'];
|
||||
}
|
||||
if ($isColleagueLeg) {
|
||||
$uidSource = ($uidSource ?? '0') . 'C';
|
||||
}
|
||||
|
||||
$data['handler'] = 'storeTrade';
|
||||
if ($existing && empty($existing->data)) {
|
||||
@@ -1440,6 +1460,8 @@ class ZarinController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
$uidKind = $isColleagueLeg ? '11' : '10';
|
||||
|
||||
try {
|
||||
if ($mapper->isCountDoc($product)) {
|
||||
$fi = (float) ($data['price'] ?? 0);
|
||||
@@ -1453,25 +1475,25 @@ class ZarinController extends Controller
|
||||
$result = $this->postZarinCountDoc(
|
||||
$zarin,
|
||||
$data,
|
||||
$customer,
|
||||
$accountCustomer,
|
||||
$product,
|
||||
$count,
|
||||
$isBuy ? -1 : 1,
|
||||
$fi,
|
||||
$sharh,
|
||||
$this->zarinUid('10', $uidSource ?? $zarin->id)
|
||||
$this->zarinUid($uidKind, $uidSource ?? $zarin->id)
|
||||
);
|
||||
} else {
|
||||
$result = $this->postZarinWeightDoc(
|
||||
$zarin,
|
||||
$data,
|
||||
$customer,
|
||||
$accountCustomer,
|
||||
$vazn,
|
||||
$isBuy ? 'f' : 'k',
|
||||
(float) ($data['price'] ?? 0),
|
||||
$sharh,
|
||||
$mapper->jensFelez($product) ?? 0,
|
||||
$this->zarinUid('10', $uidSource ?? $zarin->id)
|
||||
$this->zarinUid($uidKind, $uidSource ?? $zarin->id)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -198,6 +198,25 @@ class PargarInboundController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$colleagueLogs = PargarSyncLog::query()
|
||||
->where('type', PargarSyncService::TYPE_ORDER)
|
||||
->where('sync_status', PargarSyncService::STATUS_PENDING)
|
||||
->where('data', 'like', '%"colleague_leg":true%')
|
||||
->orderBy('id')
|
||||
->limit(100)
|
||||
->get();
|
||||
|
||||
foreach ($colleagueLogs as $log) {
|
||||
$order = Order::find((int) ($log->order_id ?? 0));
|
||||
if (! $order) {
|
||||
continue;
|
||||
}
|
||||
$payload = $this->orderExportPayload($order, $log);
|
||||
if ($payload !== null) {
|
||||
$documents->push($payload);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'state' => 'true',
|
||||
'msg' => '',
|
||||
@@ -583,12 +602,23 @@ class PargarInboundController extends Controller
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function orderExportPayload(Order $order): ?array
|
||||
private function orderExportPayload(Order $order, ?PargarSyncLog $forcedLog = null): ?array
|
||||
{
|
||||
$sync = $this->sync();
|
||||
$log = PargarSyncLog::where('order_id', $order->id)->latest('id')->first();
|
||||
$log = $forcedLog ?? PargarSyncLog::where('order_id', $order->id)
|
||||
->where(function ($q) {
|
||||
$q->whereNull('data')
|
||||
->orWhere('data', 'not like', '%"colleague_leg":true%');
|
||||
})
|
||||
->latest('id')
|
||||
->first();
|
||||
$payload = $log ? (json_decode((string) $log->data, true) ?: []) : [];
|
||||
|
||||
// اگر لاگ اجباری همکار نیست و لاگ مشتری نبود، سند مشتری نساز
|
||||
if ($forcedLog === null && $log && ! empty($payload['colleague_leg'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$paymentKind = (string) ($payload['payment_kind'] ?? PargarSyncService::PAYMENT_CASH);
|
||||
if ($paymentKind === PargarSyncService::PAYMENT_OTHER_PRODUCT) {
|
||||
return null;
|
||||
@@ -602,6 +632,8 @@ class PargarInboundController extends Controller
|
||||
$isCustomerBuy = (int) ($order->direction ?? 1) === 1;
|
||||
$customerDirection = (int) ($order->direction ?? 0);
|
||||
$fromCart = (bool) ($payload['from_cart'] ?? $this->isCartOrder($order));
|
||||
$isColleagueLeg = ! empty($payload['colleague_leg']);
|
||||
$colleagueAccountId = trim((string) ($payload['colleague_account_id'] ?? ''));
|
||||
|
||||
$product = $sync->resolveOrderProduct($order, $payload);
|
||||
$number = (float) ($payload['number'] ?? 0);
|
||||
@@ -609,6 +641,22 @@ class PargarInboundController extends Controller
|
||||
$totalPrice = (float) ($order->total_price ?? $order->payment_amount ?? 0);
|
||||
$unitPrice = (float) ($order->price ?? 0);
|
||||
|
||||
if ($isColleagueLeg) {
|
||||
if (isset($payload['direction'])) {
|
||||
$isCustomerBuy = (int) $payload['direction'] === 1;
|
||||
$customerDirection = (int) $payload['direction'];
|
||||
}
|
||||
if (isset($payload['total_price'])) {
|
||||
$totalPrice = (float) $payload['total_price'];
|
||||
}
|
||||
if (isset($payload['price'])) {
|
||||
$unitPrice = (float) $payload['price'];
|
||||
}
|
||||
if (isset($payload['amount'])) {
|
||||
$amount = abs((float) $payload['amount']);
|
||||
}
|
||||
}
|
||||
|
||||
$isCountBased = $product
|
||||
? self::isCountBasedProduct(self::productUnitPayload($product))
|
||||
: false;
|
||||
@@ -660,11 +708,19 @@ class PargarInboundController extends Controller
|
||||
$totalPrice = 0;
|
||||
}
|
||||
|
||||
$accountId = $isColleagueLeg && $colleagueAccountId !== ''
|
||||
? (int) $colleagueAccountId
|
||||
: (int) ($customer->pargar_account_id ?? 0);
|
||||
|
||||
$description = $isColleagueLeg
|
||||
? (((int) ($payload['direction'] ?? $customerDirection) === 1) ? 'خرید از اپلیکیشن همکار' : 'فروش از اپلیکیشن همکار')
|
||||
: self::accountingShopTradeDesc($customerDirection, $fromCart);
|
||||
|
||||
return [
|
||||
'OrderId' => (string) $order->id,
|
||||
'OrderId' => $isColleagueLeg ? ((string) $order->id).'-C' : (string) $order->id,
|
||||
'OrderType' => $isCustomerBuy ? -1 : 1,
|
||||
'PriceType' => 1,
|
||||
'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0),
|
||||
'CustomerAccountId' => $accountId,
|
||||
'ItemId' => $itemId,
|
||||
'ItemName' => $itemName,
|
||||
'OrderDate' => $order->pay_at
|
||||
@@ -675,7 +731,7 @@ class PargarInboundController extends Controller
|
||||
'Weight' => $weight,
|
||||
'Price' => $totalPrice,
|
||||
'Carat' => 750,
|
||||
'Description' => self::accountingShopTradeDesc($customerDirection, $fromCart),
|
||||
'Description' => $description,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -50,9 +50,37 @@ class PublicStorageController extends Controller
|
||||
*/
|
||||
private function candidatePaths(string $path): array
|
||||
{
|
||||
return [
|
||||
storage_path('app/public/'.$path),
|
||||
public_path('storage/'.$path),
|
||||
];
|
||||
$relative = [$path, ...$this->alternateExtensionPaths($path)];
|
||||
$full = [];
|
||||
foreach ($relative as $rel) {
|
||||
$full[] = storage_path('app/public/'.$rel);
|
||||
$full[] = storage_path('app/public/storage/'.$rel);
|
||||
$full[] = public_path('storage/'.$rel);
|
||||
}
|
||||
|
||||
return $full;
|
||||
}
|
||||
|
||||
/**
|
||||
* آپلود ادمین WebP میسازد ولی const گاهی همان هش را با .png نگه میدارد.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function alternateExtensionPaths(string $path): array
|
||||
{
|
||||
if (! preg_match('/^(.*)\.([a-z0-9]+)$/i', $path, $matches)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stem = $matches[1];
|
||||
$ext = strtolower($matches[2]);
|
||||
$out = [];
|
||||
foreach (['webp', 'png', 'jpg', 'jpeg', 'gif'] as $alt) {
|
||||
if ($alt !== $ext) {
|
||||
$out[] = $stem.'.'.$alt;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +263,15 @@ class SingleOrderController extends Controller
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['order'])) {
|
||||
try {
|
||||
app(\App\Services\ColleagueAccountingService::class)->recordCoveredTrade($data);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
$response['reward'] = $this->give_order_reward($data, $wallet, $wallet_interface);
|
||||
if ((int) $data['direction'] === 1 && $customer) {
|
||||
$referrer = Customer::where('referral_id', $customer->referral_to_id)->first();
|
||||
|
||||
+13
-2
@@ -51,7 +51,9 @@ class File extends Model
|
||||
$trimmed = trim($path);
|
||||
|
||||
if (preg_match('/^https?:\/\//i', $trimmed)) {
|
||||
return self::rewriteWrongPublicUrl($trimmed) ?? $trimmed;
|
||||
$rewritten = self::rewriteWrongPublicUrl($trimmed) ?? $trimmed;
|
||||
|
||||
return self::preferHttpsUrl($rewritten);
|
||||
}
|
||||
|
||||
$relative = self::normalizeRelativeStoragePath($trimmed);
|
||||
@@ -163,7 +165,16 @@ class File extends Model
|
||||
$base = 'https://'.ltrim($base, '/');
|
||||
}
|
||||
|
||||
return $base;
|
||||
return self::preferHttpsUrl($base);
|
||||
}
|
||||
|
||||
private static function preferHttpsUrl(string $url): string
|
||||
{
|
||||
if (str_contains($url, 'localhost') || str_contains($url, '127.0.0.1')) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return (string) preg_replace('/^http:\/\//i', 'https://', $url);
|
||||
}
|
||||
|
||||
private static function baseUrlFromSettings(): string
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Http\Controllers\Admin\TahesabController;
|
||||
use App\Http\Controllers\Admin\ZarinController;
|
||||
use App\Models\Order;
|
||||
use App\Models\PargarSyncLog;
|
||||
use App\Models\Tahesab;
|
||||
use App\Models\Zarin;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* سند حسابداری سمت همکار (Montzar) — جدا از سند مشتری.
|
||||
* کد حساب از settings: montzar_account_{zarin|tahesab|pargar}
|
||||
*/
|
||||
class ColleagueAccountingService
|
||||
{
|
||||
public function __construct(
|
||||
private ConfigService $config,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* بعد از پوشش سفارش روی همکار، سند را روی کد حساب همکار ثبت/صف میکند.
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function recordCoveredTrade(array $data): void
|
||||
{
|
||||
if (! $this->config->usesMontzarPricing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $data['order'] ?? null;
|
||||
if (! $order instanceof Order || ! $order->id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->orderCoveredByColleague($order, $data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$provider = $this->config->accountingProvider();
|
||||
$accountCode = $this->colleagueAccountCode($provider);
|
||||
if ($accountCode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->alreadyQueued($order, $provider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = $this->buildColleaguePayload($data, $order, $accountCode);
|
||||
|
||||
try {
|
||||
match ($provider) {
|
||||
ConfigService::ACCOUNTING_ZARIN => app(ZarinController::class)->storeTrade($payload),
|
||||
ConfigService::ACCOUNTING_TAHESAB => app(TahesabController::class)->storeTrade($payload),
|
||||
ConfigService::ACCOUNTING_PARGAR => $this->queuePargarColleagueTrade($order, $payload, $accountCode),
|
||||
default => null,
|
||||
};
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function colleagueAccountCode(string $provider): ?string
|
||||
{
|
||||
$key = match ($provider) {
|
||||
ConfigService::ACCOUNTING_ZARIN => 'montzar_account_zarin',
|
||||
ConfigService::ACCOUNTING_TAHESAB => 'montzar_account_tahesab',
|
||||
ConfigService::ACCOUNTING_PARGAR => 'montzar_account_pargar',
|
||||
default => null,
|
||||
};
|
||||
if ($key === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$code = trim($this->config->getString($key, ''));
|
||||
if ($code === '' || in_array($code, ['0', '-1'], true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function orderCoveredByColleague(Order $order, array $data): bool
|
||||
{
|
||||
if (Schema::hasColumn('orders', 'provider_id') && ! empty($order->provider_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! empty($data['provider_id'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Schema::hasColumn('orders', 'total_price_montzar')
|
||||
&& $order->total_price_montzar !== null
|
||||
&& (float) $order->total_price_montzar > 0;
|
||||
}
|
||||
|
||||
private function alreadyQueued(Order $order, string $provider): bool
|
||||
{
|
||||
return match ($provider) {
|
||||
ConfigService::ACCOUNTING_ZARIN => Zarin::query()
|
||||
->where('order_id', $order->id)
|
||||
->where('desc', 'like', '%همکار%')
|
||||
->exists(),
|
||||
ConfigService::ACCOUNTING_TAHESAB => Tahesab::query()
|
||||
->where('order_id', $order->id)
|
||||
->where('desc', 'like', '%همکار%')
|
||||
->exists(),
|
||||
ConfigService::ACCOUNTING_PARGAR => PargarSyncLog::query()
|
||||
->where('order_id', $order->id)
|
||||
->where('data', 'like', '%"colleague_leg":true%')
|
||||
->exists(),
|
||||
default => true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function buildColleaguePayload(array $data, Order $order, string $accountCode): array
|
||||
{
|
||||
$amount = abs((float) ($order->amount ?? $data['amount'] ?? 0));
|
||||
$montzarUnit = Schema::hasColumn('orders', 'total_price_montzar')
|
||||
? (float) ($order->total_price_montzar ?? 0)
|
||||
: 0.0;
|
||||
$price = $montzarUnit > 0
|
||||
? $montzarUnit
|
||||
: (float) ($order->price ?? $data['price'] ?? 0);
|
||||
$total = $amount * $price;
|
||||
$direction = (int) ($order->direction ?? $data['direction'] ?? 0);
|
||||
$action = $direction === 1 ? 'خرید' : 'فروش';
|
||||
$sharh = "{$action} از اپلیکیشن همکار";
|
||||
|
||||
$payload = $data;
|
||||
$payload['order'] = $order;
|
||||
$payload['customer_id'] = (int) ($order->customer_id ?? $data['customer_id'] ?? 0);
|
||||
$payload['amount'] = $amount;
|
||||
$payload['price'] = $price;
|
||||
$payload['total_price'] = $total;
|
||||
$payload['direction'] = $direction;
|
||||
$payload['product_id'] = $order->product_id ?? ($data['product_id'] ?? null);
|
||||
$payload['accounting_account_code'] = $accountCode;
|
||||
$payload['colleague_leg'] = true;
|
||||
$payload['sharh_override'] = $sharh;
|
||||
$payload['pay_at'] = $order->pay_at ?? $order->created_at ?? ($data['pay_at'] ?? now());
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
private function queuePargarColleagueTrade(Order $order, array $payload, string $accountCode): void
|
||||
{
|
||||
if (! $this->config->isPargarAccounting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$direction = (int) ($payload['direction'] ?? 0);
|
||||
$desc = (string) ($payload['sharh_override'] ?? 'معامله با اپلیکیشن همکار');
|
||||
|
||||
PargarSyncLog::create([
|
||||
'customer_id' => $order->customer_id,
|
||||
'amount' => $payload['amount'] ?? $order->amount,
|
||||
'desc' => $desc,
|
||||
'order_id' => $order->id,
|
||||
'date' => $order->pay_at ?? $order->created_at,
|
||||
'type' => PargarSyncService::TYPE_ORDER,
|
||||
'sync_status' => PargarSyncService::STATUS_PENDING,
|
||||
'data' => json_encode([
|
||||
'handler' => 'exportColleagueOrder',
|
||||
'colleague_leg' => true,
|
||||
'colleague_account_id' => $accountCode,
|
||||
'direction' => $direction,
|
||||
'price' => $payload['price'] ?? $order->price,
|
||||
'total_price' => $payload['total_price'] ?? $order->total_price,
|
||||
'payment_kind' => PargarSyncService::PAYMENT_CASH,
|
||||
'product_id' => $order->product_id,
|
||||
'from_cart' => false,
|
||||
], JSON_UNESCAPED_UNICODE),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -555,6 +555,31 @@ class PargarSyncService
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_ends_with(strtoupper($id), '-C')) {
|
||||
$baseId = (int) substr($id, 0, -2);
|
||||
if ($baseId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$log = PargarSyncLog::query()
|
||||
->where('order_id', $baseId)
|
||||
->where('type', self::TYPE_ORDER)
|
||||
->where('sync_status', self::STATUS_PENDING)
|
||||
->where('data', 'like', '%"colleague_leg":true%')
|
||||
->latest('id')
|
||||
->first();
|
||||
|
||||
if ($log) {
|
||||
$this->markSuccess($log, $id, ['OrderId' => $id]);
|
||||
if ($log->customer_id) {
|
||||
$customerIds[(int) $log->customer_id] = true;
|
||||
}
|
||||
$confirmed++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$order = Order::find($id);
|
||||
if (! $order) {
|
||||
continue;
|
||||
@@ -563,7 +588,13 @@ class PargarSyncService
|
||||
$order->pargar_confirmed_at = now();
|
||||
$order->save();
|
||||
|
||||
$log = PargarSyncLog::where('order_id', $order->id)->latest('id')->first();
|
||||
$log = PargarSyncLog::where('order_id', $order->id)
|
||||
->where(function ($q) {
|
||||
$q->whereNull('data')
|
||||
->orWhere('data', 'not like', '%"colleague_leg":true%');
|
||||
})
|
||||
->latest('id')
|
||||
->first();
|
||||
if ($log) {
|
||||
$this->markSuccess($log, (string) $order->id, ['OrderId' => (string) $order->id]);
|
||||
}
|
||||
|
||||
@@ -281,6 +281,21 @@ INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `de
|
||||
SELECT 'montzar_product_id', '25', 'کد محصول همکار', 1, 1, NULL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_product_id' LIMIT 1);
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
|
||||
SELECT 'montzar_account_zarin', '', 'کد حساب همکار در زرین', 1, 1,
|
||||
'کد حساب طرفحساب همکار در زرین؛ سند خرید/فروش پوشششده روی همکار روی این حساب ثبت میشود.'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_account_zarin' LIMIT 1);
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
|
||||
SELECT 'montzar_account_tahesab', '', 'کد حساب همکار در تهحساب', 1, 1,
|
||||
'کد حساب طرفحساب همکار در تهحساب؛ سند خرید/فروش پوشششده روی همکار روی این حساب ثبت میشود.'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_account_tahesab' LIMIT 1);
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
|
||||
SELECT 'montzar_account_pargar', '', 'کد حساب همکار در پرگار', 1, 1,
|
||||
'کد حساب طرفحساب همکار در پرگار؛ سند خرید/فروش پوشششده روی همکار روی این حساب ثبت میشود.'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_account_pargar' LIMIT 1);
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
|
||||
SELECT 'montzar_base_url', 'https://limangold.ir', 'آدرس سرور همکار', 1, 1, NULL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_base_url' LIMIT 1);
|
||||
@@ -1301,7 +1316,9 @@ UPDATE `settings` SET `type` = 'text' WHERE `key` IN (
|
||||
'server_url', 'key', 'main_product', 'zaarbox_api_url', 'zaarbox_price_key', 'chart_id',
|
||||
'etehadie_api_url', 'montzar_mobile', 'montzar_password', 'montzar_base_url',
|
||||
'montzar_token', 'montzar_diff', 'montzar_live_price', 'montzar_mazaneh',
|
||||
'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', 'startDay', 'endDay',
|
||||
'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh',
|
||||
'montzar_account_zarin', 'montzar_account_tahesab', 'montzar_account_pargar',
|
||||
'startDay', 'endDay',
|
||||
'payping_start_url', 'payping_verify_url', 'payping_gateway_items', 'payping_fin_url',
|
||||
'payping_fin_token', 'tahesab_host', 'tahesab_token', 'tahesab_db',
|
||||
'pargar_username', 'pargar_password', 'pargar_api_token',
|
||||
@@ -1735,6 +1752,9 @@ UPDATE `settings` SET `description` = 'روشن: تطبیق کد ملی و مو
|
||||
UPDATE `settings` SET `description` = 'شماره موبایل حساب API سرویس همکار قیمت. برای دریافت توکن و لیست محصولات.' WHERE `key` = 'montzar_mobile';
|
||||
UPDATE `settings` SET `description` = 'رمز عبور حساب API سرویس همکار قیمت. محرمانه نگه دارید.' WHERE `key` = 'montzar_password';
|
||||
UPDATE `settings` SET `description` = 'شناسه محصول آبشده در API همکار. باید با محصول مرجع در سرویس خارجی یکسان باشد.' WHERE `key` = 'montzar_product_id';
|
||||
UPDATE `settings` SET `description` = 'کد حساب طرفحساب همکار در زرین؛ سند خرید/فروش پوشششده روی همکار روی این حساب ثبت میشود.' WHERE `key` = 'montzar_account_zarin';
|
||||
UPDATE `settings` SET `description` = 'کد حساب طرفحساب همکار در تهحساب؛ سند خرید/فروش پوشششده روی همکار روی این حساب ثبت میشود.' WHERE `key` = 'montzar_account_tahesab';
|
||||
UPDATE `settings` SET `description` = 'کد حساب طرفحساب همکار در پرگار؛ سند خرید/فروش پوشششده روی همکار روی این حساب ثبت میشود.' WHERE `key` = 'montzar_account_pargar';
|
||||
-- [super-admin can_edit=0]
|
||||
-- UPDATE `settings` SET `description` = 'آدرس پایه سرور API همکار (بدون اسلش انتهایی). مثال: https://example.com' WHERE `key` = 'montzar_base_url';
|
||||
UPDATE `settings` SET `description` = 'توکن دسترسی API همکار. پس از ورود موفق توسط سیستم پر میشود؛ دستی ویرایش نکنید.' WHERE `key` = 'montzar_token';
|
||||
@@ -2060,6 +2080,9 @@ UPDATE `settings` SET `title_fa` = 'باز بودن فروش همکار' WHERE `
|
||||
UPDATE `settings` SET `title_fa` = 'پاسخ سفارش همکار' WHERE `key` = 'montzar_order_response';
|
||||
UPDATE `settings` SET `title_fa` = 'رمز API همکار' WHERE `key` = 'montzar_password';
|
||||
UPDATE `settings` SET `title_fa` = 'کد محصول همکار' WHERE `key` = 'montzar_product_id';
|
||||
UPDATE `settings` SET `title_fa` = 'کد حساب همکار در زرین' WHERE `key` = 'montzar_account_zarin';
|
||||
UPDATE `settings` SET `title_fa` = 'کد حساب همکار در تهحساب' WHERE `key` = 'montzar_account_tahesab';
|
||||
UPDATE `settings` SET `title_fa` = 'کد حساب همکار در پرگار' WHERE `key` = 'montzar_account_pargar';
|
||||
UPDATE `settings` SET `title_fa` = 'توکن همکار (خودکار)' WHERE `key` = 'montzar_token';
|
||||
UPDATE `settings` SET `title_fa` = 'مظنه خرید همکار' WHERE `key` = 'montzar_truly_buy_mazaneh';
|
||||
UPDATE `settings` SET `title_fa` = 'مظنه فروش همکار' WHERE `key` = 'montzar_truly_sell_mazaneh';
|
||||
@@ -2244,6 +2267,7 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN (
|
||||
'min_first_buy', 'min_withdraw', 'min_deposit', 'minimum_sell',
|
||||
'montzar_mobile', 'montzar_open_buy', 'montzar_open_sell',
|
||||
'montzar_password', 'montzar_product_id',
|
||||
'montzar_account_zarin', 'montzar_account_tahesab', 'montzar_account_pargar',
|
||||
'open_register',
|
||||
'referral_id', 'referral_show', 'referral_limit', 'referral_gift_on_first_order', 'referral_gift_buy_from_montzar',
|
||||
'register_jibit_verify', 'register_referral_bonus_message', 'register_referral_sms_to_referrer',
|
||||
|
||||
Reference in New Issue
Block a user