progress and some bug fix in commodity,ai
This commit is contained in:
parent
91cf5d4eb6
commit
aaeb3cf31e
29
hesabixCore/migrations/Version20241201000001.php
Normal file
29
hesabixCore/migrations/Version20241201000001.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?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 Version20241201000001 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add customCode column to commodity table';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE commodity ADD customCode TINYINT(1) DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE commodity DROP customCode');
|
||||
}
|
||||
}
|
|
@ -30,23 +30,62 @@ class CommodityService
|
|||
$em = $this->entityManager;
|
||||
if (!isset($params['name']) || trim($params['name']) === '')
|
||||
return ['result' => -1, 'error' => 'نام کالا الزامی است'];
|
||||
|
||||
if ($code == 0) {
|
||||
// افزودن کالای جدید
|
||||
$data = $em->getRepository(Commodity::class)->findOneBy([
|
||||
'name' => $params['name'],
|
||||
'bid' => $acc['bid']
|
||||
]);
|
||||
if (!$data) {
|
||||
$data = new Commodity();
|
||||
$data->setCode((new \App\Service\Provider($em))->getAccountingCode($acc['bid'], 'Commodity'));
|
||||
|
||||
// بررسی کد سفارشی
|
||||
if (isset($params['customCode']) && $params['customCode'] === true && isset($params['code'])) {
|
||||
// بررسی تکراری نبودن کد سفارشی
|
||||
$existingCommodity = $em->getRepository(Commodity::class)->findOneBy([
|
||||
'code' => $params['code'],
|
||||
'bid' => $acc['bid']
|
||||
]);
|
||||
if ($existingCommodity) {
|
||||
return ['result' => 2, 'error' => 'کد کالا تکراری است'];
|
||||
}
|
||||
$data->setCode($params['code']);
|
||||
$data->setCustomCode(true);
|
||||
} else {
|
||||
// کد اتوماتیک
|
||||
$data->setCode((new \App\Service\Provider($em))->getAccountingCode($acc['bid'], 'Commodity'));
|
||||
$data->setCustomCode(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ویرایش کالای موجود
|
||||
$data = $em->getRepository(Commodity::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'code' => $code
|
||||
]);
|
||||
if (!$data)
|
||||
return ['result' => -2, 'error' => 'کالا یافت نشد'];
|
||||
|
||||
// بررسی کد سفارشی در زمان ویرایش
|
||||
if (isset($params['customCode']) && $params['customCode'] === true && isset($params['code'])) {
|
||||
// بررسی تکراری نبودن کد سفارشی (به جز خود کالا)
|
||||
$existingCommodity = $em->getRepository(Commodity::class)->findOneBy([
|
||||
'code' => $params['code'],
|
||||
'bid' => $acc['bid']
|
||||
]);
|
||||
if ($existingCommodity && $existingCommodity->getId() !== $data->getId()) {
|
||||
return ['result' => 2, 'error' => 'کد کالا تکراری است'];
|
||||
}
|
||||
$data->setCode($params['code']);
|
||||
$data->setCustomCode(true);
|
||||
} elseif (isset($params['customCode']) && $params['customCode'] === false) {
|
||||
// تغییر به کد اتوماتیک
|
||||
$data->setCode((new \App\Service\Provider($em))->getAccountingCode($acc['bid'], 'Commodity'));
|
||||
$data->setCustomCode(false);
|
||||
}
|
||||
}
|
||||
|
||||
$unit = null;
|
||||
if (!isset($params['unit']))
|
||||
$unit = $em->getRepository(CommodityUnit::class)->findAll()[0];
|
||||
|
|
|
@ -267,6 +267,7 @@ class CommodityController extends AbstractController
|
|||
$temp['taxCode'] = $item->getTaxCode();
|
||||
$temp['taxType'] = $item->getTaxType();
|
||||
$temp['taxUnit'] = $item->getTaxUnit();
|
||||
$temp['customCode'] = $item->isCustomCode();
|
||||
//calculate count
|
||||
if ($item->isKhadamat()) {
|
||||
$temp['count'] = 0;
|
||||
|
@ -334,6 +335,7 @@ class CommodityController extends AbstractController
|
|||
$temp['taxCode'] = $item->getTaxCode();
|
||||
$temp['taxType'] = $item->getTaxType();
|
||||
$temp['taxUnit'] = $item->getTaxUnit();
|
||||
$temp['customCode'] = $item->isCustomCode();
|
||||
//calculate count
|
||||
if ($item->isKhadamat()) {
|
||||
$temp['count'] = 0;
|
||||
|
@ -429,6 +431,7 @@ class CommodityController extends AbstractController
|
|||
$temp['taxCode'] = $item->getTaxCode();
|
||||
$temp['taxType'] = $item->getTaxType();
|
||||
$temp['taxUnit'] = $item->getTaxUnit();
|
||||
$temp['customCode'] = $item->isCustomCode();
|
||||
//calculate count
|
||||
if ($item->isKhadamat()) {
|
||||
$temp['count'] = 0;
|
||||
|
@ -528,7 +531,8 @@ class CommodityController extends AbstractController
|
|||
$temp[] = $item->getMinOrderCount();
|
||||
$temp[] = $item->getDes();
|
||||
$temp[] = $item->getUnit()->getName();
|
||||
$temp[] = $item->getCat()->getName();
|
||||
$cat = $item->getCat();
|
||||
$temp[] = $cat ? $cat->getName() : '';
|
||||
$array[] = $temp;
|
||||
}
|
||||
$filePath = $provider->createExcellFromArray($array, [
|
||||
|
@ -604,20 +608,28 @@ class CommodityController extends AbstractController
|
|||
return $this->json(['id' => $pid]);
|
||||
}
|
||||
|
||||
#[Route('/api/commodity/info/{code}', name: 'app_commodity_info')]
|
||||
public function app_commodity_info($code, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||
#[Route('/api/commodity/info/{id}', name: 'app_commodity_info')]
|
||||
public function app_commodity_info($id, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('commodity');
|
||||
if (!$acc)
|
||||
throw $this->createAccessDeniedException();
|
||||
$data = $entityManager->getRepository(Commodity::class)->findOneBy([
|
||||
'bid' => $acc['bid'],
|
||||
'code' => $code
|
||||
'code' => $id
|
||||
]);
|
||||
|
||||
if (!$data) {
|
||||
return $this->json(['error' => 'کالا یافت نشد'], 404);
|
||||
}
|
||||
|
||||
$res = Explore::ExploreCommodity($data);
|
||||
$res['cat'] = '';
|
||||
if ($data->getCat())
|
||||
$res['cat'] = $data->getCat()->getId();
|
||||
$cat = $data->getCat();
|
||||
if ($cat !== null) {
|
||||
$res['cat'] = $cat->getId();
|
||||
}
|
||||
$res['customCode'] = $data->isCustomCode();
|
||||
$count = 0;
|
||||
//calculate count
|
||||
if ($data->isKhadamat()) {
|
||||
|
@ -813,8 +825,8 @@ class CommodityController extends AbstractController
|
|||
'result' => 1,
|
||||
]);
|
||||
}
|
||||
#[Route('/api/commodity/mod/{code}', name: 'app_commodity_mod')]
|
||||
public function app_commodity_mod(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse
|
||||
#[Route('/api/commodity/mod/{id}', name: 'app_commodity_mod')]
|
||||
public function app_commodity_mod(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $id = 0): JsonResponse
|
||||
{
|
||||
$acc = $access->hasRole('commodity');
|
||||
if (!$acc)
|
||||
|
@ -824,7 +836,7 @@ class CommodityController extends AbstractController
|
|||
$params = json_decode($content, true);
|
||||
}
|
||||
$commodityService = new \App\Cog\CommodityService($entityManager);
|
||||
$result = $commodityService->addOrUpdateCommodity($params, $acc, $code);
|
||||
$result = $commodityService->addOrUpdateCommodity($params, $acc, $id);
|
||||
if (isset($result['error'])) {
|
||||
return $this->json($result, 400);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ class Commodity
|
|||
#[ORM\Column(type: 'bigint')]
|
||||
private $code;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?bool $customCode = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
private $priceBuy;
|
||||
|
||||
|
@ -178,6 +181,18 @@ class Commodity
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function isCustomCode(): ?bool
|
||||
{
|
||||
return $this->customCode;
|
||||
}
|
||||
|
||||
public function setCustomCode(?bool $customCode): static
|
||||
{
|
||||
$this->customCode = $customCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPriceBuy(): ?int
|
||||
{
|
||||
return $this->priceBuy;
|
||||
|
|
|
@ -236,6 +236,7 @@ class Explore
|
|||
'taxCode' => $item->getTaxCode(),
|
||||
'taxType' => $item->getTaxType(),
|
||||
'taxUnit' => $item->getTaxUnit(),
|
||||
'customCode' => $item->isCustomCode(),
|
||||
'unitData' => [
|
||||
'name' => $item->getUnit()->getName(),
|
||||
'floatNumber' => $item->getUnit()->getFloatNumber(),
|
||||
|
@ -245,8 +246,9 @@ class Explore
|
|||
if ($des) {
|
||||
$result['des'] = $des;
|
||||
}
|
||||
if ($item->getCat()) {
|
||||
$result['cat'] = $item->getCat()->getName();
|
||||
$cat = $item->getCat();
|
||||
if ($cat !== null) {
|
||||
$result['cat'] = $cat->getName();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,8 +29,12 @@
|
|||
<v-tabs-window v-model="tabs">
|
||||
<!-- اطلاعات پایه -->
|
||||
<v-tabs-window-item value="0">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-2">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="text-white bg-primary">
|
||||
<v-icon start icon="mdi-account" class="text-white"></v-icon>
|
||||
{{ $t('pages.person.basic_info') }}
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row dense>
|
||||
<v-col cols="12">
|
||||
<v-switch v-model="person.speedAccess" :label="$t('pages.person.speed_access')"
|
||||
|
@ -68,8 +72,12 @@
|
|||
|
||||
<!-- اطلاعات اقتصادی -->
|
||||
<v-tabs-window-item value="1">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-2">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="text-white bg-primary">
|
||||
<v-icon start icon="mdi-chart-line" class="text-white"></v-icon>
|
||||
{{ $t('pages.person.eco_info') }}
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="person.shenasemeli" :label="$t('pages.person.national_id')" dense
|
||||
|
@ -90,8 +98,12 @@
|
|||
|
||||
<!-- اطلاعات تماس -->
|
||||
<v-tabs-window-item value="2">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-2">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="text-white bg-primary">
|
||||
<v-icon start icon="mdi-phone" class="text-white"></v-icon>
|
||||
{{ $t('pages.person.contact_info') }}
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="person.mobile" :label="$t('pages.person.mobile')" dense
|
||||
|
@ -124,8 +136,12 @@
|
|||
|
||||
<!-- آدرس -->
|
||||
<v-tabs-window-item value="3">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-2">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="text-white bg-primary">
|
||||
<v-icon start icon="mdi-map-marker" class="text-white"></v-icon>
|
||||
{{ $t('pages.person.address') }}
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="person.keshvar" :label="$t('pages.person.country')" dense
|
||||
|
@ -158,8 +174,12 @@
|
|||
|
||||
<!-- حسابهای بانکی -->
|
||||
<v-tabs-window-item value="4">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-2">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="text-white bg-primary">
|
||||
<v-icon start icon="mdi-bank" class="text-white"></v-icon>
|
||||
{{ $t('pages.person.banks_accounts') }}
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row justify="end" class="mb-2">
|
||||
<v-col cols="auto">
|
||||
<v-btn color="primary" @click="addNewCard" prepend-icon="mdi-plus" size="small">
|
||||
|
|
|
@ -8,32 +8,51 @@
|
|||
absolute
|
||||
style="top:0; left:0; right:0; z-index:2000;"
|
||||
/>
|
||||
<!-- دیالوگ مدیریت گفتوگوها - ساختار استاندارد Vuetify -->
|
||||
<!-- دیالوگ مدیریت گفتوگوها - ساختار بهبود یافته -->
|
||||
<v-dialog v-model="showConversations" max-width="420" scrollable transition="dialog-bottom-transition">
|
||||
<v-card>
|
||||
<v-toolbar color="primary" dark flat rounded>
|
||||
<v-avatar color="white" size="36" class="mr-3"><v-icon color="primary">mdi-forum</v-icon></v-avatar>
|
||||
<v-toolbar-title class="font-weight-bold">مدیریت گفتوگوها</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<!-- دکمه حذف همه گفتوگوها با تولتیپ -->
|
||||
<v-tooltip text="حذف همه گفتوگوها" location="bottom">
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
color="error"
|
||||
:loading="loadingDeleteAll"
|
||||
icon
|
||||
class="ml-1"
|
||||
@click="showDeleteAllDialog = true"
|
||||
>
|
||||
<v-icon>mdi-delete-sweep</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<!-- دکمه بستن دیالوگ -->
|
||||
<v-btn icon variant="text" @click="showConversations = false"><v-icon>mdi-close</v-icon></v-btn>
|
||||
</v-toolbar>
|
||||
<v-divider></v-divider>
|
||||
<v-card class="conversation-dialog-card">
|
||||
<!-- هدر بهبود یافته -->
|
||||
<div class="conversation-dialog-header">
|
||||
<div class="header-content">
|
||||
<div class="header-icon-wrapper">
|
||||
<v-icon size="28" color="white">mdi-forum</v-icon>
|
||||
</div>
|
||||
<div class="header-text">
|
||||
<h3 class="header-title">مدیریت گفتوگوها</h3>
|
||||
<p class="header-subtitle">{{ conversations.length }} گفتوگو موجود</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<!-- دکمه حذف همه گفتوگوها با تولتیپ -->
|
||||
<v-tooltip text="حذف همه گفتوگوها" location="bottom">
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
color="white"
|
||||
variant="text"
|
||||
:loading="loadingDeleteAll"
|
||||
icon
|
||||
size="small"
|
||||
class="header-action-btn delete-all-btn"
|
||||
@click="showDeleteAllDialog = true"
|
||||
>
|
||||
<v-icon size="20">mdi-delete-sweep</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<!-- دکمه بستن دیالوگ -->
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
color="white"
|
||||
size="small"
|
||||
class="header-action-btn close-btn"
|
||||
@click="showConversations = false"
|
||||
>
|
||||
<v-icon size="20">mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
<v-list class="py-0" style="min-height: 320px; max-height: 60vh; overflow-y: auto;">
|
||||
<template v-if="conversations.length">
|
||||
<template v-for="(conv, idx) in conversations" :key="conv.id">
|
||||
|
@ -66,14 +85,19 @@
|
|||
</div>
|
||||
</template>
|
||||
</v-list>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions class="pa-4 d-flex flex-row-reverse align-center justify-space-between">
|
||||
<v-btn color="primary" block large class="font-weight-bold" @click="createConversation">
|
||||
<v-icon start>mdi-plus</v-icon>
|
||||
<div class="conversation-dialog-footer">
|
||||
<v-btn
|
||||
color="primary"
|
||||
block
|
||||
size="large"
|
||||
class="new-conversation-btn"
|
||||
@click="createConversation"
|
||||
:loading="loadingConversation"
|
||||
>
|
||||
<v-icon start size="20">mdi-plus</v-icon>
|
||||
گفتوگوی جدید
|
||||
</v-btn>
|
||||
<!-- دکمه حذف همه را از پایین (v-card-actions) حذف کن -->
|
||||
</v-card-actions>
|
||||
</div>
|
||||
<!-- دیالوگ تایید حذف -->
|
||||
<v-dialog v-model="showDeleteDialog" max-width="320">
|
||||
<v-card>
|
||||
|
@ -723,7 +747,7 @@ export default {
|
|||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
gap: 12px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
|
@ -782,7 +806,7 @@ export default {
|
|||
}
|
||||
|
||||
.message-text {
|
||||
margin: 0 0 4px 0;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@ -929,6 +953,10 @@ export default {
|
|||
.message {
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
|
@ -1111,4 +1139,115 @@ export default {
|
|||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* استایلهای جدید دیالوگ گفتوگو */
|
||||
.conversation-dialog-card {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.conversation-dialog-header {
|
||||
background: linear-gradient(135deg, #1976d2 0%, #42a5f5 100%);
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.conversation-dialog-header::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.header-icon-wrapper {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.header-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.header-action-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.header-action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.delete-all-btn:hover {
|
||||
background: rgba(244, 67, 54, 0.2);
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.conversation-dialog-footer {
|
||||
padding: 20px 24px;
|
||||
background: #fafafa;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.new-conversation-btn {
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
text-transform: none;
|
||||
letter-spacing: 0.5px;
|
||||
box-shadow: 0 4px 16px rgba(25, 118, 210, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.new-conversation-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(25, 118, 210, 0.3);
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue