added zarin

This commit is contained in:
2026-07-23 20:24:58 +03:30
parent d8b685524b
commit 8a4a406e66
17 changed files with 2007 additions and 10 deletions
@@ -155,6 +155,16 @@ class WalletController extends Controller
}
}
if (app(ConfigService::class)->isZarinAccounting()) {
try {
$data['direction'] = $deposit ? 0 : 1;
$data['withdraw_id'] = $Withdraw->id;
app(\App\Http\Controllers\Admin\ZarinController::class)->wallet($data);
} catch (\Exception $e) {
report($e);
}
}
try {
app(PargarSyncService::class)->queueWalletDocument(
$customer,
@@ -243,6 +253,16 @@ class WalletController extends Controller
}
}
if (app(ConfigService::class)->isZarinAccounting()) {
try {
$data['direction'] = $deposit ? 1 : 0;
$data['withdraw_id'] = $Withdraw->id;
app(\App\Http\Controllers\Admin\ZarinController::class)->walletGold($data);
} catch (\Exception $e) {
report($e);
}
}
try {
app(PargarSyncService::class)->queueWalletDocument(
$customer,
@@ -408,6 +428,16 @@ class WalletController extends Controller
}
}
if (app(ConfigService::class)->isZarinAccounting()) {
try {
$data['direction'] = $deposit ? 1 : 0;
$data['withdraw_id'] = $Withdraw->id;
app(\App\Http\Controllers\Admin\ZarinController::class)->walletGold($data);
} catch (\Exception $e) {
report($e);
}
}
try {
// فقط محصول متصل به کیف اصلی → سند طلای پرگار
app(PargarSyncService::class)->queueWalletDocument(
@@ -651,6 +681,32 @@ class WalletController extends Controller
}
}
if (app(ConfigService::class)->isZarinAccounting() && isset($records['rows']) && is_array($records['rows'])) {
$withdrawIds = collect($records['rows'])->pluck('id')->filter()->values()->all();
if ($withdrawIds !== []) {
$failedByWithdraw = \App\Models\Zarin::query()
->whereIn('withdraw_id', $withdrawIds)
->where(function ($query) {
$query->whereNull('zarin_id');
if (\Illuminate\Support\Facades\Schema::hasColumn('zarin', 'sync_status')) {
$query->orWhere('sync_status', \App\Services\ZarinSyncService::STATUS_FAILED);
}
})
->orderByDesc('id')
->get()
->unique('withdraw_id')
->keyBy('withdraw_id');
foreach ($records['rows'] as &$row) {
$zarin = $failedByWithdraw->get($row['id'] ?? null);
if ($zarin) {
$row['zarin_failed_id'] = $zarin->id;
}
}
unset($row);
}
}
return response()->json($records);
}
File diff suppressed because it is too large Load Diff
@@ -277,6 +277,7 @@ class AuthController extends Controller
// claim بدون کد حسابداری — صف export مثل create
app(\App\Services\PargarSyncService::class)->registerOnCreate($customer);
app(\App\Services\TahesabSyncService::class)->registerOnCreate($customer);
app(\App\Services\ZarinSyncService::class)->registerOnCreate($customer);
}
return response()->json($customer);
@@ -384,6 +384,26 @@ class CartController extends Controller
}
}
if ($config->isZarinAccounting()) {
try {
app(\App\Http\Controllers\Admin\ZarinController::class)->storeTrade([
'customer_id' => $order->customer_id,
'amount' => $order->amount,
'direction' => (int) ($order->direction ?? 1),
'price' => $order->price,
'total_price' => $order->total_price,
'pay_at' => $order->pay_at ?? $order->created_at,
'product' => [
'metal_id' => $metalId,
'id' => $primaryProduct?->id,
],
'order' => ['id' => $order->id],
]);
} catch (\Throwable $e) {
report($e);
}
}
if ($config->isPargarAccounting()) {
try {
app(PargarSyncService::class)->recordTrade($order, true, [
@@ -411,6 +411,19 @@ class WalletController extends Controller
report($e);
}
}
if (app(ConfigService::class)->isZarinAccounting()) {
try {
app(\App\Http\Controllers\Admin\ZarinController::class)->wallet([
"customer_id" => auth('customer')->id(),
"value" => $data['value'],
"direction" => 1,
]);
} catch (\Exception $e) {
report($e);
}
}
$companyName = $this->Config('name_fa'); // Company name from configuration
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ریال را دارد. با تشکر، $companyName";
if (isset($request->otp)) {
@@ -218,6 +218,14 @@ class SingleOrderController extends Controller
}
}
if ($config->isZarinAccounting()) {
try {
$data['zarin'] = app(\App\Http\Controllers\Admin\ZarinController::class)->storeTrade($data);
} catch (\Exception $e) {
report($e);
}
}
if ($config->isPargarAccounting()) {
try {
$paymentProductId = (int) ($data['payment_product_id'] ?? 0);
+1
View File
@@ -360,6 +360,7 @@ class Product extends Model
'amount', // The amount of the record.
'unit', // The unit of the item.
'group_type', // The unit of the item.
'finance_p_name', // کد کار زرین (idKar)
'file_id', // The ID of the associated file.
'can_canceled', // Whether the record can be canceled (boolean).
'active', // The active status of the record (boolean).
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Zarin extends Model
{
use HasFactory;
protected $table = 'zarin';
protected $fillable = [
'customer_id',
'order_id',
'withdraw_id',
'amount',
'desc',
'type',
'date',
'zarin_id',
'sh_factor',
'factor_number',
'data',
'res',
'sync_status',
'sync_log',
'created_at',
'updated_at',
];
static $STS = [
1 => 'باز',
2 => 'موفق',
3 => 'ناموفق'
];
static $DIRECTION = [
1 => 'خرید',
2 => 'فروش'
];
public function customer()
{
return $this->belongsTo(Customer::class, 'customer_id', 'id');
}
}
+2 -1
View File
@@ -3,15 +3,16 @@
namespace App\Observers;
use App\Models\Customer;
use App\Services\ConfigService;
use App\Services\PargarSyncService;
use App\Services\TahesabSyncService;
use App\Services\ZarinSyncService;
class CustomerObserver
{
public function created(Customer $customer): void
{
app(TahesabSyncService::class)->registerOnCreate($customer);
app(ZarinSyncService::class)->registerOnCreate($customer);
app(PargarSyncService::class)->registerOnCreate($customer);
}
}
+8 -2
View File
@@ -728,8 +728,8 @@ class ConfigService
public const ACCOUNTING_NONE = 'none';
public const ACCOUNTING_TAHESAB = 'tahesab';
public const ACCOUNTING_PARGAR = 'pargar';
public const ACCOUNTING_ZARIN = 'zarin';
/** none | tahesab | pargar — از setting accounting_provider */
public function isConditionalOrderEnabled(): bool
{
return $this->getBool('enable_conditional_order', false);
@@ -762,10 +762,11 @@ class ConfigService
return max(0, $this->getInt('fee_conditional_order', 0));
}
/** none | tahesab | pargar | zarin — از setting accounting_provider */
public function accountingProvider(): string
{
$provider = strtolower(trim($this->getString('accounting_provider', '')));
if (in_array($provider, [self::ACCOUNTING_NONE, self::ACCOUNTING_TAHESAB, self::ACCOUNTING_PARGAR], true)) {
if (in_array($provider, [self::ACCOUNTING_NONE, self::ACCOUNTING_TAHESAB, self::ACCOUNTING_PARGAR, self::ACCOUNTING_ZARIN], true)) {
return $provider;
}
@@ -789,6 +790,11 @@ class ConfigService
return $this->accountingProvider() === self::ACCOUNTING_PARGAR;
}
public function isZarinAccounting(): bool
{
return $this->accountingProvider() === self::ACCOUNTING_ZARIN;
}
/**
* Hostname for WebOTP SMS (@domain #code) and other public links.
* Only from the current HTTP request — no settings, no .env.
+24
View File
@@ -180,6 +180,18 @@ class ReferralGiftService
}
}
if ($config->isZarinAccounting()) {
try {
app(\App\Http\Controllers\Admin\ZarinController::class)->referrerGoldReward(
$customer,
$giftForInviter,
'پاداش بابت معرفی'
);
} catch (\Exception $e) {
report($e);
}
}
$currentGold = Controller::formatSmsGoldWeight((float) $walletInterface->wallet->refresh()->gold);
$message = "سلام {$referrer->name} عزیز،\n\n{$giftForInviter} گرم طلا بابت معرفی {$customer->name} به حساب شما واریز شد.\nموجودی فعلی: {$currentGold} گرم.\n\nاطلاعات بیشتر: {$companyUrl}\n\nبا احترام،\n{$companyName}";
Controller::Send($referrer->mobile, $message);
@@ -238,6 +250,18 @@ class ReferralGiftService
}
}
if ($config->isZarinAccounting()) {
try {
app(\App\Http\Controllers\Admin\ZarinController::class)->goldReward([
'customer_id' => $customer->id,
'value' => $giftForInvited,
'desc' => 'پاداش بابت ثبت نام',
]);
} catch (\Exception $e) {
report($e);
}
}
$currentGold = Controller::formatSmsGoldWeight((float) $walletInterface->wallet->refresh()->gold);
$message = "سلام {$customer->name} عزیز،\n\n{$giftForInvited} گرم طلا بابت ثبت نام به حساب شما واریز شد.\nموجودی فعلی: {$currentGold} گرم.\n\nاطلاعات بیشتر: {$companyUrl}\n\nبا احترام،\n{$companyName}";
Controller::Send($customer->mobile, $message);
+268
View File
@@ -0,0 +1,268 @@
<?php
namespace App\Services;
use App\Http\Controllers\Admin\ZarinController;
use App\Models\Customer;
use App\Models\Zarin;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
class ZarinSyncService
{
public const STATUS_PENDING = 'pending';
public const STATUS_SUCCESS = 'success';
public const STATUS_FAILED = 'failed';
public function responseOk(mixed $response): bool
{
if (! is_array($response)) {
return false;
}
if ($this->hasNumericIdKey($response)) {
return true;
}
$data = $response['data'] ?? null;
if (! is_array($data)) {
return false;
}
$row = array_is_list($data) ? ($data[0] ?? null) : $data;
return is_array($row) && $this->hasNumericIdKey($row);
}
private function hasNumericIdKey(array $row): bool
{
foreach (['idFactor', 'idSanad', 'pkRadifSanad', 'idHesab', 'OK'] as $key) {
if (! array_key_exists($key, $row)) {
continue;
}
$value = $row[$key];
if ($value === null || $value === '' || $value === false) {
continue;
}
if (is_numeric($value) || (is_string($value) && trim($value) !== '')) {
return true;
}
}
return false;
}
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(Zarin $row, array $response, ?array $payload = null): void
{
$flat = $this->flattenResponse($response);
$zarinId = $flat['OK']
?? $flat['idFactor']
?? $flat['idSanad']
?? $flat['pkRadifSanad']
?? $flat['idHesab']
?? null;
$shFactor = $flat['Sh_factor']
?? $flat['pkRadifSanad']
?? $flat['idRadifSanad']
?? null;
$update = [
'sh_factor' => $shFactor !== null ? (string) $shFactor : null,
'zarin_id' => $zarinId !== null ? (string) $zarinId : null,
'factor_number' => isset($flat['Factor_Number']) ? (string) $flat['Factor_Number'] : (isset($flat['idFactor']) ? (string) $flat['idFactor'] : null),
'res' => json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
'sync_status' => self::STATUS_SUCCESS,
'sync_log' => null,
];
if ($payload !== null) {
$update['data'] = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
if ($this->hasSyncColumns()) {
Zarin::where('id', $row->id)->update($update);
} else {
unset($update['sync_status'], $update['sync_log']);
Zarin::where('id', $row->id)->update($update);
}
}
public function markFailed(Zarin $row, mixed $response = null, ?string $extra = null): void
{
$update = [
'res' => is_array($response)
? json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: (is_string($response) ? $response : null),
];
if ($this->hasSyncColumns()) {
$update['sync_status'] = self::STATUS_FAILED;
$update['sync_log'] = $this->buildLog($response, $extra);
}
Zarin::where('id', $row->id)->update($update);
}
public function isFailed(Zarin $row): bool
{
if ($this->hasSyncColumns()) {
return ($row->sync_status ?? null) === self::STATUS_FAILED
|| (empty($row->zarin_id) && ($row->sync_status ?? null) !== self::STATUS_SUCCESS);
}
return empty($row->zarin_id);
}
public function registerOnCreate(Customer $customer): void
{
if (! empty($customer->moshtari_code) || ! $this->isSyncEnabled()) {
return;
}
$customerId = (int) $customer->id;
dispatch(function () use ($customerId) {
$service = app(ZarinSyncService::class);
$customer = Customer::find($customerId);
if ($customer === null || ! empty($customer->moshtari_code) || ! $service->isSyncEnabled()) {
return;
}
try {
$result = $service->registerCustomer($customer);
if (! ($result['ok'] ?? false)) {
Log::warning('Zarin auto-register on customer create failed', [
'customer_id' => $customerId,
'message' => $result['message'] ?? null,
]);
}
} catch (\Throwable $e) {
Log::warning('Zarin auto-register on customer create threw', [
'customer_id' => $customerId,
'error' => $e->getMessage(),
]);
}
})->afterResponse();
}
public function registerCustomer(Customer $customer): array
{
$customer->refresh();
if (! empty($customer->moshtari_code)) {
return [
'ok' => true,
'message' => 'مشتری قبلاً در زرین ثبت شده است',
'moshtari_code' => $customer->moshtari_code,
];
}
$controller = app(ZarinController::class);
$result = $controller->createCustomerInZarin($customer->id);
if (is_string($result)) {
return ['ok' => false, 'message' => $result];
}
$customer->refresh();
if (empty($customer->moshtari_code)) {
return ['ok' => false, 'message' => 'ثبت مشتری در زرین ناموفق بود'];
}
return [
'ok' => true,
'message' => 'مشتری با موفقیت در زرین ثبت شد',
'moshtari_code' => $customer->moshtari_code,
];
}
public function retry(Zarin $row): array
{
if (! $this->isFailed($row) && ! empty($row->zarin_id)) {
return ['ok' => false, 'message' => 'این سند قبلاً در زرین ثبت شده است'];
}
$payload = json_decode((string) $row->data, true) ?: [];
$handler = $payload['handler'] ?? $this->inferHandler($row);
$controller = app(ZarinController::class);
$result = match ($handler) {
'wallet' => $controller->wallet(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
'walletGold' => $controller->walletGold(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
'storeTrade' => $controller->storeTrade(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
'goldReward' => $controller->goldReward(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
default => false,
};
$row->refresh();
if ($result !== false && ! empty($row->zarin_id)) {
return ['ok' => true, 'message' => 'سند با موفقیت در زرین ثبت شد', 'zarin' => $row];
}
return [
'ok' => false,
'message' => $row->sync_log ?: 'ارسال مجدد به زرین ناموفق بود',
'zarin' => $row,
];
}
private function inferHandler(Zarin $row): string
{
if ($row->order_id) {
return 'storeTrade';
}
if ($row->type === 'ريال') {
return 'wallet';
}
$desc = (string) $row->desc;
if (str_contains($desc, 'شارژ کیف') || str_contains($desc, 'برداشت از کیف')) {
return 'walletGold';
}
return 'goldReward';
}
public function isSyncEnabled(): bool
{
$config = app(ConfigService::class);
return $config->isZarinAccounting()
&& trim($config->getString('zarin_api', '')) !== ''
&& trim($config->getString('zarin_token', '')) !== '';
}
private function flattenResponse(array $response): array
{
$data = $response['data'] ?? null;
if (is_array($data)) {
$row = array_is_list($data) ? ($data[0] ?? []) : $data;
if (is_array($row)) {
return array_merge($response, $row);
}
}
return $response;
}
private function hasSyncColumns(): bool
{
return Schema::hasTable('zarin') && Schema::hasColumn('zarin', 'sync_status');
}
}
@@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('zarin')) {
Schema::create('zarin', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('customer_id')->nullable()->index();
$table->unsignedBigInteger('order_id')->nullable()->index();
$table->unsignedBigInteger('withdraw_id')->nullable()->index();
$table->decimal('amount', 18, 4)->nullable();
$table->string('desc')->nullable();
$table->string('type', 32)->nullable();
$table->timestamp('date')->nullable();
$table->string('zarin_id')->nullable()->index();
$table->string('sh_factor')->nullable();
$table->string('factor_number')->nullable();
$table->longText('data')->nullable();
$table->longText('res')->nullable();
$table->string('sync_status', 20)->default('pending');
$table->text('sync_log')->nullable();
$table->timestamps();
});
}
if (Schema::hasTable('products') && ! Schema::hasColumn('products', 'finance_p_name')) {
Schema::table('products', function (Blueprint $table) {
$table->string('finance_p_name')->nullable()->after('group_type');
});
}
}
public function down(): void
{
Schema::dropIfExists('zarin');
if (Schema::hasTable('products') && Schema::hasColumn('products', 'finance_p_name')) {
Schema::table('products', function (Blueprint $table) {
$table->dropColumn('finance_p_name');
});
}
}
};
@@ -0,0 +1,92 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
/**
* کلیدهای اتصال زرین — فقط از settings، can_edit=0 (مثل ته‌حساب/پرگار).
* value ردیف موجود را تغییر نمی‌دهد.
*/
return new class extends Migration
{
private const KEYS = [
'zarin_api' => [
'value' => '',
'title_fa' => 'آدرس API زرین',
'description' => 'آدرس پایه وب‌سرویس زرین (بدون /ZarrinService/accounting).',
'type' => 'text',
],
'zarin_token' => [
'value' => '',
'title_fa' => 'توکن زرین',
'description' => 'توکن مکمل API زرین که در نرم‌افزار زرین تعریف می‌شود.',
'type' => 'text',
],
'zarin_noe_kar' => [
'value' => '16',
'title_fa' => 'نوع کار پیش‌فرض زرین',
'description' => 'کد noeKar هنگام ساخت کار ساخته در زرین.',
'type' => 'number',
],
'zarin_gorohe_kar' => [
'value' => '52',
'title_fa' => 'گروه کار پیش‌فرض زرین',
'description' => 'کد gorooheKar هنگام ساخت/جستجوی کار در زرین. برای جداسازی چند پنل روی یک زرین، برای هر پنل مقدار متفاوت بگذارید.',
'type' => 'number',
],
'zarin_name_g' => [
'value' => '',
'title_fa' => 'نام گروه حساب مشتریان زرین',
'description' => 'پارامتر nameG در addhesab. اگر چند پنل یک توکن زرین دارند، برای هر پنل نام گروه متفاوت بگذارید. خالی = نام برند (name_fa).',
'type' => 'text',
],
'zarin_uid_prefix' => [
'value' => '',
'title_fa' => 'پیشوند عددی uid زرین',
'description' => 'فقط عدد. وقتی چند پنل یک زرین دارند باید متفاوت باشد تا uid تکراری نشود. مثال: 11 برای این پنل، 12 برای پنل دیگر.',
'type' => 'text',
],
];
public function up(): void
{
foreach (self::KEYS as $key => $meta) {
$exists = DB::table('settings')->where('key', $key)->exists();
if (! $exists) {
DB::table('settings')->insert([
'key' => $key,
'value' => $meta['value'],
'title_fa' => $meta['title_fa'],
'can_edit' => 0,
'is_active' => 1,
'description' => $meta['description'],
'type' => $meta['type'],
]);
continue;
}
// فقط متادیتا — value دست‌نخورده
DB::table('settings')->where('key', $key)->update([
'title_fa' => $meta['title_fa'],
'description' => $meta['description'],
'type' => $meta['type'],
'can_edit' => 0,
'is_active' => 1,
]);
}
if (DB::table('settings')->where('key', 'accounting_provider')->exists()) {
DB::table('settings')->where('key', 'accounting_provider')->update([
'title_fa' => 'سیستم حسابداری',
'type' => 'select',
'can_edit' => 0,
'description' => 'none,pargar,tahesab,zarin',
]);
}
}
public function down(): void
{
// کلیدهای حسابداری حذف نمی‌شوند
}
};
+38 -4
View File
@@ -489,9 +489,33 @@ SELECT 'send_to_tahesab', 'false', 'ارسال به ته‌حساب (قدیمی)
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'send_to_tahesab' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'accounting_provider', 'none', 'سیستم حسابداری', 0, 1, 'none,pargar,tahesab'
SELECT 'accounting_provider', 'none', 'سیستم حسابداری', 0, 1, 'none,pargar,tahesab,zarin'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'accounting_provider' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_api', '', 'آدرس API زرین', 0, 1, 'آدرس پایه وب‌سرویس زرین (بدون /ZarrinService/accounting).', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_api' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_token', '', 'توکن زرین', 0, 1, 'توکن مکمل API زرین که در نرم‌افزار زرین تعریف می‌شود.', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_token' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_noe_kar', '16', 'نوع کار پیش‌فرض زرین', 0, 1, 'کد noeKar هنگام ساخت کار ساخته در زرین.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_noe_kar' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_gorohe_kar', '52', 'گروه کار پیش‌فرض زرین', 0, 1, 'کد gorooheKar هنگام ساخت/جستجوی کار در زرین. برای جداسازی چند پنل روی یک زرین، برای هر پنل مقدار متفاوت بگذارید.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_gorohe_kar' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_name_g', '', 'نام گروه حساب مشتریان زرین', 0, 1, 'پارامتر nameG در addhesab. اگر چند پنل یک توکن زرین دارند، برای هر پنل نام گروه متفاوت بگذارید. خالی = نام برند (name_fa).', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_name_g' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_uid_prefix', '', 'پیشوند عددی uid زرین', 0, 1, 'فقط عدد. وقتی چند پنل یک زرین دارند باید متفاوت باشد تا uid تکراری نشود.', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_uid_prefix' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'send_to_pargar', 'false', 'ارسال به پرگار (قدیمی)', 0, 0, NULL
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'send_to_pargar' LIMIT 1);
@@ -903,7 +927,7 @@ UPDATE `settings` SET `type` = 'longtext' WHERE `key` IN (
);
-- select (+ گزینه‌ها در description)
UPDATE `settings` SET `type` = 'select', `description` = 'none,pargar,tahesab' WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `type` = 'select', `description` = 'none,pargar,tahesab,zarin' WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `type` = 'select', `description` = 'منبع فعال قیمت|montzar,zaarbox,main_product' WHERE `key` = 'price_provider';
UPDATE `settings` SET `type` = 'switch', `title_fa` = 'کد معرف اجباری', `description` = 'روشن = اجباری در ثبت‌نام | خاموش = اختیاری' WHERE `key` = 'referral_id';
UPDATE `settings` SET `type` = 'select', `description` = 'sepal,' WHERE `key` = 'payping_gateway_name';
@@ -1697,12 +1721,22 @@ UPDATE `settings` SET `can_edit` = 0, `is_active` = 0 WHERE `key` IN ('send_to_t
UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
'accounting_provider',
'tahesab_host', 'tahesab_port', 'tahesab_token', 'tahesab_db', 'tahesab_bank_account',
'zarin_api', 'zarin_token', 'zarin_noe_kar', 'zarin_gorohe_kar', 'zarin_name_g', 'zarin_uid_prefix',
'pargar_username', 'pargar_password', 'pargar_api_token',
'pargar_item_id_rial', 'pargar_item_id_gold'
);
UPDATE `settings` SET `title_fa` = 'سیستم حسابداری', `type` = 'select', `description` = 'none,pargar,tahesab'
UPDATE `settings` SET `can_edit` = 0, `is_active` = 1 WHERE `key` LIKE 'zarin_%';
UPDATE `settings` SET `type` = 'text', `can_edit` = 0 WHERE `key` IN ('zarin_api', 'zarin_token', 'zarin_name_g', 'zarin_uid_prefix');
UPDATE `settings` SET `type` = 'number', `can_edit` = 0 WHERE `key` IN ('zarin_noe_kar', 'zarin_gorohe_kar');
UPDATE `settings` SET `title_fa` = 'سیستم حسابداری', `type` = 'select', `description` = 'none,pargar,tahesab,zarin'
WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `description` = 'سیستم حسابداری: none=هیچکدام، pargar=API پرگار، tahesab=ارسال خودکار ته‌حساب.' WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `description` = 'سیستم حسابداری: none=هیچکدام، pargar=API پرگار، tahesab=ارسال خودکار ته‌حساب، zarin=ارسال خودکار زرین.' WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `title_fa` = 'آدرس API زرین', `description` = 'آدرس پایه وب‌سرویس زرین (بدون /ZarrinService/accounting).' WHERE `key` = 'zarin_api';
UPDATE `settings` SET `title_fa` = 'توکن زرین', `description` = 'توکن مکمل API زرین که در نرم‌افزار زرین تعریف می‌شود.' WHERE `key` = 'zarin_token';
UPDATE `settings` SET `title_fa` = 'نوع کار پیش‌فرض زرین', `type` = 'number', `description` = 'کد noeKar هنگام ساخت کار ساخته در زرین.' WHERE `key` = 'zarin_noe_kar';
UPDATE `settings` SET `title_fa` = 'گروه کار پیش‌فرض زرین', `type` = 'number', `description` = 'کد gorooheKar هنگام ساخت/جستجوی کار در زرین. برای جداسازی چند پنل روی یک زرین، برای هر پنل مقدار متفاوت بگذارید.' WHERE `key` = 'zarin_gorohe_kar';
UPDATE `settings` SET `title_fa` = 'نام گروه حساب مشتریان زرین', `type` = 'text', `description` = 'پارامتر nameG در addhesab. اگر چند پنل یک توکن زرین دارند، برای هر پنل نام گروه متفاوت بگذارید. خالی = نام برند (name_fa).' WHERE `key` = 'zarin_name_g';
UPDATE `settings` SET `title_fa` = 'پیشوند عددی uid زرین', `type` = 'text', `description` = 'فقط عدد. وقتی چند پنل یک زرین دارند باید متفاوت باشد تا uid تکراری نشود.' WHERE `key` = 'zarin_uid_prefix';
UPDATE `settings` SET `value` = 'pargar'
WHERE `key` = 'accounting_provider'
AND TRIM(COALESCE(`value`, '')) IN ('', 'none')
@@ -444,9 +444,33 @@ SELECT 'send_to_tahesab', 'false', 'ارسال به ته‌حساب (قدیمی)
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'send_to_tahesab' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'accounting_provider', 'none', 'سیستم حسابداری', 0, 1, 'none,pargar,tahesab'
SELECT 'accounting_provider', 'none', 'سیستم حسابداری', 0, 1, 'none,pargar,tahesab,zarin'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'accounting_provider' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_api', '', 'آدرس API زرین', 0, 1, 'آدرس پایه وب‌سرویس زرین (بدون /ZarrinService/accounting).', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_api' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_token', '', 'توکن زرین', 0, 1, 'توکن مکمل API زرین که در نرم‌افزار زرین تعریف می‌شود.', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_token' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_noe_kar', '16', 'نوع کار پیش‌فرض زرین', 0, 1, 'کد noeKar هنگام ساخت کار ساخته در زرین.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_noe_kar' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_gorohe_kar', '52', 'گروه کار پیش‌فرض زرین', 0, 1, 'کد gorooheKar هنگام ساخت/جستجوی کار در زرین. برای جداسازی چند پنل روی یک زرین، برای هر پنل مقدار متفاوت بگذارید.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_gorohe_kar' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_name_g', '', 'نام گروه حساب مشتریان زرین', 0, 1, 'پارامتر nameG در addhesab. اگر چند پنل یک توکن زرین دارند، برای هر پنل نام گروه متفاوت بگذارید. خالی = نام برند (name_fa).', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_name_g' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'zarin_uid_prefix', '', 'پیشوند عددی uid زرین', 0, 1, 'فقط عدد. وقتی چند پنل یک زرین دارند باید متفاوت باشد تا uid تکراری نشود.', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'zarin_uid_prefix' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'send_to_pargar', 'false', 'ارسال به پرگار (قدیمی)', 0, 0, NULL
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'send_to_pargar' LIMIT 1);
@@ -1389,11 +1413,21 @@ UPDATE `settings` SET `can_edit` = 0, `is_active` = 0 WHERE `key` IN ('send_to_t
UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
'accounting_provider',
'tahesab_host', 'tahesab_port', 'tahesab_token', 'tahesab_db', 'tahesab_bank_account',
'zarin_api', 'zarin_token', 'zarin_noe_kar', 'zarin_gorohe_kar', 'zarin_name_g', 'zarin_uid_prefix',
'pargar_username', 'pargar_password', 'pargar_api_token'
);
UPDATE `settings` SET `title_fa` = 'سیستم حسابداری', `type` = 'select', `description` = 'none,pargar,tahesab'
UPDATE `settings` SET `can_edit` = 0, `is_active` = 1 WHERE `key` LIKE 'zarin_%';
UPDATE `settings` SET `type` = 'text', `can_edit` = 0 WHERE `key` IN ('zarin_api', 'zarin_token', 'zarin_name_g', 'zarin_uid_prefix');
UPDATE `settings` SET `type` = 'number', `can_edit` = 0 WHERE `key` IN ('zarin_noe_kar', 'zarin_gorohe_kar');
UPDATE `settings` SET `title_fa` = 'سیستم حسابداری', `type` = 'select', `description` = 'none,pargar,tahesab,zarin'
WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `description` = 'سیستم حسابداری: none=هیچکدام، pargar=API پرگار، tahesab=ارسال خودکار ته‌حساب.' WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `description` = 'سیستم حسابداری: none=هیچکدام، pargar=API پرگار، tahesab=ارسال خودکار ته‌حساب، zarin=ارسال خودکار زرین.' WHERE `key` = 'accounting_provider';
UPDATE `settings` SET `title_fa` = 'آدرس API زرین', `description` = 'آدرس پایه وب‌سرویس زرین (بدون /ZarrinService/accounting).' WHERE `key` = 'zarin_api';
UPDATE `settings` SET `title_fa` = 'توکن زرین', `description` = 'توکن مکمل API زرین که در نرم‌افزار زرین تعریف می‌شود.' WHERE `key` = 'zarin_token';
UPDATE `settings` SET `title_fa` = 'نوع کار پیش‌فرض زرین', `type` = 'number', `description` = 'کد noeKar هنگام ساخت کار ساخته در زرین.' WHERE `key` = 'zarin_noe_kar';
UPDATE `settings` SET `title_fa` = 'گروه کار پیش‌فرض زرین', `type` = 'number', `description` = 'کد gorooheKar هنگام ساخت/جستجوی کار در زرین. برای جداسازی چند پنل روی یک زرین، برای هر پنل مقدار متفاوت بگذارید.' WHERE `key` = 'zarin_gorohe_kar';
UPDATE `settings` SET `title_fa` = 'نام گروه حساب مشتریان زرین', `type` = 'text', `description` = 'پارامتر nameG در addhesab. اگر چند پنل یک توکن زرین دارند، برای هر پنل نام گروه متفاوت بگذارید. خالی = نام برند (name_fa).' WHERE `key` = 'zarin_name_g';
UPDATE `settings` SET `title_fa` = 'پیشوند عددی uid زرین', `type` = 'text', `description` = 'فقط عدد. وقتی چند پنل یک زرین دارند باید متفاوت باشد تا uid تکراری نشود.' WHERE `key` = 'zarin_uid_prefix';
UPDATE `settings` SET `value` = 'pargar'
WHERE `key` = 'accounting_provider'
AND TRIM(COALESCE(`value`, '')) IN ('', 'none')
+11
View File
@@ -15,6 +15,7 @@ use App\Http\Controllers\Admin\PriceTriggerController as AdminPriceTriggerContro
use App\Http\Controllers\Finance\FinanceController;
use App\Http\Controllers\Admin\ProductController;
use App\Http\Controllers\Admin\TahesabController;
use App\Http\Controllers\Admin\ZarinController;
use App\Http\Controllers\Admin\PargarController;
use App\Http\Controllers\Admin\FileController;
use App\Http\Controllers\Site\OfferCodesController;
@@ -188,6 +189,16 @@ Route::middleware('auth:user')->group(function (){
Route::post('tahesab/{id}/retry', [TahesabController::class, 'retrySanad']);
Route::post('customer/{id}/tahesab/register', [TahesabController::class, 'registerCustomer']);
Route::post('wallet/zarin', [ZarinController::class, 'wallet']);
Route::post('zarin/table', [ZarinController::class, 'table']);
Route::get('zarin/customer-sync/status', [ZarinController::class, 'customerSyncStatus']);
Route::post('zarin/customer-sync/panel-to-zarin', [ZarinController::class, 'syncPanelToZarin']);
Route::post('zarin/customer-sync/import-from-zarin', [ZarinController::class, 'importFromZarin']);
Route::post('zarin/customer-sync/rollback-import', [ZarinController::class, 'rollbackImport']);
Route::get('zarin/{id}', [ZarinController::class, 'show']);
Route::post('zarin/{id}/retry', [ZarinController::class, 'retrySanad']);
Route::post('customer/{id}/zarin/register', [ZarinController::class, 'registerCustomer']);
Route::post('pargar/table', [PargarController::class, 'table']);
Route::get('pargar/customer-sync/status', [PargarController::class, 'customerSyncStatus']);
Route::post('pargar/customer-sync/panel-to-pargar', [PargarController::class, 'syncPanelToPargar']);