feat(tax): implement tax rate history tracking and API endpoints
This commit is contained in:
@@ -5,18 +5,21 @@ import { z } from 'zod';
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { api } from '../lib/api';
|
import { api } from '../lib/api';
|
||||||
import type { ApiResponse } from '../lib/api';
|
import type { ApiResponse } from '../lib/api';
|
||||||
|
import { formatDateTime } from '../lib/utils';
|
||||||
import { Cog6ToothIcon, CheckCircleIcon } from '@heroicons/react/24/outline';
|
import { Cog6ToothIcon, CheckCircleIcon } from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
|
interface TaxHistoryRow {
|
||||||
|
tax_percent: number;
|
||||||
|
enabled: boolean;
|
||||||
|
changed_by_name: string | null;
|
||||||
|
changed_at: number;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Schema ────────────────────────────────────────────────────────────────
|
// ── Schema ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
site_name: z.string().min(1, 'نام سایت الزامی است'),
|
site_name: z.string().min(1, 'نام سایت الزامی است'),
|
||||||
support_phone: z.string(),
|
support_phone: z.string(),
|
||||||
commission_enabled: z.string(),
|
|
||||||
commission_percent: z.string().refine(v => {
|
|
||||||
const n = Number(v);
|
|
||||||
return !isNaN(n) && n >= 0 && n <= 100;
|
|
||||||
}, 'درصد باید بین ۰ تا ۱۰۰ باشد'),
|
|
||||||
max_cancel_hours_before: z.string(),
|
max_cancel_hours_before: z.string(),
|
||||||
appointment_reminder_hours: z.string(),
|
appointment_reminder_hours: z.string(),
|
||||||
// financial engine
|
// financial engine
|
||||||
@@ -44,8 +47,6 @@ type FormValues = z.infer<typeof schema>;
|
|||||||
interface Settings {
|
interface Settings {
|
||||||
site_name: string;
|
site_name: string;
|
||||||
support_phone: string;
|
support_phone: string;
|
||||||
commission_enabled: string;
|
|
||||||
commission_percent: string;
|
|
||||||
max_cancel_hours_before: string;
|
max_cancel_hours_before: string;
|
||||||
appointment_reminder_hours: string;
|
appointment_reminder_hours: string;
|
||||||
appointment_commission_enabled: string;
|
appointment_commission_enabled: string;
|
||||||
@@ -81,6 +82,14 @@ export default function SettingsPage() {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const settings: Settings | undefined = (data?.data as any)?.data ?? data?.data;
|
const settings: Settings | undefined = (data?.data as any)?.data ?? data?.data;
|
||||||
|
|
||||||
|
const taxHistoryQ = useQuery({
|
||||||
|
queryKey: ['tax-history'],
|
||||||
|
queryFn: () => api.get<ApiResponse<TaxHistoryRow[]>>('/api/v1/admin/settings/tax-history'),
|
||||||
|
staleTime: 30_000,
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const taxHistory: TaxHistoryRow[] = (taxHistoryQ.data?.data as any)?.data ?? taxHistoryQ.data?.data ?? [];
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -95,8 +104,6 @@ export default function SettingsPage() {
|
|||||||
reset({
|
reset({
|
||||||
site_name: settings.site_name ?? 'ClinicPro',
|
site_name: settings.site_name ?? 'ClinicPro',
|
||||||
support_phone: settings.support_phone ?? '',
|
support_phone: settings.support_phone ?? '',
|
||||||
commission_enabled: settings.commission_enabled ?? '0',
|
|
||||||
commission_percent: settings.commission_percent ?? '0',
|
|
||||||
max_cancel_hours_before: settings.max_cancel_hours_before ?? '24',
|
max_cancel_hours_before: settings.max_cancel_hours_before ?? '24',
|
||||||
appointment_reminder_hours: settings.appointment_reminder_hours ?? '2',
|
appointment_reminder_hours: settings.appointment_reminder_hours ?? '2',
|
||||||
appointment_commission_enabled: settings.appointment_commission_enabled ?? '0',
|
appointment_commission_enabled: settings.appointment_commission_enabled ?? '0',
|
||||||
@@ -123,10 +130,10 @@ export default function SettingsPage() {
|
|||||||
api.patch<ApiResponse<Settings>>('/api/v1/admin/settings', values),
|
api.patch<ApiResponse<Settings>>('/api/v1/admin/settings', values),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
qc.invalidateQueries({ queryKey: ['admin-settings'] });
|
qc.invalidateQueries({ queryKey: ['admin-settings'] });
|
||||||
|
qc.invalidateQueries({ queryKey: ['tax-history'] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const commissionEnabled = watch('commission_enabled') === '1';
|
|
||||||
const paymentTestMode = watch('payment_test_mode') === '1';
|
const paymentTestMode = watch('payment_test_mode') === '1';
|
||||||
const apptCommissionEnabled = watch('appointment_commission_enabled') === '1';
|
const apptCommissionEnabled = watch('appointment_commission_enabled') === '1';
|
||||||
const upgradeCommissionEnabled = watch('upgrade_commission_enabled') === '1';
|
const upgradeCommissionEnabled = watch('upgrade_commission_enabled') === '1';
|
||||||
@@ -210,71 +217,6 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* تنظیمات کمیسیون */}
|
|
||||||
<div className="card card-pad">
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: '1.25rem' }}>
|
|
||||||
<div className="ico" style={{ background: 'var(--success-bg)', color: 'var(--success)', width: 36, height: 36, borderRadius: 10 }}>
|
|
||||||
<span style={{ fontSize: 18 }}>٪</span>
|
|
||||||
</div>
|
|
||||||
<h3 style={{ fontSize: 15 }}>کمیسیون سایت</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
|
||||||
{/* toggle فعال/غیرفعال */}
|
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer' }}>
|
|
||||||
<input type="hidden" {...register('commission_enabled')} />
|
|
||||||
<div
|
|
||||||
style={{ position: 'relative', width: 44, height: 24, flexShrink: 0, cursor: 'pointer' }}
|
|
||||||
onClick={() => setValue('commission_enabled', commissionEnabled ? '0' : '1', { shouldDirty: true })}
|
|
||||||
>
|
|
||||||
<div style={{
|
|
||||||
width: 44, height: 24, borderRadius: 12,
|
|
||||||
background: commissionEnabled ? 'var(--primary)' : 'var(--border)',
|
|
||||||
transition: 'background .2s', position: 'relative',
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
position: 'absolute', top: 2, width: 20, height: 20, borderRadius: '50%', background: 'var(--surface)',
|
|
||||||
transition: 'right .2s', right: commissionEnabled ? 2 : 22,
|
|
||||||
boxShadow: '0 1px 3px rgba(0,0,0,.2)',
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontSize: 14 }}>
|
|
||||||
{commissionEnabled ? 'کمیسیون فعال است' : 'کمیسیون غیرفعال است'}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{commissionEnabled && (
|
|
||||||
<div style={{ maxWidth: 280 }}>
|
|
||||||
<label style={{ fontSize: 13, fontWeight: 500, display: 'block', marginBottom: 6 }}>
|
|
||||||
درصد کمیسیون از کاربر (۰–۱۰۰)
|
|
||||||
</label>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
||||||
<input
|
|
||||||
{...register('commission_percent')}
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
max={100}
|
|
||||||
style={{
|
|
||||||
width: 120, height: 38, padding: '0 12px', borderRadius: 'var(--r-sm)',
|
|
||||||
border: `1px solid ${errors.commission_percent ? 'var(--danger)' : 'var(--border)'}`,
|
|
||||||
background: 'var(--surface)', color: 'var(--text)', fontSize: 13.5,
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="muted" style={{ fontSize: 13 }}>درصد</span>
|
|
||||||
</div>
|
|
||||||
{errors.commission_percent && (
|
|
||||||
<p style={{ color: 'var(--danger)', fontSize: 12, marginTop: 4 }}>{errors.commission_percent.message}</p>
|
|
||||||
)}
|
|
||||||
<p className="muted" style={{ fontSize: 12, marginTop: 8 }}>
|
|
||||||
کمیسیون فقط از کاربر دریافت میشود. منشیها کمیسیون ندارند.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* موتور مالی نمایندگی */}
|
{/* موتور مالی نمایندگی */}
|
||||||
<div className="card card-pad">
|
<div className="card card-pad">
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: '1.25rem' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: '1.25rem' }}>
|
||||||
@@ -352,6 +294,23 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{taxHistory.length > 0 && (
|
||||||
|
<div style={{ marginTop: 14 }}>
|
||||||
|
<div style={{ fontSize: 12.5, fontWeight: 600, marginBottom: 8 }}>تاریخچه تغییرات مالیات</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxWidth: 460 }}>
|
||||||
|
{taxHistory.map((h, i) => (
|
||||||
|
<div key={i} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, fontSize: 12.5, padding: '6px 10px', borderRadius: 'var(--r-sm)', background: 'var(--surface-3)' }}>
|
||||||
|
<span>
|
||||||
|
{h.enabled ? `${h.tax_percent}٪` : 'غیرفعال'}
|
||||||
|
{h.changed_by_name && <span className="muted"> — {h.changed_by_name}</span>}
|
||||||
|
</span>
|
||||||
|
<span className="muted">{formatDateTime(h.changed_at)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* هزینه پنل پیامک */}
|
{/* هزینه پنل پیامک */}
|
||||||
@@ -555,8 +514,6 @@ export default function SettingsPage() {
|
|||||||
onClick={() => settings && reset({
|
onClick={() => settings && reset({
|
||||||
site_name: settings.site_name,
|
site_name: settings.site_name,
|
||||||
support_phone: settings.support_phone,
|
support_phone: settings.support_phone,
|
||||||
commission_enabled: settings.commission_enabled,
|
|
||||||
commission_percent: settings.commission_percent,
|
|
||||||
max_cancel_hours_before: settings.max_cancel_hours_before,
|
max_cancel_hours_before: settings.max_cancel_hours_before,
|
||||||
appointment_reminder_hours: settings.appointment_reminder_hours,
|
appointment_reminder_hours: settings.appointment_reminder_hours,
|
||||||
appointment_commission_enabled: settings.appointment_commission_enabled,
|
appointment_commission_enabled: settings.appointment_commission_enabled,
|
||||||
|
|||||||
@@ -984,6 +984,8 @@ Reject a pending request. **Permission:** `ROLE_ADMIN`
|
|||||||
|
|
||||||
**ترتیب محاسبه** (در `CommissionService`): ۱) کسر `sms_panel_fee_rials` ۲) مالیاتِ استخراجی `afterSms × tax/(100+tax)` ۳) پورسانت = `netAfterTax × percent/100`. سهم نماینده به کیفپولش (`WalletTransaction` credit) واریز و یک ردیف `FinancialBreakdown` ثبت میشود (idempotent بر اساس `payment_id`).
|
**ترتیب محاسبه** (در `CommissionService`): ۱) کسر `sms_panel_fee_rials` ۲) مالیاتِ استخراجی `afterSms × tax/(100+tax)` ۳) پورسانت = `netAfterTax × percent/100`. سهم نماینده به کیفپولش (`WalletTransaction` credit) واریز و یک ردیف `FinancialBreakdown` ثبت میشود (idempotent بر اساس `payment_id`).
|
||||||
|
|
||||||
|
> هر تغییر `tax_percent`/`tax_enabled` در یک ردیف `TaxRateHistory` ثبت و از `GET /api/v1/admin/settings/tax-history` قابل مشاهده است.
|
||||||
|
|
||||||
### GET `/api/v1/admin/financial-breakdowns`
|
### GET `/api/v1/admin/financial-breakdowns`
|
||||||
|
|
||||||
لیست تفکیک مالی تراکنشها (paginated). **Permission:** `ROLE_ADMIN`
|
لیست تفکیک مالی تراکنشها (paginated). **Permission:** `ROLE_ADMIN`
|
||||||
@@ -1035,3 +1037,17 @@ Reject a pending request. **Permission:** `ROLE_ADMIN`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### GET `/api/v1/admin/settings/tax-history`
|
||||||
|
|
||||||
|
تاریخچهی تغییرات مالیات بر ارزش افزوده (۵۰ ردیف آخر، نزولی). هر بار که `tax_percent` یا `tax_enabled` از طریق `PATCH /api/v1/admin/settings` تغییر کند، یک ردیف با کاربرِ تغییردهنده ثبت میشود. **Permission:** `ROLE_ADMIN`
|
||||||
|
|
||||||
|
**Response `200`:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{ "tax_percent": 10, "enabled": true, "changed_by_name": "مدیر سیستم", "changed_at": 1782800000 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20260624093738 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE TABLE tax_rate_history (id INT AUTO_INCREMENT NOT NULL, tax_percent NUMERIC(5, 2) NOT NULL, enabled TINYINT NOT NULL, changed_at INT NOT NULL, changed_by INT DEFAULT NULL, INDEX IDX_6D489C3210BC6D9F (changed_by), INDEX idx_tax_history_date (changed_at), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
|
||||||
|
$this->addSql('ALTER TABLE tax_rate_history ADD CONSTRAINT FK_6D489C3210BC6D9F FOREIGN KEY (changed_by) REFERENCES users (id) ON DELETE SET NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE tax_rate_history DROP FOREIGN KEY FK_6D489C3210BC6D9F');
|
||||||
|
$this->addSql('DROP TABLE tax_rate_history');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,8 +16,6 @@ use OpenApi\Attributes as OA;
|
|||||||
class SiteConfigController extends BaseController
|
class SiteConfigController extends BaseController
|
||||||
{
|
{
|
||||||
private const ALLOWED_KEYS = [
|
private const ALLOWED_KEYS = [
|
||||||
'commission_enabled',
|
|
||||||
'commission_percent',
|
|
||||||
// financial engine
|
// financial engine
|
||||||
'appointment_commission_enabled',
|
'appointment_commission_enabled',
|
||||||
'upgrade_commission_enabled',
|
'upgrade_commission_enabled',
|
||||||
@@ -47,6 +45,7 @@ class SiteConfigController extends BaseController
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly SiteConfigRepository $configRepo,
|
private readonly SiteConfigRepository $configRepo,
|
||||||
|
private readonly \App\Config\Repository\TaxRateHistoryRepository $taxHistoryRepo,
|
||||||
private readonly EntityManagerInterface $em,
|
private readonly EntityManagerInterface $em,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -57,10 +56,14 @@ class SiteConfigController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/api/v1/admin/settings', methods: ['PATCH'])]
|
#[Route('/api/v1/admin/settings', methods: ['PATCH'])]
|
||||||
public function patch(Request $request): JsonResponse
|
public function patch(Request $request, #[\Symfony\Component\Security\Http\Attribute\CurrentUser] \App\Auth\Entity\User $admin): JsonResponse
|
||||||
{
|
{
|
||||||
$data = json_decode($request->getContent(), true) ?? [];
|
$data = json_decode($request->getContent(), true) ?? [];
|
||||||
|
|
||||||
|
// مقادیر فعلی مالیات برای تشخیص تغییر و ثبت تاریخچه.
|
||||||
|
$prevTaxPercent = $this->configRepo->get('tax_percent');
|
||||||
|
$prevTaxEnabled = $this->configRepo->get('tax_enabled');
|
||||||
|
|
||||||
foreach ($data as $key => $value) {
|
foreach ($data as $key => $value) {
|
||||||
if (!in_array($key, self::ALLOWED_KEYS, true)) {
|
if (!in_array($key, self::ALLOWED_KEYS, true)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -70,6 +73,28 @@ class SiteConfigController extends BaseController
|
|||||||
|
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
|
||||||
|
$newTaxPercent = $this->configRepo->get('tax_percent');
|
||||||
|
$newTaxEnabled = $this->configRepo->get('tax_enabled');
|
||||||
|
|
||||||
|
if ($newTaxPercent !== $prevTaxPercent || $newTaxEnabled !== $prevTaxEnabled) {
|
||||||
|
$this->taxHistoryRepo->save(new \App\Config\Entity\TaxRateHistory(
|
||||||
|
(string) $newTaxPercent,
|
||||||
|
$newTaxEnabled === '1',
|
||||||
|
$admin,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return $this->success($this->configRepo->getAll());
|
return $this->success($this->configRepo->getAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/v1/admin/settings/tax-history', methods: ['GET'])]
|
||||||
|
public function taxHistory(): JsonResponse
|
||||||
|
{
|
||||||
|
$rows = $this->taxHistoryRepo->findBy([], ['changedAt' => 'DESC'], 50);
|
||||||
|
|
||||||
|
return $this->success(array_map(
|
||||||
|
fn(\App\Config\Entity\TaxRateHistory $h) => $h->toArray(),
|
||||||
|
$rows,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Config\Entity;
|
||||||
|
|
||||||
|
use App\Auth\Entity\User;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
#[ORM\Entity]
|
||||||
|
#[ORM\Table(name: 'tax_rate_history')]
|
||||||
|
#[ORM\Index(columns: ['changed_at'], name: 'idx_tax_history_date')]
|
||||||
|
class TaxRateHistory
|
||||||
|
{
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\GeneratedValue]
|
||||||
|
#[ORM\Column(type: 'integer')]
|
||||||
|
private ?int $id = null;
|
||||||
|
|
||||||
|
#[ORM\Column(name: 'tax_percent', type: 'decimal', precision: 5, scale: 2)]
|
||||||
|
private string $taxPercent;
|
||||||
|
|
||||||
|
#[ORM\Column(name: 'enabled', type: 'boolean')]
|
||||||
|
private bool $enabled;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||||
|
#[ORM\JoinColumn(name: 'changed_by', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
|
||||||
|
private ?User $changedBy = null;
|
||||||
|
|
||||||
|
#[ORM\Column(name: 'changed_at', type: 'integer')]
|
||||||
|
private int $changedAt;
|
||||||
|
|
||||||
|
public function __construct(string $taxPercent, bool $enabled, ?User $changedBy)
|
||||||
|
{
|
||||||
|
$this->taxPercent = $taxPercent;
|
||||||
|
$this->enabled = $enabled;
|
||||||
|
$this->changedBy = $changedBy;
|
||||||
|
$this->changedAt = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int { return $this->id; }
|
||||||
|
public function getTaxPercent(): string { return $this->taxPercent; }
|
||||||
|
public function isEnabled(): bool { return $this->enabled; }
|
||||||
|
public function getChangedBy(): ?User { return $this->changedBy; }
|
||||||
|
public function getChangedAt(): int { return $this->changedAt; }
|
||||||
|
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'tax_percent' => (float) $this->taxPercent,
|
||||||
|
'enabled' => $this->enabled,
|
||||||
|
'changed_by_name' => $this->changedBy?->getRealName(),
|
||||||
|
'changed_at' => $this->changedAt,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,6 @@ class SiteConfigRepository extends ServiceEntityRepository
|
|||||||
{
|
{
|
||||||
// Default values returned when a key is missing from DB
|
// Default values returned when a key is missing from DB
|
||||||
private const DEFAULTS = [
|
private const DEFAULTS = [
|
||||||
'commission_enabled' => '0',
|
|
||||||
'commission_percent' => '0',
|
|
||||||
// financial engine
|
// financial engine
|
||||||
'appointment_commission_enabled' => '0',
|
'appointment_commission_enabled' => '0',
|
||||||
'upgrade_commission_enabled' => '0',
|
'upgrade_commission_enabled' => '0',
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Config\Repository;
|
||||||
|
|
||||||
|
use App\Config\Entity\TaxRateHistory;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
class TaxRateHistoryRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, TaxRateHistory::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save(TaxRateHistory $entity, bool $flush = true): void
|
||||||
|
{
|
||||||
|
$this->getEntityManager()->persist($entity);
|
||||||
|
if ($flush) $this->getEntityManager()->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user