update Warranty & ImportWorkflow plugins
This commit is contained in:
parent
2dde89e03c
commit
91d2558893
47
hesabixCore/migrations/Version20250820090839.php
Normal file
47
hesabixCore/migrations/Version20250820090839.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?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 Version20250820090839 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(<<<'SQL'
|
||||
ALTER TABLE business DROP invoice_approver, DROP warehouse_approver, DROP financial_approver
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE hesabdari_doc CHANGE is_preview is_preview TINYINT(1) DEFAULT 0, CHANGE is_approved is_approved TINYINT(1) DEFAULT 1
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE import_workflow DROP total_amount, DROP total_amount_irr
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE business ADD invoice_approver VARCHAR(255) DEFAULT NULL, ADD warehouse_approver VARCHAR(255) DEFAULT NULL, ADD financial_approver VARCHAR(255) DEFAULT NULL
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE hesabdari_doc CHANGE is_preview is_preview TINYINT(1) DEFAULT NULL, CHANGE is_approved is_approved TINYINT(1) DEFAULT NULL
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE import_workflow ADD total_amount VARCHAR(255) DEFAULT NULL, ADD total_amount_irr VARCHAR(255) DEFAULT NULL
|
||||
SQL);
|
||||
}
|
||||
}
|
47
hesabixCore/migrations/Version20250820104158.php
Normal file
47
hesabixCore/migrations/Version20250820104158.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?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 Version20250820104158 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(<<<'SQL'
|
||||
ALTER TABLE business DROP invoice_approver, DROP warehouse_approver, DROP financial_approver
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE hesabdari_doc CHANGE is_preview is_preview TINYINT(1) DEFAULT 0, CHANGE is_approved is_approved TINYINT(1) DEFAULT 1
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE import_workflow DROP total_amount, DROP total_amount_irr
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE business ADD invoice_approver VARCHAR(255) DEFAULT NULL, ADD warehouse_approver VARCHAR(255) DEFAULT NULL, ADD financial_approver VARCHAR(255) DEFAULT NULL
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE hesabdari_doc CHANGE is_preview is_preview TINYINT(1) DEFAULT NULL, CHANGE is_approved is_approved TINYINT(1) DEFAULT NULL
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE import_workflow ADD total_amount VARCHAR(255) DEFAULT NULL, ADD total_amount_irr VARCHAR(255) DEFAULT NULL
|
||||
SQL);
|
||||
}
|
||||
}
|
63
hesabixCore/migrations/Version20250820174027.php
Normal file
63
hesabixCore/migrations/Version20250820174027.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?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 Version20250820174027 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Change monetary fields from VARCHAR to DECIMAL to support decimal currency amounts';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// ImportWorkflow table - exchange rate
|
||||
$this->addSql('ALTER TABLE import_workflow MODIFY exchange_rate DECIMAL(15,2) DEFAULT NULL');
|
||||
|
||||
// ImportWorkflowItem table - monetary fields
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY unit_price DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY unit_price_irr DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY total_price DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY total_price_irr DECIMAL(15,2) DEFAULT NULL');
|
||||
|
||||
// ImportWorkflowPayment table - monetary fields
|
||||
$this->addSql('ALTER TABLE import_workflow_payment MODIFY amount DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_payment MODIFY amount_irr DECIMAL(15,2) DEFAULT NULL');
|
||||
|
||||
// ImportWorkflowCustoms table - monetary fields
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY customs_duty DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY value_added_tax DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY other_charges DECIMAL(15,2) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY total_customs_charges DECIMAL(15,2) DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// ImportWorkflow table - exchange rate
|
||||
$this->addSql('ALTER TABLE import_workflow MODIFY exchange_rate VARCHAR(255) DEFAULT NULL');
|
||||
|
||||
// ImportWorkflowItem table - monetary fields
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY unit_price VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY unit_price_irr VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY total_price VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_item MODIFY total_price_irr VARCHAR(255) DEFAULT NULL');
|
||||
|
||||
// ImportWorkflowPayment table - monetary fields
|
||||
$this->addSql('ALTER TABLE import_workflow_payment MODIFY amount VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_payment MODIFY amount_irr VARCHAR(255) DEFAULT NULL');
|
||||
|
||||
// ImportWorkflowCustoms table - monetary fields
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY customs_duty VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY value_added_tax VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY other_charges VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE import_workflow_customs MODIFY total_customs_charges VARCHAR(255) DEFAULT NULL');
|
||||
}
|
||||
}
|
|
@ -360,6 +360,9 @@ class AdminController extends AbstractController
|
|||
'passChequeInput' => $registryMGR->get('sms', 'plugAccproPassChequeInput'),
|
||||
'rejectChequeInput' => $registryMGR->get('sms', 'plugAccproRejectChequeInput')
|
||||
];
|
||||
$resp['plugWarranty'] = [
|
||||
'sendSerial' => $registryMGR->get('sms', 'plugWarrantySendSerial'),
|
||||
];
|
||||
return $this->json($resp);
|
||||
}
|
||||
|
||||
|
@ -431,7 +434,10 @@ class AdminController extends AbstractController
|
|||
if (array_key_exists('rejectChequeInput', $params['plugAccpro']))
|
||||
$registryMGR->update('sms', 'plugAccproRejectChequeInput', $params['plugAccpro']['rejectChequeInput'] ?? '');
|
||||
}
|
||||
|
||||
if (array_key_exists('plugWarranty', $params)) {
|
||||
if (array_key_exists('sendSerial', $params['plugWarranty']))
|
||||
$registryMGR->update('sms', 'plugWarrantySendSerial', $params['plugWarranty']['sendSerial'] ?? '');
|
||||
}
|
||||
return $this->json(JsonResp::success());
|
||||
}
|
||||
|
||||
|
|
|
@ -653,22 +653,23 @@ class ApprovalController extends AbstractController
|
|||
}
|
||||
|
||||
$approversMap = [
|
||||
'sell' => 'getApproverSellInvoice',
|
||||
'buy' => 'getApproverBuyInvoice',
|
||||
'storeroom' => 'getApproverWarehouseTransfer',
|
||||
'rfsell' => 'getApproverReturnSell',
|
||||
'rfbuy' => 'getApproverReturnBuy',
|
||||
'sell_receive' => 'getApproverReceiveFromPersons',
|
||||
'buy_send' => 'getApproverPayToPersons',
|
||||
'hesabdari' => 'getApproverAccountingDocs',
|
||||
'transfer' => 'getApproverBankTransfers',
|
||||
'getApproverSellInvoice' => ['sell'],
|
||||
'getApproverBuyInvoice' => ['buy'],
|
||||
'getApproverWarehouseTransfer' => ['storeroom'],
|
||||
'getApproverReturnSell' => ['rfsell'],
|
||||
'getApproverReturnBuy' => ['rfbuy'],
|
||||
'getApproverReceiveFromPersons' => ['person_receive'],
|
||||
'getApproverPayToPersons' => ['person_send'],
|
||||
'getApproverAccountingDocs' => ['calc'],
|
||||
'getApproverBankTransfers' => ['transfer'],
|
||||
];
|
||||
|
||||
if (!isset($approversMap[$documentType])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$method = $approversMap[$documentType];
|
||||
foreach ($approversMap as $method => $types) {
|
||||
if (in_array($documentType, $types, true)) {
|
||||
return $business->$method() === $user->getEmail();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ class PlugImportWorkflowController extends AbstractController
|
|||
'status' => $workflow->getStatus(),
|
||||
'dateSubmit' => $workflow->getDateSubmit(),
|
||||
'supplierName' => $workflow->getSupplierName(),
|
||||
'totalAmount' => $workflow->getTotalAmount(),
|
||||
'totalAmount' => $workflow->getComputedTotalAmount(),
|
||||
'currency' => $workflow->getCurrency(),
|
||||
'submitter' => $workflow->getSubmitter()->getFullName()
|
||||
];
|
||||
|
@ -137,10 +137,8 @@ class PlugImportWorkflowController extends AbstractController
|
|||
$workflow->setSupplierAddress($data['supplierAddress'] ?? '');
|
||||
$workflow->setSupplierPhone($data['supplierPhone'] ?? '');
|
||||
$workflow->setSupplierEmail($data['supplierEmail'] ?? '');
|
||||
$workflow->setTotalAmount($data['totalAmount'] ?? '');
|
||||
$workflow->setCurrency($data['currency'] ?? '');
|
||||
$workflow->setExchangeRate($data['exchangeRate'] ?? '');
|
||||
$workflow->setTotalAmountIRR($data['totalAmountIRR'] ?? '');
|
||||
$workflow->setExchangeRate(isset($data['exchangeRate']) && $data['exchangeRate'] !== '' ? $data['exchangeRate'] : null);
|
||||
$workflow->setStatus('draft');
|
||||
|
||||
$entityManager->persist($workflow);
|
||||
|
@ -242,10 +240,8 @@ class PlugImportWorkflowController extends AbstractController
|
|||
'supplierAddress' => $workflow->getSupplierAddress(),
|
||||
'supplierPhone' => $workflow->getSupplierPhone(),
|
||||
'supplierEmail' => $workflow->getSupplierEmail(),
|
||||
'totalAmount' => $workflow->getTotalAmount(),
|
||||
'currency' => $workflow->getCurrency(),
|
||||
'exchangeRate' => $workflow->getExchangeRate(),
|
||||
'totalAmountIRR' => $workflow->getTotalAmountIRR(),
|
||||
'submitter' => $workflow->getSubmitter()->getFullName(),
|
||||
'items' => [],
|
||||
'payments' => [],
|
||||
|
@ -632,10 +628,10 @@ class PlugImportWorkflowController extends AbstractController
|
|||
$c->setDeclarationNumber($data['declarationNumber'] ?? '');
|
||||
$c->setCustomsCode($data['customsCode'] ?? null);
|
||||
$c->setClearanceDate(isset($data['clearanceDate']) ? ($this->jalaliToGregorian($data['clearanceDate']) ?? null) : null);
|
||||
$c->setCustomsDuty(isset($data['customsDuty']) ? (string)$data['customsDuty'] : null);
|
||||
$c->setValueAddedTax(isset($data['valueAddedTax']) ? (string)$data['valueAddedTax'] : null);
|
||||
$c->setOtherCharges(isset($data['otherCharges']) ? (string)$data['otherCharges'] : null);
|
||||
$c->setTotalCustomsCharges(isset($data['totalCustomsCharges']) ? (string)$data['totalCustomsCharges'] : null);
|
||||
$c->setCustomsDuty(isset($data['customsDuty']) && $data['customsDuty'] !== '' ? (string)$data['customsDuty'] : null);
|
||||
$c->setValueAddedTax(isset($data['valueAddedTax']) && $data['valueAddedTax'] !== '' ? (string)$data['valueAddedTax'] : null);
|
||||
$c->setOtherCharges(isset($data['otherCharges']) && $data['otherCharges'] !== '' ? (string)$data['otherCharges'] : null);
|
||||
$c->setTotalCustomsCharges(isset($data['totalCustomsCharges']) && $data['totalCustomsCharges'] !== '' ? (string)$data['totalCustomsCharges'] : null);
|
||||
$c->setCustomsBroker($data['customsBroker'] ?? null);
|
||||
$c->setCustomsBrokerPhone($data['customsBrokerPhone'] ?? null);
|
||||
$c->setCustomsBrokerEmail($data['customsBrokerEmail'] ?? null);
|
||||
|
@ -665,10 +661,10 @@ class PlugImportWorkflowController extends AbstractController
|
|||
if (isset($data['declarationNumber'])) $c->setDeclarationNumber($data['declarationNumber']);
|
||||
if (isset($data['customsCode'])) $c->setCustomsCode($data['customsCode']);
|
||||
if (isset($data['clearanceDate'])) $c->setClearanceDate($this->jalaliToGregorian($data['clearanceDate']) ?? null);
|
||||
if (isset($data['customsDuty'])) $c->setCustomsDuty((string)$data['customsDuty']);
|
||||
if (isset($data['valueAddedTax'])) $c->setValueAddedTax((string)$data['valueAddedTax']);
|
||||
if (isset($data['otherCharges'])) $c->setOtherCharges((string)$data['otherCharges']);
|
||||
if (isset($data['totalCustomsCharges'])) $c->setTotalCustomsCharges((string)$data['totalCustomsCharges']);
|
||||
if (isset($data['customsDuty'])) $c->setCustomsDuty($data['customsDuty'] !== '' ? (string)$data['customsDuty'] : null);
|
||||
if (isset($data['valueAddedTax'])) $c->setValueAddedTax($data['valueAddedTax'] !== '' ? (string)$data['valueAddedTax'] : null);
|
||||
if (isset($data['otherCharges'])) $c->setOtherCharges($data['otherCharges'] !== '' ? (string)$data['otherCharges'] : null);
|
||||
if (isset($data['totalCustomsCharges'])) $c->setTotalCustomsCharges($data['totalCustomsCharges'] !== '' ? (string)$data['totalCustomsCharges'] : null);
|
||||
if (isset($data['customsBroker'])) $c->setCustomsBroker($data['customsBroker']);
|
||||
if (isset($data['customsBrokerPhone'])) $c->setCustomsBrokerPhone($data['customsBrokerPhone']);
|
||||
if (isset($data['customsBrokerEmail'])) $c->setCustomsBrokerEmail($data['customsBrokerEmail']);
|
||||
|
@ -714,9 +710,9 @@ class PlugImportWorkflowController extends AbstractController
|
|||
$p = new ImportWorkflowPayment();
|
||||
$p->setImportWorkflow($workflow);
|
||||
$p->setType($data['type'] ?? 'other');
|
||||
$p->setAmount((string)($data['amount'] ?? '0'));
|
||||
$p->setAmount(($data['amount'] ?? '0'));
|
||||
$p->setCurrency($data['currency'] ?? 'IRR');
|
||||
$p->setAmountIRR(isset($data['amountIRR']) ? (string)$data['amountIRR'] : null);
|
||||
$p->setAmountIRR(isset($data['amountIRR']) && $data['amountIRR'] !== '' ? (string)$data['amountIRR'] : null);
|
||||
$p->setPaymentDate($this->jalaliToGregorian($data['paymentDate']) ?? date('Y-m-d'));
|
||||
$p->setReferenceNumber($data['referenceNumber'] ?? null);
|
||||
$p->setBankName($data['bankName'] ?? null);
|
||||
|
@ -749,7 +745,7 @@ class PlugImportWorkflowController extends AbstractController
|
|||
if (isset($data['type'])) $p->setType($data['type']);
|
||||
if (isset($data['amount'])) $p->setAmount((string)$data['amount']);
|
||||
if (isset($data['currency'])) $p->setCurrency($data['currency']);
|
||||
if (isset($data['amountIRR'])) $p->setAmountIRR((string)$data['amountIRR']);
|
||||
if (isset($data['amountIRR'])) $p->setAmountIRR($data['amountIRR'] !== '' ? (string)$data['amountIRR'] : null);
|
||||
if (isset($data['paymentDate'])) $p->setPaymentDate($this->jalaliToGregorian($data['paymentDate']) ?? date('Y-m-d'));
|
||||
if (isset($data['referenceNumber'])) $p->setReferenceNumber($data['referenceNumber']);
|
||||
if (isset($data['bankName'])) $p->setBankName($data['bankName']);
|
||||
|
@ -818,10 +814,28 @@ class PlugImportWorkflowController extends AbstractController
|
|||
$item->setModel($data['model'] ?? null);
|
||||
$item->setOriginCountry($data['originCountry'] ?? null);
|
||||
$item->setQuantity($data['quantity'] ?? '0');
|
||||
$item->setUnitPrice($data['unitPrice'] ?? null);
|
||||
$item->setUnitPriceIRR($data['unitPriceIRR'] ?? null);
|
||||
$item->setTotalPrice($data['totalPrice'] ?? null);
|
||||
$item->setTotalPriceIRR($data['totalPriceIRR'] ?? null);
|
||||
$item->setUnitPrice(isset($data['unitPrice']) && $data['unitPrice'] !== '' ? $data['unitPrice'] : null);
|
||||
|
||||
// محاسبه قیمت واحد ریالی بر اساس نرخ ارز پرونده
|
||||
$unitPrice = (float) ($data['unitPrice'] ?? 0);
|
||||
$exchangeRate = (float) ($workflow->getExchangeRate() ?? 0);
|
||||
$currency = $workflow->getCurrency();
|
||||
|
||||
if ($currency === 'IRR') {
|
||||
$unitPriceIRR = $unitPrice;
|
||||
} else {
|
||||
$unitPriceIRR = $unitPrice * $exchangeRate;
|
||||
}
|
||||
$item->setUnitPriceIRR((string) round($unitPriceIRR));
|
||||
|
||||
// محاسبه قیمت کل
|
||||
$quantity = (float) ($data['quantity'] ?? 0);
|
||||
$totalPrice = $quantity * $unitPrice;
|
||||
$item->setTotalPrice((string) round($totalPrice));
|
||||
|
||||
// محاسبه قیمت کل ریالی
|
||||
$totalPriceIRR = $quantity * $unitPriceIRR;
|
||||
$item->setTotalPriceIRR((string) round($totalPriceIRR));
|
||||
$item->setWeight($data['weight'] ?? null);
|
||||
$item->setVolume($data['volume'] ?? null);
|
||||
$item->setDescription($data['description'] ?? null);
|
||||
|
@ -869,10 +883,30 @@ class PlugImportWorkflowController extends AbstractController
|
|||
if (isset($data['model'])) $item->setModel($data['model']);
|
||||
if (isset($data['originCountry'])) $item->setOriginCountry($data['originCountry']);
|
||||
if (isset($data['quantity'])) $item->setQuantity($data['quantity']);
|
||||
if (isset($data['unitPrice'])) $item->setUnitPrice($data['unitPrice']);
|
||||
if (isset($data['unitPriceIRR'])) $item->setUnitPriceIRR($data['unitPriceIRR']);
|
||||
if (isset($data['totalPrice'])) $item->setTotalPrice($data['totalPrice']);
|
||||
if (isset($data['totalPriceIRR'])) $item->setTotalPriceIRR($data['totalPriceIRR']);
|
||||
if (isset($data['unitPrice'])) {
|
||||
$item->setUnitPrice($data['unitPrice'] !== '' ? $data['unitPrice'] : null);
|
||||
|
||||
// محاسبه مجدد قیمت واحد ریالی بر اساس نرخ ارز پرونده
|
||||
$unitPrice = (float) $data['unitPrice'];
|
||||
$exchangeRate = (float) ($workflow->getExchangeRate() ?? 0);
|
||||
$currency = $workflow->getCurrency();
|
||||
|
||||
if ($currency === 'IRR') {
|
||||
$unitPriceIRR = $unitPrice;
|
||||
} else {
|
||||
$unitPriceIRR = $unitPrice * $exchangeRate;
|
||||
}
|
||||
$item->setUnitPriceIRR((string) round($unitPriceIRR));
|
||||
|
||||
// محاسبه مجدد قیمت کل
|
||||
$quantity = (float) $item->getQuantity();
|
||||
$totalPrice = $quantity * $unitPrice;
|
||||
$item->setTotalPrice((string) round($totalPrice));
|
||||
|
||||
// محاسبه مجدد قیمت کل ریالی
|
||||
$totalPriceIRR = $quantity * $unitPriceIRR;
|
||||
$item->setTotalPriceIRR((string) round($totalPriceIRR));
|
||||
}
|
||||
if (isset($data['weight'])) $item->setWeight($data['weight']);
|
||||
if (isset($data['volume'])) $item->setVolume($data['volume']);
|
||||
if (isset($data['description'])) $item->setDescription($data['description']);
|
||||
|
@ -942,10 +976,9 @@ class PlugImportWorkflowController extends AbstractController
|
|||
if (isset($data['supplierAddress'])) $workflow->setSupplierAddress($data['supplierAddress']);
|
||||
if (isset($data['supplierPhone'])) $workflow->setSupplierPhone($data['supplierPhone']);
|
||||
if (isset($data['supplierEmail'])) $workflow->setSupplierEmail($data['supplierEmail']);
|
||||
if (isset($data['totalAmount'])) $workflow->setTotalAmount($data['totalAmount']);
|
||||
|
||||
if (isset($data['currency'])) $workflow->setCurrency($data['currency']);
|
||||
if (isset($data['exchangeRate'])) $workflow->setExchangeRate($data['exchangeRate']);
|
||||
if (isset($data['totalAmountIRR'])) $workflow->setTotalAmountIRR($data['totalAmountIRR']);
|
||||
if (isset($data['exchangeRate'])) $workflow->setExchangeRate($data['exchangeRate'] !== '' ? $data['exchangeRate'] : null);
|
||||
if (isset($data['status'])) $workflow->setStatus($data['status']);
|
||||
|
||||
$workflow->setDateMod(date('Y-m-d H:i:s'));
|
||||
|
|
|
@ -60,18 +60,12 @@ class ImportWorkflow
|
|||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $supplierEmail = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $totalAmount = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $currency = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $exchangeRate = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $totalAmountIRR = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'importWorkflow', targetEntity: ImportWorkflowItem::class, orphanRemoval: true)]
|
||||
private Collection $items;
|
||||
|
||||
|
@ -250,17 +244,6 @@ class ImportWorkflow
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getTotalAmount(): ?string
|
||||
{
|
||||
return $this->totalAmount;
|
||||
}
|
||||
|
||||
public function setTotalAmount(?string $totalAmount): static
|
||||
{
|
||||
$this->totalAmount = $totalAmount;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCurrency(): ?string
|
||||
{
|
||||
return $this->currency;
|
||||
|
@ -283,17 +266,6 @@ class ImportWorkflow
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getTotalAmountIRR(): ?string
|
||||
{
|
||||
return $this->totalAmountIRR;
|
||||
}
|
||||
|
||||
public function setTotalAmountIRR(?string $totalAmountIRR): static
|
||||
{
|
||||
$this->totalAmountIRR = $totalAmountIRR;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getItems(): Collection
|
||||
{
|
||||
return $this->items;
|
||||
|
|
|
@ -29,16 +29,16 @@ class ImportWorkflowCustoms
|
|||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $clearanceDate = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $customsDuty = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $valueAddedTax = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $otherCharges = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $totalCustomsCharges = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
|
|
|
@ -43,16 +43,16 @@ class ImportWorkflowItem
|
|||
#[ORM\Column(length: 255)]
|
||||
private ?string $quantity = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $unitPrice = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $unitPriceIRR = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $totalPrice = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $totalPriceIRR = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
|
|
|
@ -23,13 +23,13 @@ class ImportWorkflowPayment
|
|||
#[ORM\Column(length: 255)]
|
||||
private ?string $type = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2)]
|
||||
private ?string $amount = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $currency = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
|
||||
private ?string $amountIRR = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
|
|
|
@ -15,7 +15,7 @@ class FileStorage
|
|||
{
|
||||
$safeOriginal = preg_replace('/[^A-Za-z0-9_.-]/', '_', $file->getClientOriginalName());
|
||||
$relativeDir = 'storage/' . trim($businessId) . '/' . trim($context);
|
||||
$absDir = rtrim($this->kernel->getProjectDir(), '/').'/var/' . $relativeDir;
|
||||
$absDir = rtrim($this->kernel->getProjectDir(), '/').'/../hesabixArchive/' . $relativeDir;
|
||||
if (!is_dir($absDir)) {
|
||||
@mkdir($absDir, 0775, true);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class FileStorage
|
|||
public function absolutePath(string $relativePath): string
|
||||
{
|
||||
$relativePath = ltrim($relativePath, '/');
|
||||
return rtrim($this->kernel->getProjectDir(), '/').'/var/' . $relativePath;
|
||||
return rtrim($this->kernel->getProjectDir(), '/').'/../hesabixArchive/' . $relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,18 +75,7 @@
|
|||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.totalAmount)"
|
||||
label="مبلغ کل"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
:rules="[rules.positiveMoney, rules.maxAmount]"
|
||||
@update:modelValue="onMoneyInput('totalAmount', $event)"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="formData.currency"
|
||||
:items="currencyOptions"
|
||||
|
@ -95,26 +84,29 @@
|
|||
required
|
||||
></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.exchangeRate)"
|
||||
label="نرخ تبدیل"
|
||||
:model-value="formatMoneyTyping(formData.exchangeRate)"
|
||||
label="نرخ تبدیل (ریال)"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
inputmode="decimal"
|
||||
:rules="[rules.positiveMoney, rules.maxExchangeRate]"
|
||||
@update:modelValue="onMoneyInput('exchangeRate', $event)"
|
||||
@blur="formData.exchangeRate = parseMoney(formData.exchangeRate)"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
:model-value="formatMoney(formData.totalAmountIRR)"
|
||||
label="مبلغ کل (ریال)"
|
||||
readonly
|
||||
></v-text-field>
|
||||
<v-alert
|
||||
type="info"
|
||||
variant="tonal"
|
||||
class="mb-4"
|
||||
>
|
||||
<strong>نکته:</strong> مبلغ کل پرونده به صورت خودکار از جمع اقلام محاسبه میشود و نیازی به وارد کردن دستی نیست.
|
||||
</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
@ -174,10 +166,8 @@ const formData = ref({
|
|||
supplierPhone: '',
|
||||
supplierEmail: '',
|
||||
supplierAddress: '',
|
||||
totalAmount: '',
|
||||
currency: 'USD',
|
||||
exchangeRate: '',
|
||||
totalAmountIRR: '',
|
||||
description: ''
|
||||
})
|
||||
|
||||
|
@ -213,35 +203,34 @@ const rules = {
|
|||
maxExchangeRate: (value) => !value || parseFloat(value) <= 999999 || 'نرخ تبدیل نباید بیشتر از 999,999 باشد'
|
||||
}
|
||||
|
||||
const parseMoneyInput = (val) => {
|
||||
if (val === null || val === undefined) return 0
|
||||
const cleaned = String(val).replace(/,/g, '').replace(/[^\d.-]/g, '')
|
||||
const num = Number(cleaned)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
const parseMoney = (val) => {
|
||||
if (val === null || val === undefined || val === '') return 0
|
||||
const clean = String(val).replace(/,/g, '')
|
||||
const num = parseFloat(clean)
|
||||
return isNaN(num) ? 0 : parseFloat(num.toFixed(2))
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
formData.value[field] = numeric
|
||||
const formatMoneyTyping = (val) => {
|
||||
if (val === null || val === undefined || val === '') return ''
|
||||
const str = String(val).replace(/,/g, '')
|
||||
if (str === '') return ''
|
||||
const parts = str.split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, val) => {
|
||||
formData.value[field] = parseMoney(val)
|
||||
}
|
||||
|
||||
const formatMoney = (value) => {
|
||||
const numericValue = Number(value) || 0
|
||||
return numericValue
|
||||
.toFixed(0)
|
||||
.toFixed(2)
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
|
||||
watch([
|
||||
() => formData.value.totalAmount,
|
||||
() => formData.value.exchangeRate,
|
||||
() => formData.value.currency
|
||||
], ([newTotalAmount, newExchangeRate, currency]) => {
|
||||
const total = parseMoneyInput(newTotalAmount)
|
||||
const rate = currency === 'IRR' ? 1 : parseMoneyInput(newExchangeRate)
|
||||
const result = Math.round(total * rate)
|
||||
formData.value.totalAmountIRR = isNaN(result) ? 0 : result
|
||||
}, { immediate: true })
|
||||
// مبلغ کل به صورت محاسباتی از اقلام محاسبه میشود
|
||||
|
||||
// Methods
|
||||
const create = async () => {
|
||||
|
@ -287,10 +276,8 @@ const resetForm = () => {
|
|||
supplierPhone: '',
|
||||
supplierEmail: '',
|
||||
supplierAddress: '',
|
||||
totalAmount: '',
|
||||
currency: 'USD',
|
||||
exchangeRate: '',
|
||||
totalAmountIRR: '',
|
||||
description: ''
|
||||
}
|
||||
if (form.value) {
|
||||
|
|
|
@ -28,6 +28,12 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.clearanceDate="{ item }">
|
||||
<div>
|
||||
{{ formatDate(item.clearanceDate) }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
|
@ -93,34 +99,37 @@
|
|||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.customsDuty)"
|
||||
:model-value="formatMoneyTyping(formData.customsDuty)"
|
||||
label="حقوق گمرکی"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
inputmode="decimal"
|
||||
:rules="[rules.positiveMoney]"
|
||||
@update:modelValue="onMoneyInput('customsDuty', $event)"
|
||||
@blur="formData.customsDuty = parseMoney(formData.customsDuty)"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.valueAddedTax)"
|
||||
:model-value="formatMoneyTyping(formData.valueAddedTax)"
|
||||
label="مالیات بر ارزش افزوده"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
inputmode="decimal"
|
||||
:rules="[rules.positiveMoney]"
|
||||
@update:modelValue="onMoneyInput('valueAddedTax', $event)"
|
||||
@blur="formData.valueAddedTax = parseMoney(formData.valueAddedTax)"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.otherCharges)"
|
||||
:model-value="formatMoneyTyping(formData.otherCharges)"
|
||||
label="سایر عوارض"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
inputmode="decimal"
|
||||
:rules="[rules.positiveMoney]"
|
||||
@update:modelValue="onMoneyInput('otherCharges', $event)"
|
||||
@blur="formData.otherCharges = parseMoney(formData.otherCharges)"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
@ -261,22 +270,30 @@ const rules = {
|
|||
}
|
||||
|
||||
// Helpers for money formatting/parse and LTR input
|
||||
const parseMoneyInput = (val) => {
|
||||
if (val === null || val === undefined) return 0
|
||||
const cleaned = String(val).replace(/,/g, '').replace(/[^\d.-]/g, '')
|
||||
const num = Number(cleaned)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
const parseMoney = (val) => {
|
||||
if (val === null || val === undefined || val === '') return 0
|
||||
const clean = String(val).replace(/,/g, '')
|
||||
const num = parseFloat(clean)
|
||||
return isNaN(num) ? 0 : parseFloat(num.toFixed(2))
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
formData.value[field] = numeric
|
||||
const formatMoneyTyping = (val) => {
|
||||
if (val === null || val === undefined || val === '') return ''
|
||||
const str = String(val).replace(/,/g, '')
|
||||
if (str === '') return ''
|
||||
const parts = str.split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, val) => {
|
||||
formData.value[field] = parseMoney(val)
|
||||
}
|
||||
|
||||
const formatMoney = (value) => {
|
||||
const numericValue = Number(value) || 0
|
||||
return numericValue
|
||||
.toFixed(0)
|
||||
.toFixed(2)
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
|
||||
|
@ -391,6 +408,11 @@ const formatNumber = (number) => {
|
|||
if (!number) return '0'
|
||||
return new Intl.NumberFormat('fa-IR').format(number)
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '-'
|
||||
return new Date(date).toLocaleDateString('fa-IR')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -3,24 +3,13 @@
|
|||
<v-card-text>
|
||||
<div class="d-flex justify-space-between align-center mb-4">
|
||||
<h3>آیتمهای وارداتی</h3>
|
||||
<v-btn
|
||||
color="primary"
|
||||
prepend-icon="mdi-plus"
|
||||
@click="showAddDialog = true"
|
||||
>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="showAddDialog = true">
|
||||
افزودن آیتم
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
:loading="loading"
|
||||
density="comfortable"
|
||||
class="elevation-1"
|
||||
:header-props="{ class: 'custom-header' }"
|
||||
no-data-text="آیتمی ثبت نشده است"
|
||||
>
|
||||
<v-data-table :headers="headers" :items="items" :loading="loading" density="comfortable" class="elevation-1"
|
||||
:header-props="{ class: 'custom-header' }" no-data-text="آیتمی ثبت نشده است">
|
||||
<template v-slot:item.unitPrice="{ item }">
|
||||
<div>
|
||||
{{ formatNumber(item.unitPrice) }}
|
||||
|
@ -29,26 +18,22 @@
|
|||
</template>
|
||||
|
||||
<template v-slot:item.totalPrice="{ item }">
|
||||
<div class="d-flex justify-center align-center gap-2">
|
||||
<div>
|
||||
{{ formatNumber(item.totalPrice) }}
|
||||
{{ formatNumber(item.unitPrice * item.quantity) }}
|
||||
<small class="text-medium-emphasis">{{ getCurrency(item) }}</small>
|
||||
</div>
|
||||
<span class="mx-1">|</span>
|
||||
<div>
|
||||
{{ formatNumber(Number(item.unitPrice * item.quantity) * Number(props.exchangeRate)) }}
|
||||
<small class="text-medium-emphasis">ریال</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="editItem(item)"
|
||||
></v-btn>
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
size="small"
|
||||
variant="text"
|
||||
color="error"
|
||||
@click="deleteItem(item)"
|
||||
></v-btn>
|
||||
<v-btn icon="mdi-pencil" size="small" variant="text" @click="editItem(item)"></v-btn>
|
||||
<v-btn icon="mdi-delete" size="small" variant="text" color="error" @click="deleteItem(item)"></v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card-text>
|
||||
|
@ -68,20 +53,12 @@
|
|||
<v-row>
|
||||
<v-col cols="12">
|
||||
<template v-if="!editingItem">
|
||||
<Hcommoditysearch
|
||||
v-model="selectedCommodity"
|
||||
:return-object="true"
|
||||
label="انتخاب کالا"
|
||||
/>
|
||||
<Hcommoditysearch v-model="selectedCommodity" :return-object="true" label="انتخاب کالا" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-text-field
|
||||
:model-value="selectedCommodity ? (selectedCommodity.name + (selectedCommodity.code ? ` (${selectedCommodity.code})` : '')) : ''"
|
||||
label="کالا"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
disabled
|
||||
/>
|
||||
label="کالا" variant="outlined" density="compact" disabled />
|
||||
</template>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
@ -90,52 +67,32 @@
|
|||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.brand"
|
||||
label="برند"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.brand" label="برند"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.model"
|
||||
label="مدل"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.model" label="مدل"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.originCountry"
|
||||
label="کشور مبدا"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.originCountry" label="کشور مبدا"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.quantity"
|
||||
label="تعداد"
|
||||
type="number"
|
||||
:rules="[rules.required, rules.positive]"
|
||||
required
|
||||
min="1"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.quantity" label="تعداد" type="number"
|
||||
:rules="[rules.required, rules.positive]" required min="1"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.unitPrice)"
|
||||
label="قیمت واحد (ارزی)"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
:rules="[rules.required, rules.positiveMoney]"
|
||||
required
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field class="ltr-input" :model-value="formatMoneyTyping(formData.unitPrice)"
|
||||
label="قیمت واحد (ارزی)" type="text" inputmode="decimal"
|
||||
:rules="[rules.required, rules.positiveMoney]" required
|
||||
@update:modelValue="onMoneyInput('unitPrice', $event)"
|
||||
></v-text-field>
|
||||
@blur="formData.unitPrice = parseMoney(formData.unitPrice)"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<!-- <v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.unitPriceIRR)"
|
||||
|
@ -146,56 +103,32 @@
|
|||
required
|
||||
@update:modelValue="onMoneyInput('unitPriceIRR', $event)"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
:model-value="(formData.quantity && formData.unitPrice) ? formatMoney(totalPrice) : ''"
|
||||
label="قیمت کل"
|
||||
readonly
|
||||
></v-text-field>
|
||||
</v-col> -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :model-value="formatMoney(computedTotalPriceIRR)" label="قیمت کل" readonly></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.weight"
|
||||
label="وزن (کیلوگرم)"
|
||||
type="number"
|
||||
step="0.01"
|
||||
:rules="[rules.positive]"
|
||||
min="0"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.weight" label="وزن (کیلوگرم)" type="number" step="0.01"
|
||||
:rules="[rules.positive]" min="0"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.volume"
|
||||
label="حجم (متر مکعب)"
|
||||
type="number"
|
||||
step="0.01"
|
||||
:rules="[rules.positive]"
|
||||
min="0"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.volume" label="حجم (متر مکعب)" type="number" step="0.01"
|
||||
:rules="[rules.positive]" min="0"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="formData.specifications"
|
||||
label="ویژگیها"
|
||||
rows="2"
|
||||
></v-textarea>
|
||||
<v-textarea v-model="formData.specifications" label="ویژگیها" rows="2"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="formData.description"
|
||||
label="توضیحات"
|
||||
rows="2"
|
||||
></v-textarea>
|
||||
<v-textarea v-model="formData.description" label="توضیحات" rows="2"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
@ -205,12 +138,7 @@
|
|||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="closeDialog">لغو</v-btn>
|
||||
<v-btn
|
||||
type="submit"
|
||||
color="primary"
|
||||
:loading="loading"
|
||||
:disabled="!valid"
|
||||
>
|
||||
<v-btn type="submit" color="primary" :loading="loading" :disabled="!valid">
|
||||
{{ editingItem ? 'ویرایش' : 'افزودن' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
@ -254,6 +182,10 @@ const props = defineProps({
|
|||
currency: {
|
||||
type: String,
|
||||
default: 'USD'
|
||||
},
|
||||
exchangeRate: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -311,50 +243,54 @@ const headers = [
|
|||
|
||||
// Validation rules
|
||||
const rules = {
|
||||
required: (value) => !!value || 'این فیلد الزامی است',
|
||||
positive: (value) => !value || parseFloat(value) > 0 || 'مقدار باید مثبت باشد',
|
||||
required: (value) =>
|
||||
value !== null && value !== '' && value !== undefined || 'این فیلد الزامی است',
|
||||
positive: (value) => {
|
||||
const num = parseMoney(value)
|
||||
return num > 0 || 'مقدار باید مثبت باشد'
|
||||
},
|
||||
positiveMoney: (value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
return numeric > 0 || 'مقدار باید مثبت باشد'
|
||||
const num = parseMoney(value)
|
||||
return num > 0 || 'مقدار باید مثبت باشد'
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers for money formatting/parse and LTR input
|
||||
const parseMoneyInput = (val) => {
|
||||
if (val === null || val === undefined) return 0
|
||||
const cleaned = String(val).replace(/,/g, '').replace(/[^\d.-]/g, '')
|
||||
const num = Number(cleaned)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
formData.value[field] = numeric
|
||||
}
|
||||
|
||||
const formatMoney = (value) => {
|
||||
const numericValue = Number(value) || 0
|
||||
return numericValue
|
||||
.toFixed(0)
|
||||
.toFixed(2)
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
|
||||
// Computed
|
||||
const totalPrice = computed(() => {
|
||||
if (formData.value.quantity && formData.value.unitPrice) {
|
||||
const total = parseFloat(formData.value.quantity) * parseFloat(formData.value.unitPrice)
|
||||
formData.value.totalPrice = total.toString()
|
||||
return total
|
||||
}
|
||||
return 0
|
||||
})
|
||||
const parseMoney = (val) => {
|
||||
if (val === null || val === undefined || val === '') return 0
|
||||
const clean = String(val).replace(/,/g, '')
|
||||
const num = parseFloat(clean)
|
||||
return isNaN(num) ? 0 : parseFloat(num.toFixed(2))
|
||||
}
|
||||
|
||||
// Watch for unit price IRR and quantity changes
|
||||
watch([() => formData.value.quantity, () => formData.value.unitPriceIRR], () => {
|
||||
if (formData.value.quantity && formData.value.unitPriceIRR) {
|
||||
const total = parseFloat(formData.value.quantity) * parseFloat(formData.value.unitPriceIRR)
|
||||
formData.value.totalPriceIRR = total.toString()
|
||||
}
|
||||
const formatMoneyTyping = (val) => {
|
||||
if (val === null || val === undefined || val === '') return ''
|
||||
const str = String(val).replace(/,/g, '')
|
||||
if (str === '') return ''
|
||||
const parts = str.split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, val) => {
|
||||
formData.value[field] = parseMoney(val)
|
||||
}
|
||||
|
||||
const computedTotalPriceIRR = computed(() => {
|
||||
if (!formData.value.quantity || !formData.value.unitPrice || !props.exchangeRate) return 0
|
||||
const quantity = parseFloat(formData.value.quantity)
|
||||
const unitPrice = parseFloat(formData.value.unitPrice)
|
||||
const exchangeRate = parseFloat(props.exchangeRate)
|
||||
const currency = props.currency
|
||||
|
||||
if (currency === 'IRR') return quantity * unitPrice
|
||||
return (quantity * unitPrice * exchangeRate).toFixed(2)
|
||||
})
|
||||
|
||||
// Methods
|
||||
|
@ -482,12 +418,19 @@ const closeDialog = () => {
|
|||
}
|
||||
|
||||
// Utilities
|
||||
const formatNumber = (number) => {
|
||||
if (!number) return '0'
|
||||
return new Intl.NumberFormat('fa-IR').format(number)
|
||||
const formatNumber = (number, fractionDigits = 2) => {
|
||||
if (number === null || number === undefined || number === '') return '0'
|
||||
const formatted = new Intl.NumberFormat('fa-IR', {
|
||||
minimumFractionDigits: fractionDigits,
|
||||
maximumFractionDigits: fractionDigits
|
||||
}).format(number)
|
||||
|
||||
return formatted.replace('٫', '.')
|
||||
}
|
||||
|
||||
const getCurrency = () => props.currency || 'USD'
|
||||
const getCurrency = (item) => {
|
||||
return props.currency || 'USD'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -518,6 +461,3 @@ const getCurrency = () => props.currency || 'USD'
|
|||
background-color: #f5f5f5 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,30 +3,15 @@
|
|||
<v-card-text>
|
||||
<div class="d-flex justify-space-between align-center mb-4">
|
||||
<h3>پرداختها</h3>
|
||||
<v-btn
|
||||
color="primary"
|
||||
prepend-icon="mdi-plus"
|
||||
@click="showAddDialog = true"
|
||||
>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="showAddDialog = true">
|
||||
افزودن پرداخت
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="payments"
|
||||
:loading="loading"
|
||||
density="comfortable"
|
||||
class="elevation-1"
|
||||
:header-props="{ class: 'custom-header' }"
|
||||
no-data-text="پرداختی ثبت نشده است"
|
||||
>
|
||||
<v-data-table :headers="headers" :items="payments" :loading="loading" density="comfortable" class="elevation-1"
|
||||
:header-props="{ class: 'custom-header' }" no-data-text="پرداختی ثبت نشده است">
|
||||
<template v-slot:item.type="{ item }">
|
||||
<v-chip
|
||||
:color="getTypeColor(item.type)"
|
||||
size="small"
|
||||
variant="flat"
|
||||
>
|
||||
<v-chip :color="getTypeColor(item.type)" size="small" variant="flat">
|
||||
{{ getTypeText(item.type) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
@ -40,16 +25,13 @@
|
|||
|
||||
<template v-slot:item.amountIRR="{ item }">
|
||||
<div>
|
||||
{{ formatNumber(item.amountIRR) }}
|
||||
{{ formatNumber(Number(item.amount) * Number(props.exchangeRate)) }}
|
||||
<small class="text-medium-emphasis">ریال</small>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.status="{ item }">
|
||||
<v-chip
|
||||
:color="getStatusColor(item.status)"
|
||||
size="small"
|
||||
>
|
||||
<v-chip :color="getStatusColor(item.status)" size="small">
|
||||
{{ getStatusText(item.status) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
@ -59,19 +41,8 @@
|
|||
</template>
|
||||
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="editPayment(item)"
|
||||
></v-btn>
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
size="small"
|
||||
variant="text"
|
||||
color="error"
|
||||
@click="deletePayment(item)"
|
||||
></v-btn>
|
||||
<v-btn icon="mdi-pencil" size="small" variant="text" @click="editPayment(item)"></v-btn>
|
||||
<v-btn icon="mdi-delete" size="small" variant="text" color="error" @click="deletePayment(item)"></v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card-text>
|
||||
|
@ -89,113 +60,65 @@
|
|||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="formData.type"
|
||||
:items="paymentTypes"
|
||||
label="نوع پرداخت"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
></v-select>
|
||||
<v-select v-model="formData.type" :items="paymentTypes" label="نوع پرداخت" :rules="[rules.required]"
|
||||
required></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="formData.status"
|
||||
:items="statusOptions"
|
||||
label="وضعیت"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
></v-select>
|
||||
<v-select v-model="formData.status" :items="statusOptions" label="وضعیت" :rules="[rules.required]"
|
||||
required></v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.amount)"
|
||||
label="مبلغ"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
:rules="[rules.required, rules.positiveMoney]"
|
||||
required
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field class="ltr-input" :model-value="formatMoneyTyping(formData.amount)" label="مبلغ (ارزی)"
|
||||
type="text" inputmode="decimal" :rules="[rules.required, rules.positiveMoney]" required
|
||||
@update:modelValue="onMoneyInput('amount', $event)"
|
||||
></v-text-field>
|
||||
@blur="formData.amount = parseMoney(formData.amount)"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-select
|
||||
v-model="formData.currency"
|
||||
:items="currencyOptions"
|
||||
label="واحد پول"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
></v-select>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :model-value="formatMoney(computedAmountIRR)" label="مبلغ (ریال) - محاسباتی" readonly
|
||||
variant="outlined" color="primary"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(formData.amountIRR)"
|
||||
label="مبلغ (ریال)"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
:rules="[rules.positiveMoney]"
|
||||
@update:modelValue="onMoneyInput('amountIRR', $event)"
|
||||
></v-text-field>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-alert type="info" variant="tonal" class="mb-4">
|
||||
<strong>نکته:</strong> مبلغ ریالی به صورت خودکار بر اساس نرخ ارز پرونده محاسبه میشود.
|
||||
</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<h-date-picker
|
||||
v-model="formData.paymentDate"
|
||||
label="تاریخ پرداخت"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
<h-date-picker v-model="formData.paymentDate" label="تاریخ پرداخت" :rules="[rules.required]" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.referenceNumber"
|
||||
label="شماره مرجع"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.referenceNumber" label="شماره مرجع"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.bankName"
|
||||
label="نام بانک"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.bankName" label="نام بانک"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.accountNumber"
|
||||
label="شماره حساب"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.accountNumber" label="شماره حساب"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.recipientName"
|
||||
label="نام دریافت کننده"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.recipientName" label="نام دریافت کننده"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="formData.receiptNumber"
|
||||
label="شماره رسید"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="formData.receiptNumber" label="شماره رسید"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="formData.description"
|
||||
label="توضیحات"
|
||||
rows="2"
|
||||
></v-textarea>
|
||||
<v-textarea v-model="formData.description" label="توضیحات" rows="2"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
@ -205,12 +128,7 @@
|
|||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="closeDialog">لغو</v-btn>
|
||||
<v-btn
|
||||
type="submit"
|
||||
color="primary"
|
||||
:loading="loading"
|
||||
:disabled="!valid"
|
||||
>
|
||||
<v-btn type="submit" color="primary" :loading="loading" :disabled="!valid">
|
||||
{{ editingPayment ? 'ویرایش' : 'افزودن' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
@ -236,7 +154,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import axios from 'axios'
|
||||
import Swal from 'sweetalert2'
|
||||
import HDatepicker from '@/components/forms/Hdatepicker.vue'
|
||||
|
@ -250,6 +168,14 @@ const props = defineProps({
|
|||
payments: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
currency: {
|
||||
type: String,
|
||||
default: 'USD'
|
||||
},
|
||||
exchangeRate: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -321,34 +247,56 @@ const currencyOptions = [
|
|||
|
||||
// Validation rules
|
||||
const rules = {
|
||||
required: (value) => !!value || 'این فیلد الزامی است',
|
||||
positive: (value) => !value || parseFloat(value) > 0 || 'مقدار باید مثبت باشد',
|
||||
required: (value) =>
|
||||
value !== null && value !== '' && value !== undefined || 'این فیلد الزامی است',
|
||||
positive: (value) => {
|
||||
const num = parseMoney(value)
|
||||
return num > 0 || 'مقدار باید مثبت باشد'
|
||||
},
|
||||
positiveMoney: (value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
return numeric > 0 || 'مقدار باید مثبت باشد'
|
||||
const num = parseMoney(value)
|
||||
return num > 0 || 'مقدار باید مثبت باشد'
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers for money formatting/parse and LTR input
|
||||
const parseMoneyInput = (val) => {
|
||||
if (val === null || val === undefined) return 0
|
||||
const cleaned = String(val).replace(/,/g, '').replace(/[^\d.-]/g, '')
|
||||
const num = Number(cleaned)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
const parseMoney = (val) => {
|
||||
if (val === null || val === undefined || val === '') return 0
|
||||
const clean = String(val).replace(/,/g, '')
|
||||
const num = parseFloat(clean)
|
||||
return isNaN(num) ? 0 : parseFloat(num.toFixed(2))
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
formData.value[field] = numeric
|
||||
const formatMoneyTyping = (val) => {
|
||||
if (val === null || val === undefined || val === '') return ''
|
||||
const str = String(val).replace(/,/g, '')
|
||||
if (str === '') return ''
|
||||
const parts = str.split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, val) => {
|
||||
formData.value[field] = parseMoney(val)
|
||||
}
|
||||
|
||||
const formatMoney = (value) => {
|
||||
const numericValue = Number(value) || 0
|
||||
return numericValue
|
||||
.toFixed(0)
|
||||
.toFixed(2)
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
|
||||
// محاسبه مبلغ ریالی بر اساس نرخ ارز پرونده
|
||||
const computedAmountIRR = computed(() => {
|
||||
if (!formData.value.amount || !props.exchangeRate) return 0
|
||||
const amount = parseFloat(formData.value.amount)
|
||||
const exchangeRate = parseFloat(props.exchangeRate)
|
||||
const currency = props.currency
|
||||
|
||||
if (currency === 'IRR') return amount
|
||||
return Math.round(amount * exchangeRate)
|
||||
})
|
||||
|
||||
// Methods
|
||||
const editPayment = (payment) => {
|
||||
editingPayment.value = payment
|
||||
|
@ -498,9 +446,14 @@ const getStatusText = (status) => {
|
|||
return texts[status] || status
|
||||
}
|
||||
|
||||
const formatNumber = (number) => {
|
||||
if (!number) return '0'
|
||||
return new Intl.NumberFormat('fa-IR').format(number)
|
||||
const formatNumber = (number, fractionDigits = 2) => {
|
||||
if (number === null || number === undefined || number === '') return '0'
|
||||
const formatted = new Intl.NumberFormat('fa-IR', {
|
||||
minimumFractionDigits: fractionDigits,
|
||||
maximumFractionDigits: fractionDigits
|
||||
}).format(number)
|
||||
|
||||
return formatted.replace('٫', '.')
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
|
@ -542,6 +495,3 @@ const closeDialog = () => {
|
|||
background-color: #f5f5f5 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -508,6 +508,7 @@ const fa_lang = {
|
|||
year_label: "سال مالی جاری",
|
||||
global_settings: "تنظیمات سراسری",
|
||||
warranty_settings: "تنظیمات گارانتی",
|
||||
warranty_page_title: " سریال های گارانتی",
|
||||
gate_pay: "درگاه پرداخت",
|
||||
a4l: "کاغذ A4 افقی",
|
||||
a4p: "کاغذ A4 عمودی",
|
||||
|
@ -826,6 +827,7 @@ const fa_lang = {
|
|||
sms_settings_plug_accpro_pass_cheque_input: "واگذاری چک",
|
||||
sms_settings_reject_cheque_input: "برگشت چک",
|
||||
sms_settings_plug_accpro_reject_cheque_input: "برگشت چک",
|
||||
sms_settings_warranty_send_serial: "ارسال سریال گارانتی",
|
||||
inquiry_zohal_api_key: "کلید API زحل",
|
||||
inquiry_zohal_api_key_des: "کلید API زحل برای دریافت اطلاعات از سامانه زحل",
|
||||
inquiry_zohal_api_key_placeholder: "کلید API زحل",
|
||||
|
|
|
@ -127,17 +127,6 @@
|
|||
class="mb-3"
|
||||
@update:model-value="loadWorkflows"
|
||||
/>
|
||||
<v-select
|
||||
v-model="filters.status"
|
||||
label="وضعیت"
|
||||
:items="statusOptions"
|
||||
clearable
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
@update:model-value="loadWorkflows"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- دسکتاپ -->
|
||||
|
@ -155,18 +144,6 @@
|
|||
@update:model-value="loadWorkflows"
|
||||
class="ml-2"
|
||||
/>
|
||||
<v-select
|
||||
v-model="filters.status"
|
||||
label="وضعیت"
|
||||
:items="statusOptions"
|
||||
clearable
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
style="max-width: 200px;"
|
||||
@update:model-value="loadWorkflows"
|
||||
class="ml-2"
|
||||
/>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
color="primary"
|
||||
|
@ -179,15 +156,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.status="{ item }">
|
||||
<v-chip
|
||||
:color="getStatusColor(item.status)"
|
||||
size="small"
|
||||
>
|
||||
{{ getStatusText(item.status) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.totalAmount="{ item }">
|
||||
<div>
|
||||
{{ formatNumber(item.totalAmount) }}
|
||||
|
@ -322,7 +290,6 @@ const showNotification = (text, color = 'success') => {
|
|||
|
||||
// Filters
|
||||
const filters = ref({
|
||||
status: '',
|
||||
search: ''
|
||||
})
|
||||
|
||||
|
@ -340,22 +307,10 @@ const headers = [
|
|||
{ title: 'عنوان', key: 'title', sortable: true },
|
||||
{ title: 'تامین کننده', key: 'supplierName', sortable: true },
|
||||
{ title: 'مبلغ کل', key: 'totalAmount', sortable: true },
|
||||
{ title: 'وضعیت', key: 'status', sortable: true },
|
||||
{ title: 'تاریخ ثبت', key: 'dateSubmit', sortable: true },
|
||||
{ title: 'ثبت کننده', key: 'submitter', sortable: true }
|
||||
]
|
||||
|
||||
// Status options
|
||||
const statusOptions = [
|
||||
{ title: 'پیشنویس', value: 'draft' },
|
||||
{ title: 'در حال پردازش', value: 'processing' },
|
||||
{ title: 'ارسال شده', value: 'shipped' },
|
||||
{ title: 'رسیده', value: 'arrived' },
|
||||
{ title: 'ترخیص شده', value: 'cleared' },
|
||||
{ title: 'تکمیل شده', value: 'completed' },
|
||||
{ title: 'لغو شده', value: 'cancelled' }
|
||||
]
|
||||
|
||||
// Methods
|
||||
const loadWorkflows = async () => {
|
||||
loading.value = true
|
||||
|
@ -365,10 +320,6 @@ const loadWorkflows = async () => {
|
|||
limit: pagination.value.limit
|
||||
}
|
||||
|
||||
if (filters.value.status) {
|
||||
params.status = filters.value.status
|
||||
}
|
||||
|
||||
if (filters.value.search) {
|
||||
params.search = filters.value.search
|
||||
}
|
||||
|
@ -398,30 +349,18 @@ const loadStats = async () => {
|
|||
} else {
|
||||
// Calculate stats from current data if API not available
|
||||
const totalWorkflows = workflows.value.length
|
||||
const draftWorkflows = workflows.value.filter(w => w.status === 'draft').length
|
||||
const processingWorkflows = workflows.value.filter(w => w.status === 'processing').length
|
||||
const completedWorkflows = workflows.value.filter(w => w.status === 'completed').length
|
||||
|
||||
stats.value = {
|
||||
totalWorkflows,
|
||||
draftWorkflows,
|
||||
processingWorkflows,
|
||||
completedWorkflows
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('خطا در بارگذاری آمار:', error)
|
||||
// Calculate stats from current data
|
||||
const totalWorkflows = workflows.value.length
|
||||
const draftWorkflows = workflows.value.filter(w => w.status === 'draft').length
|
||||
const processingWorkflows = workflows.value.filter(w => w.status === 'processing').length
|
||||
const completedWorkflows = workflows.value.filter(w => w.status === 'completed').length
|
||||
|
||||
stats.value = {
|
||||
totalWorkflows,
|
||||
draftWorkflows,
|
||||
processingWorkflows,
|
||||
completedWorkflows
|
||||
}
|
||||
} finally {
|
||||
statsLoading.value = false
|
||||
|
@ -434,11 +373,6 @@ const updatePagination = (options) => {
|
|||
loadWorkflows()
|
||||
}
|
||||
|
||||
const filterByStatus = (status) => {
|
||||
filters.value.status = filters.value.status === status ? '' : status
|
||||
loadWorkflows()
|
||||
}
|
||||
|
||||
const viewWorkflow = (workflow) => {
|
||||
router.push(`/acc/plugins/import-workflow/${workflow.id}`)
|
||||
}
|
||||
|
@ -479,50 +413,6 @@ const onWorkflowCreated = () => {
|
|||
// loadStats()
|
||||
}
|
||||
|
||||
// Utilities
|
||||
const getStatusColor = (status) => {
|
||||
const colors = {
|
||||
draft: 'grey',
|
||||
processing: 'blue',
|
||||
shipped: 'orange',
|
||||
arrived: 'purple',
|
||||
cleared: 'teal',
|
||||
completed: 'green',
|
||||
cancelled: 'red'
|
||||
}
|
||||
return colors[status] || 'grey'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const texts = {
|
||||
draft: 'پیشنویس',
|
||||
processing: 'در حال پردازش',
|
||||
shipped: 'ارسال شده',
|
||||
arrived: 'رسیده',
|
||||
cleared: 'ترخیص شده',
|
||||
completed: 'تکمیل شده',
|
||||
cancelled: 'لغو شده'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
const getCardClasses = (status) => {
|
||||
const baseClasses = 'stats-card'
|
||||
const statusClasses = {
|
||||
'draft': 'draft-card',
|
||||
'processing': 'processing-card',
|
||||
'completed': 'completed-card'
|
||||
}
|
||||
|
||||
const classes = [baseClasses, statusClasses[status]]
|
||||
|
||||
if (filters.value.status === status) {
|
||||
classes.push('active-filter')
|
||||
}
|
||||
|
||||
return classes.join(' ')
|
||||
}
|
||||
|
||||
const formatNumber = (number) => {
|
||||
if (!number) return '0'
|
||||
return new Intl.NumberFormat('fa-IR').format(number)
|
||||
|
|
|
@ -7,28 +7,12 @@
|
|||
<v-card class="mb-4">
|
||||
<v-card-title class="d-flex align-center justify-space-between">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
icon="mdi-arrow-right"
|
||||
variant="text"
|
||||
@click="$router.back()"
|
||||
class="ml-2"
|
||||
></v-btn>
|
||||
<v-btn icon="mdi-arrow-right" variant="text" @click="$router.back()" class="ml-2"></v-btn>
|
||||
<v-icon class="ml-2" color="primary">mdi-import</v-icon>
|
||||
<span>{{ workflow?.title || 'جزئیات پرونده واردات' }}</span>
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<v-chip
|
||||
:color="getStatusColor(workflow?.status)"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ getStatusText(workflow?.status) }}
|
||||
</v-chip>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
prepend-icon="mdi-pencil"
|
||||
@click="editMode = !editMode"
|
||||
>
|
||||
<v-btn color="primary" variant="outlined" prepend-icon="mdi-pencil" @click="editMode = !editMode">
|
||||
{{ editMode ? 'لغو ویرایش' : 'ویرایش' }}
|
||||
</v-btn>
|
||||
<!-- <v-btn
|
||||
|
@ -61,119 +45,67 @@
|
|||
<v-form v-if="editMode" ref="form" v-model="valid" validate-on="input">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="editData.title"
|
||||
label="عنوان پرونده"
|
||||
:rules="[rules.required, rules.minLength]"
|
||||
counter="100"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="editData.title" label="عنوان پرونده"
|
||||
:rules="[rules.required, rules.minLength]" counter="100"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="editData.supplierName"
|
||||
label="نام تامین کننده"
|
||||
:rules="[rules.required, rules.minLength]"
|
||||
counter="100"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="editData.supplierName" label="نام تامین کننده"
|
||||
:rules="[rules.required, rules.minLength]" counter="100"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="editData.supplierCountry"
|
||||
label="کشور تامین کننده"
|
||||
:rules="[rules.maxLength]"
|
||||
counter="50"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="editData.supplierCountry" label="کشور تامین کننده"
|
||||
:rules="[rules.maxLength]" counter="50"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="editData.supplierPhone"
|
||||
label="تلفن تامین کننده"
|
||||
:rules="[rules.phone]"
|
||||
counter="20"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="editData.supplierPhone" label="تلفن تامین کننده" :rules="[rules.phone]"
|
||||
counter="20"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="editData.supplierEmail"
|
||||
label="ایمیل تامین کننده"
|
||||
:rules="[rules.email]"
|
||||
counter="100"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="editData.supplierEmail" label="ایمیل تامین کننده" :rules="[rules.email]"
|
||||
counter="100"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="editData.supplierAddress"
|
||||
label="آدرس تامین کننده"
|
||||
rows="2"
|
||||
:rules="[rules.maxLength]"
|
||||
counter="500"
|
||||
></v-textarea>
|
||||
<v-textarea v-model="editData.supplierAddress" label="آدرس تامین کننده" rows="2"
|
||||
:rules="[rules.maxLength]" counter="500"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(editData.totalAmount)"
|
||||
label="مبلغ کل (ارزی)"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
:rules="[rules.positiveMoney]"
|
||||
@update:modelValue="onMoneyInput('totalAmount', $event)"
|
||||
></v-text-field>
|
||||
<v-col cols="12" md="6">
|
||||
<v-select v-model="editData.currency" :items="currencyOptions" label="واحد پول"
|
||||
:rules="[rules.required]"></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-select
|
||||
v-model="editData.currency"
|
||||
:items="currencyOptions"
|
||||
label="واحد پول"
|
||||
:rules="[rules.required]"
|
||||
></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
class="ltr-input"
|
||||
:model-value="formatMoney(editData.exchangeRate)"
|
||||
label="نرخ تبدیل (ریال)"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
:rules="[rules.exchangeRateRule]"
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field class="ltr-input" :model-value="formatMoneyTyping(editData.exchangeRate)"
|
||||
label="نرخ تبدیل (ریال)" type="text" inputmode="decimal" :rules="[rules.exchangeRateRule]"
|
||||
@update:modelValue="onMoneyInput('exchangeRate', $event)"
|
||||
></v-text-field>
|
||||
@blur="editData.exchangeRate = parseMoney(editData.exchangeRate)"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :model-value="formatMoney(computedTotalAmount)" label="مبلغ کل (ارزی) - محاسباتی"
|
||||
readonly variant="outlined" color="primary"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :model-value="formatMoney(computedTotalAmountIRR)" label="مبلغ کل (ریال) - محاسباتی"
|
||||
readonly variant="outlined" color="primary"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
:model-value="formatMoney(editData.totalAmountIRR)"
|
||||
label="مبلغ کل (ریال)"
|
||||
readonly
|
||||
></v-text-field>
|
||||
<v-textarea v-model="editData.description" label="توضیحات" rows="3"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="editData.description"
|
||||
label="توضیحات"
|
||||
rows="3"
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="saveChanges"
|
||||
:loading="saveLoading"
|
||||
:disabled="!isFormValidForSave"
|
||||
>
|
||||
<v-btn color="primary" @click="saveChanges" :loading="saveLoading"
|
||||
:disabled="!isFormValidForSave">
|
||||
ذخیره تغییرات
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
@ -220,20 +152,41 @@
|
|||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<div class="mb-3">
|
||||
<strong>مبلغ کل:</strong>
|
||||
<div>{{ formatMoney(workflow.totalAmount) }} {{ workflow.currency }}</div>
|
||||
<strong>مبلغ کل (ارزی):</strong>
|
||||
<div>{{formatMoney(workflow.items?.reduce((total, item) => Number(total) +
|
||||
Number(item.unitPrice * item.quantity || 0), 0)) }} {{ workflow.currency }}</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<div class="mb-3">
|
||||
<strong>نرخ تبدیل:</strong>
|
||||
<strong>نرخ تبدیل (ریال):</strong>
|
||||
<div>{{ formatMoney(workflow.exchangeRate) }}</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<div class="mb-3">
|
||||
<strong>مبلغ کل (ریال):</strong>
|
||||
<div>{{ formatMoney(workflow.totalAmountIRR) }}</div>
|
||||
<div>{{formatMoney(workflow.items?.reduce((total, item) => Number(total) +
|
||||
Number(item.unitPrice * item.quantity * workflow.exchangeRate || 0), 0)) }}</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<div class="mb-3">
|
||||
<strong>هزینه های ترخیص (ریال):</strong>
|
||||
<div>{{formatMoney(workflow.customs?.reduce((total, custom) => Number(total) +
|
||||
Number(custom.totalCustomsCharges || 0), 0)) }}</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<div class="mb-3">
|
||||
<strong>مجموعه پرداخت ها :</strong>
|
||||
<div class="d-flex align-center gap-2">
|
||||
<div>{{formatMoney(workflow.payments?.reduce((total, payment) => Number(total) +
|
||||
Number(payment.amount || 0), 0)) }} {{ workflow.currency }}</div>
|
||||
<div>|</div>
|
||||
<div>{{formatMoney(workflow.payments?.reduce((total, payment) => Number(total) +
|
||||
Number(payment.amount * workflow.exchangeRate || 0), 0)) }} ریال</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
@ -276,90 +229,64 @@
|
|||
<v-col cols="12">
|
||||
<v-card>
|
||||
<v-tabs v-model="activeTab" bg-color="primary">
|
||||
<v-tab value="items">آیتمها <v-chip size="small" color="secondary" variant="tonal" class="ms-2" style="color: white !important;">{{ workflow.items?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="payments">پرداختها <v-chip size="small" color="secondary" variant="tonal" class="ms-2" style="color: white !important;">{{ workflow.payments?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="documents">اسناد <v-chip size="small" color="secondary" variant="tonal" class="ms-2" style="color: white !important;">{{ workflow.documents?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="stages">مراحل <v-chip size="small" color="secondary" variant="tonal" class="ms-2" style="color: white !important;">{{ workflow.stages?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="shipping">حمل و نقل <v-chip size="small" color="secondary" variant="tonal" class="ms-2" style="color: white !important;">{{ workflow.shipping?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="customs">ترخیص <v-chip size="small" color="secondary" variant="tonal" class="ms-2" style="color: white !important;">{{ workflow.customs?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="items">آیتمها <v-chip size="small" color="secondary" variant="tonal" class="ms-2"
|
||||
style="color: white !important;">{{ workflow.items?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="payments">پرداختها <v-chip size="small" color="secondary" variant="tonal" class="ms-2"
|
||||
style="color: white !important;">{{ workflow.payments?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="documents">اسناد <v-chip size="small" color="secondary" variant="tonal" class="ms-2"
|
||||
style="color: white !important;">{{ workflow.documents?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="stages">مراحل <v-chip size="small" color="secondary" variant="tonal" class="ms-2"
|
||||
style="color: white !important;">{{ workflow.stages?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="shipping">حمل و نقل <v-chip size="small" color="secondary" variant="tonal" class="ms-2"
|
||||
style="color: white !important;">{{ workflow.shipping?.length || 0 }}</v-chip></v-tab>
|
||||
<v-tab value="customs">ترخیص <v-chip size="small" color="secondary" variant="tonal" class="ms-2"
|
||||
style="color: white !important;">{{ workflow.customs?.length || 0 }}</v-chip></v-tab>
|
||||
<!-- <v-tab value="tickets">حوالههای مرتبط</v-tab> -->
|
||||
</v-tabs>
|
||||
|
||||
<v-tabs-window v-model="activeTab">
|
||||
<v-tabs-window-item value="items">
|
||||
<ImportWorkflowItems
|
||||
:workflow-id="workflowId"
|
||||
:items="workflow.items"
|
||||
:currency="workflow.currency"
|
||||
@updated="loadWorkflow"
|
||||
/>
|
||||
<ImportWorkflowItems :workflow-id="workflowId" :items="workflow.items" :currency="workflow.currency"
|
||||
:exchange-rate="workflow.exchangeRate" @updated="loadWorkflow" />
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="payments">
|
||||
<ImportWorkflowPayments
|
||||
:workflow-id="workflowId"
|
||||
:payments="workflow.payments"
|
||||
@updated="loadWorkflow"
|
||||
/>
|
||||
<ImportWorkflowPayments :workflow-id="workflowId" :payments="workflow.payments"
|
||||
:currency="workflow.currency" :exchange-rate="workflow.exchangeRate" @updated="loadWorkflow" />
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="documents">
|
||||
<ImportWorkflowDocuments
|
||||
:workflow-id="workflowId"
|
||||
:documents="workflow.documents"
|
||||
@updated="loadWorkflow"
|
||||
/>
|
||||
<ImportWorkflowDocuments :workflow-id="workflowId" :documents="workflow.documents"
|
||||
@updated="loadWorkflow" />
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="stages">
|
||||
<ImportWorkflowStages
|
||||
:workflow-id="workflowId"
|
||||
:stages="workflow.stages"
|
||||
@updated="loadWorkflow"
|
||||
/>
|
||||
<ImportWorkflowStages :workflow-id="workflowId" :stages="workflow.stages" @updated="loadWorkflow" />
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="shipping">
|
||||
<ImportWorkflowShipping
|
||||
:workflow-id="workflowId"
|
||||
:shipping="workflow.shipping"
|
||||
@updated="loadWorkflow"
|
||||
/>
|
||||
<ImportWorkflowShipping :workflow-id="workflowId" :shipping="workflow.shipping"
|
||||
@updated="loadWorkflow" />
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="customs">
|
||||
<ImportWorkflowCustoms
|
||||
:workflow-id="workflowId"
|
||||
:customs="workflow.customs"
|
||||
@updated="loadWorkflow"
|
||||
/>
|
||||
<ImportWorkflowCustoms :workflow-id="workflowId" :customs="workflow.customs"
|
||||
@updated="loadWorkflow" />
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="tickets">
|
||||
<v-card flat>
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center mb-4 gap-2">
|
||||
<v-select
|
||||
v-model="ticketsStatusFilter"
|
||||
:items="ticketStatusOptions"
|
||||
label="فیلتر وضعیت"
|
||||
style="max-width: 260px"
|
||||
clearable
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
@update:model-value="loadRelatedTickets"
|
||||
/>
|
||||
<v-select v-model="ticketsStatusFilter" :items="ticketStatusOptions" label="فیلتر وضعیت"
|
||||
style="max-width: 260px" clearable density="compact" variant="outlined"
|
||||
@update:model-value="loadRelatedTickets" />
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" variant="text" icon="mdi-refresh" @click="loadRelatedTickets" :loading="loadingTickets" />
|
||||
<v-btn color="primary" variant="text" icon="mdi-refresh" @click="loadRelatedTickets"
|
||||
:loading="loadingTickets" />
|
||||
</div>
|
||||
<v-data-table
|
||||
:headers="ticketsHeaders"
|
||||
:header-props="{ class: 'custom-header' }"
|
||||
:items="relatedTickets"
|
||||
:loading="loadingTickets"
|
||||
density="comfortable"
|
||||
class="elevation-1"
|
||||
>
|
||||
<v-data-table :headers="ticketsHeaders" :header-props="{ class: 'custom-header' }"
|
||||
:items="relatedTickets" :loading="loadingTickets" density="comfortable" class="elevation-1">
|
||||
<template #item.code="{ item }">
|
||||
<v-chip color="secondary" variant="tonal" size="small">{{ item.code }}</v-chip>
|
||||
</template>
|
||||
|
@ -369,12 +296,8 @@
|
|||
</v-chip>
|
||||
</template>
|
||||
<template #item.actions="{ item }">
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="text"
|
||||
size="small"
|
||||
@click="$router.push({ name: 'storeroom_ticket_view', params: { id: item.code } })"
|
||||
>
|
||||
<v-btn color="primary" variant="text" size="small"
|
||||
@click="$router.push({ name: 'storeroom_ticket_view', params: { id: item.code } })">
|
||||
مشاهده
|
||||
</v-btn>
|
||||
<v-menu>
|
||||
|
@ -384,7 +307,8 @@
|
|||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item v-for="st in ticketStatusOptions" :key="st.value" @click="updateTicketStatus(item.code, st.value)" :disabled="!st.value">
|
||||
<v-list-item v-for="st in ticketStatusOptions" :key="st.value"
|
||||
@click="updateTicketStatus(item.code, st.value)" :disabled="!st.value">
|
||||
<v-list-item-title>{{ st.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
@ -407,47 +331,27 @@
|
|||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-select
|
||||
v-model="selectedStoreroomId"
|
||||
:items="storerooms"
|
||||
item-title="name"
|
||||
item-value="id"
|
||||
label="انبار"
|
||||
:loading="loadingStorerooms"
|
||||
:disabled="loadingStorerooms"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
required
|
||||
/>
|
||||
<v-select v-model="selectedStoreroomId" :items="storerooms" item-title="name" item-value="id"
|
||||
label="انبار" :loading="loadingStorerooms" :disabled="loadingStorerooms" variant="outlined"
|
||||
density="compact" required />
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<div class="d-flex gap-2">
|
||||
<v-text-field
|
||||
v-model="personSearch"
|
||||
label="جستجوی طرفحساب"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
hide-details
|
||||
/>
|
||||
<v-text-field v-model="personSearch" label="جستجوی طرفحساب" variant="outlined" density="compact"
|
||||
hide-details />
|
||||
<v-btn color="primary" @click="searchPersons" :loading="loadingPersons">جستجو</v-btn>
|
||||
</div>
|
||||
<v-select
|
||||
class="mt-3"
|
||||
v-model="selectedPersonId"
|
||||
:items="persons"
|
||||
item-title="nikename"
|
||||
item-value="id"
|
||||
label="انتخاب طرفحساب"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
<v-select class="mt-3" v-model="selectedPersonId" :items="persons" item-title="nikename" item-value="id"
|
||||
label="انتخاب طرفحساب" variant="outlined" density="compact" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="showCreateTicketDialog = false">انصراف</v-btn>
|
||||
<v-btn color="success" @click="createInboundTicket" :loading="creatingTicket" :disabled="!canCreateTicket">ایجاد حواله</v-btn>
|
||||
<v-btn color="success" @click="createInboundTicket" :loading="creatingTicket"
|
||||
:disabled="!canCreateTicket">ایجاد
|
||||
حواله</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
@ -624,14 +528,32 @@ const rules = {
|
|||
}
|
||||
}
|
||||
|
||||
// Watch for total amount and exchange rate changes
|
||||
const computedTotalAmount = computed(() => {
|
||||
if (!workflow.value?.items) return 0
|
||||
return workflow.value.items.reduce((total, item) => {
|
||||
return total + parseMoney(item.unitPrice * item.quantity)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
const computedTotalAmountIRR = computed(() => {
|
||||
if (!workflow.value?.items) return 0
|
||||
const exchangeRate =
|
||||
parseFloat(editData.value?.exchangeRate) ||
|
||||
parseFloat(workflow.value?.exchangeRate) ||
|
||||
1
|
||||
|
||||
return workflow.value.items.reduce((total, item) => {
|
||||
const itemTotalPrice = parseMoney(item.unitPrice * item.quantity)
|
||||
return total + itemTotalPrice * exchangeRate
|
||||
}, 0)
|
||||
})
|
||||
|
||||
// Watch for exchange rate changes to trigger recomputation
|
||||
watch(
|
||||
() => [editData.value.totalAmount, editData.value.exchangeRate, editData.value.currency],
|
||||
([totalAmount, exchangeRate, currency]) => {
|
||||
const total = parseFloat(totalAmount) || 0
|
||||
const computedRate = currency === 'IRR' ? 1 : (parseFloat(exchangeRate) || 0)
|
||||
const result = Math.round(total * computedRate)
|
||||
editData.value.totalAmountIRR = isNaN(result) ? 0 : result
|
||||
() => [editData.value.exchangeRate, editData.value.currency],
|
||||
([exchangeRate, currency]) => {
|
||||
// مبلغ کل ریالی به صورت خودکار محاسبه میشود
|
||||
// نیازی به بهروزرسانی دستی نیست
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
@ -646,11 +568,8 @@ const loadWorkflow = async () => {
|
|||
workflow.value = response.data.Result
|
||||
editData.value = { ...response.data.Result }
|
||||
|
||||
// Trigger the watch manually after setting editData
|
||||
// مقدار ریالی بر اساس مبلغ و نرخ تبدیل/واحد پول محاسبه میشود
|
||||
const total = parseFloat(editData.value.totalAmount) || 0
|
||||
const rate = editData.value.currency === 'IRR' ? 1 : (parseFloat(editData.value.exchangeRate) || 0)
|
||||
editData.value.totalAmountIRR = Math.round(total * rate)
|
||||
// مبلغ کل به صورت محاسباتی از اقلام محاسبه میشود
|
||||
// نیازی به محاسبه دستی نیست
|
||||
} else {
|
||||
throw new Error(response.data.ErrorMessage)
|
||||
}
|
||||
|
@ -701,7 +620,7 @@ const parseMoneyInput = (val) => {
|
|||
if (val === null || val === undefined) return 0
|
||||
const cleaned = String(val).replace(/,/g, '').replace(/[^\d.-]/g, '')
|
||||
const num = Number(cleaned)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
return Number.isFinite(num) ? parseFloat(num.toFixed(2)) : 0
|
||||
}
|
||||
|
||||
// دکمه ذخیره تنها زمانی فعال شود که فیلدهای کلیدی معتبر باشند
|
||||
|
@ -709,18 +628,11 @@ const isFormValidForSave = computed(() => {
|
|||
const titleOk = typeof editData.value.title === 'string' && editData.value.title.trim().length >= 3
|
||||
const supplierOk = typeof editData.value.supplierName === 'string' && editData.value.supplierName.trim().length >= 3
|
||||
const currencyOk = !!editData.value.currency
|
||||
const total = parseMoneyInput(editData.value.totalAmount)
|
||||
const rate = parseMoneyInput(editData.value.exchangeRate)
|
||||
const totalOk = total >= 0
|
||||
const rateOk = editData.value.currency === 'IRR' ? rate >= 0 : rate > 0
|
||||
return titleOk && supplierOk && currencyOk && totalOk && rateOk && valid.value
|
||||
return titleOk && supplierOk && currencyOk && rateOk && valid.value
|
||||
})
|
||||
|
||||
const onMoneyInput = (field, value) => {
|
||||
const numeric = parseMoneyInput(value)
|
||||
editData.value[field] = numeric
|
||||
}
|
||||
|
||||
const getStatusColor = (status) => {
|
||||
const colors = {
|
||||
draft: 'grey',
|
||||
|
@ -734,32 +646,39 @@ const getStatusColor = (status) => {
|
|||
return colors[status] || 'grey'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const texts = {
|
||||
draft: 'پیشنویس',
|
||||
processing: 'در حال پردازش',
|
||||
shipped: 'ارسال شده',
|
||||
arrived: 'رسیده',
|
||||
cleared: 'ترخیص شده',
|
||||
completed: 'تکمیل شده',
|
||||
cancelled: 'لغو شده'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
const formatNumber = (number) => {
|
||||
if (!number) return '0'
|
||||
return new Intl.NumberFormat('fa-IR').format(number)
|
||||
}
|
||||
|
||||
// نمایش مبالغ بدون اعشار و با جداکننده ویرگول بین هر سه رقم
|
||||
// نمایش مبالغ با اعشار و با جداکننده ویرگول بین هر سه رقم
|
||||
const formatMoney = (value) => {
|
||||
const numericValue = Number(value) || 0
|
||||
return numericValue
|
||||
.toFixed(0)
|
||||
.toFixed(2)
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
|
||||
const parseMoney = (val) => {
|
||||
if (val === null || val === undefined || val === '') return 0
|
||||
const clean = String(val).replace(/,/g, '')
|
||||
const num = parseFloat(clean)
|
||||
return isNaN(num) ? 0 : parseFloat(num.toFixed(2))
|
||||
}
|
||||
|
||||
const formatMoneyTyping = (val) => {
|
||||
if (val === null || val === undefined || val === '') return ''
|
||||
const str = String(val).replace(/,/g, '')
|
||||
if (str === '') return ''
|
||||
const parts = str.split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
const onMoneyInput = (field, val) => {
|
||||
editData.value[field] = parseMoney(val)
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '-'
|
||||
return new Date(date).toLocaleDateString('fa-IR')
|
||||
|
@ -815,7 +734,3 @@ onMounted(async () => {
|
|||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,129 +1,24 @@
|
|||
<template>
|
||||
<v-toolbar color="toolbar" :title="$t('dialog.warranty_page_title')">
|
||||
<template v-slot:prepend>
|
||||
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||
icon="mdi-arrow-right" />
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="info" variant="outlined" prepend-icon="mdi-share-variant" @click="openActivationLinkDialog"
|
||||
class="ml-2">
|
||||
اشتراک گذاری لینک فعالسازی
|
||||
</v-btn>
|
||||
<v-btn color="primary" variant="outlined" prepend-icon="mdi-cog" @click="goToWarrantySettings">
|
||||
تنظیمات گارانتی
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<div class="warranty-plugin">
|
||||
<v-container fluid>
|
||||
<!-- <v-row>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div class="stats-card total-card">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-barcode-scan</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-barcode-scan</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">{{ stats.totalSerials || 0 }}</div>
|
||||
<div class="stats-label">کل سریالها</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div :class="getCardClasses('available')" @click="filterByStatus('available')" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-check-circle</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-check-circle</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.byStatus.available || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">آزاد</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div :class="getCardClasses('allocated')" @click="filterByStatus('allocated')" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-package-variant-closed</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-package-variant-closed</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.byStatus.allocated || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">تخصیص یافته</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div :class="getCardClasses('verified')" @click="filterByStatus('verified')" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-clipboard-check</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-clipboard-check</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.byStatus.verified || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">تأیید شده</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row class="mt-2">
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div :class="getCardClasses('bound')" @click="filterByStatus('bound')" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-link-variant</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-link-variant</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.byStatus.bound || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">متصل</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div :class="getCardClasses('consumed')" @click="filterByStatus('consumed')" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-check-decagram</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-check-decagram</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.byStatus.consumed || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">مصرف شده</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div :class="getCardClasses('void')" @click="filterByStatus('void')" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-cancel</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-cancel</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.byStatus.void || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">باطل</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" sm="6" md="3">
|
||||
<div class="stats-card expired-card" role="button" tabindex="0">
|
||||
<div class="stats-icon">
|
||||
<v-icon size="24" color="white" class="d-sm-none">mdi-clock-alert</v-icon>
|
||||
<v-icon size="32" color="white" class="d-none d-sm-block">mdi-clock-alert</v-icon>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="stats-number">
|
||||
<v-progress-circular v-if="statsLoading" indeterminate size="20" color="white" class="me-2" />
|
||||
{{ stats.expiredFlagCount || 0 }}
|
||||
</div>
|
||||
<div class="stats-label">دارای پایان گارانتی گذشته</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row> -->
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-card>
|
||||
|
@ -258,6 +153,60 @@
|
|||
<BulkImportDialog v-model="showBulkImportDialog" :commodities="commodities" @import="bulkImport"
|
||||
@close="closeBulkImportDialog" />
|
||||
|
||||
<!-- Activation Link Dialog -->
|
||||
<v-dialog v-model="showActivationLinkDialog" max-width="600">
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center" style="padding: 20px !important;">
|
||||
<v-icon class="ml-2" color="info">mdi-share-variant</v-icon>
|
||||
اشتراک گذاری لینک فعالسازی گارانتی
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert type="info" variant="tonal" class="mb-4">
|
||||
<strong>نکته:</strong> این لینک برای فعالسازی گارانتی توسط مشتریان استفاده میشود.
|
||||
</v-alert>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="text-body-2 font-weight-medium mb-2 d-block">لینک فعالسازی گارانتی:</label>
|
||||
<div class="d-flex align-center flex-row-reverse gap-2">
|
||||
<v-text-field :model-value="activationLink" readonly variant="outlined" density="compact"
|
||||
class="flex-grow-1 text-left" hide-details></v-text-field>
|
||||
<v-btn color="primary" variant="tonal" @click="copyActivationLink" class="ml-2" :loading="copying">
|
||||
<v-icon>mdi-content-copy</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h4 class="text-h6 mb-3">راهنمای استفاده:</h4>
|
||||
<v-list density="compact">
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon color="primary" size="small">mdi-numeric-1-circle</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>این لینک را برای مشتریان ارسال کنید</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon color="primary" size="small">mdi-numeric-2-circle</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>مشتری با مراجعه به لینک میتواند گارانتی خود را فعال کند</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon color="primary" size="small">mdi-numeric-3-circle</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>پس از فعالسازی، وضعیت گارانتی در سیستم بهروزرسانی میشود</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="showActivationLinkDialog = false">بستن</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showDeleteDialog" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title>تأیید حذف</v-card-title>
|
||||
|
@ -286,8 +235,6 @@
|
|||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- تنظیمات گارانتی به تب تنظیمات کسبوکار منتقل شد -->
|
||||
|
||||
<v-snackbar v-model="showSnackbar" :color="snackbarColor" :timeout="3000" location="bottom" class="rounded-lg"
|
||||
elevation="2">
|
||||
<div class="d-flex align-center">
|
||||
|
@ -350,7 +297,9 @@ const showAddDialog = ref(false)
|
|||
const showViewDialog = ref(false)
|
||||
const showDeleteDialog = ref(false)
|
||||
const showBulkImportDialog = ref(false)
|
||||
const showActivationLinkDialog = ref(false)
|
||||
const selectedSerial = ref<any>(null)
|
||||
const copying = ref(false)
|
||||
const settings = ref({
|
||||
requireWarrantyOnDelivery: false,
|
||||
activationGraceDays: 0,
|
||||
|
@ -724,6 +673,35 @@ const formatDate = (dateVal: any) => {
|
|||
}
|
||||
}
|
||||
|
||||
const activationLink = ref('')
|
||||
const businessId = ref(localStorage.getItem('activeBid') || '')
|
||||
|
||||
const generateActivationLink = () => {
|
||||
const baseUrl = window.location.origin + '/u/public/' + businessId.value + '/warranty-activation'
|
||||
activationLink.value = baseUrl
|
||||
}
|
||||
|
||||
const copyActivationLink = async () => {
|
||||
try {
|
||||
copying.value = true
|
||||
await navigator.clipboard.writeText(activationLink.value)
|
||||
showNotification('لینک با موفقیت کپی شد', 'success')
|
||||
} catch (err) {
|
||||
showNotification('خطا در کپی کردن لینک', 'error')
|
||||
} finally {
|
||||
copying.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const goToWarrantySettings = () => {
|
||||
router.push('/acc/business/settings')
|
||||
}
|
||||
|
||||
const openActivationLinkDialog = () => {
|
||||
generateActivationLink()
|
||||
showActivationLinkDialog.value = true
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadSerials(), loadCommodities()])
|
||||
// await loadStats()
|
||||
|
|
|
@ -524,7 +524,7 @@ onMounted(() => {
|
|||
:headers="[
|
||||
{ title: 'سریال گارانتی', key: 'serialNumber' },
|
||||
{ title: 'کالا', key: 'commodity' },
|
||||
{ title: 'وضعیت', key: 'status' },
|
||||
// { title: 'وضعیت', key: 'status' },
|
||||
{ title: 'فعالسازی', key: 'activation' },
|
||||
// { title: 'کد فعالسازی', key: 'activationTicketCode' },
|
||||
{ title: 'اتمام گارانتی', key: 'warrantyEndDate' }
|
||||
|
|
|
@ -56,6 +56,9 @@ export default defineComponent({
|
|||
chequeInput: '',
|
||||
passChequeInput: '',
|
||||
rejectChequeInput: ''
|
||||
},
|
||||
plugWarranty: {
|
||||
sendSerial: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,6 +239,14 @@ export default defineComponent({
|
|||
v-model="form.plugRepservice.creating" type="text" prepend-inner-icon="mdi-card-text"
|
||||
:rules="[() => Number(form.plugRepservice.creating) > 0 || $t('validator.required')]"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<h4 class="text-primary">افزونه گارانتی</h4>
|
||||
<v-row class="mb-2">
|
||||
<v-col cols="12" sm="12" md="4">
|
||||
<v-text-field class="" hide-details="auto" :label="$t('pages.manager.sms_settings_warranty_send_serial')"
|
||||
v-model="form.plugWarranty.sendSerial" type="text" prepend-inner-icon="mdi-card-text"
|
||||
:rules="[() => Number(form.plugWarranty.sendSerial) > 0 || $t('validator.required')]"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<v-btn type="submit" @click="submit()" color="primary" prepend-icon="mdi-content-save" :loading="loading">
|
||||
{{ $t('dialog.save') }}
|
||||
|
|
Loading…
Reference in a new issue