Files
boursecu_server/app/Services/ColleagueAccountingService.php
mohmdsdgandCursor 885ed33e6e 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>
2026-08-26 16:59:27 +03:30

192 lines
6.6 KiB
PHP

<?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),
]);
}
}