update for Moadian Plugin
This commit is contained in:
parent
300d802ee8
commit
140da029a1
|
@ -1172,10 +1172,24 @@ class TaxSettingsController extends AbstractController
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($buyerPerson) {
|
if ($buyerPerson) {
|
||||||
$buyerNationalId = $buyerPerson->getShenasemeli();
|
$buyerNationalId = $buyerPerson->getShenasemeli();
|
||||||
$buyerEconomicCode = $buyerPerson->getCodeeghtesadi();
|
$buyerEconomicCode = $buyerPerson->getCodeeghtesadi();
|
||||||
|
|
||||||
|
if (empty($buyerNationalId) || trim($buyerNationalId) === '') {
|
||||||
|
$buyerNationalId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($buyerEconomicCode) || trim($buyerEconomicCode) === '') {
|
||||||
|
$buyerEconomicCode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$personType = 1;
|
||||||
|
|
||||||
|
if (count_chars($buyerNationalId ) == 11) {
|
||||||
|
$personType = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
$dateTime = new DateTime();
|
$dateTime = new DateTime();
|
||||||
|
@ -1189,7 +1203,7 @@ class TaxSettingsController extends AbstractController
|
||||||
->setInp(1)
|
->setInp(1)
|
||||||
->setIns(1)
|
->setIns(1)
|
||||||
->setTins($taxId)
|
->setTins($taxId)
|
||||||
->setTob(1)
|
->setTob($personType)
|
||||||
->setBid($buyerNationalId)
|
->setBid($buyerNationalId)
|
||||||
->setTinb($buyerEconomicCode)
|
->setTinb($buyerEconomicCode)
|
||||||
->setSbc(null)
|
->setSbc(null)
|
||||||
|
@ -1504,5 +1518,143 @@ class TaxSettingsController extends AbstractController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/plugins/tax/invoice/validate-buyer-info/{id}', name: 'plugin_tax_invoice_validate_buyer_info', methods: ['POST'])]
|
||||||
|
public function validateBuyerInfo(int $id, Access $access, Log $log, EntityManagerInterface $em): JsonResponse
|
||||||
|
{
|
||||||
|
$acc = $access->hasRole('plugTaxSettings');
|
||||||
|
if (!$acc) {
|
||||||
|
throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$businessId = is_object($acc['bid']) ? $acc['bid']->getId() : $acc['bid'];
|
||||||
|
$user = $this->getUser();
|
||||||
|
$userId = $user instanceof \App\Entity\User ? $user->getId() : null;
|
||||||
|
|
||||||
|
$repo = $em->getRepository(PluginTaxsettingsKey::class);
|
||||||
|
$taxSettings = $repo->findOneBy(['business_id' => $businessId, 'user_id' => $userId]);
|
||||||
|
|
||||||
|
if (!$taxSettings || !$taxSettings->getPrivateKey() || !$taxSettings->getTaxMemoryId()) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'تنظیمات مالیاتی تکمیل نشده است. لطفاً ابتدا تنظیمات را تکمیل کنید.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxInvoiceRepo = $em->getRepository(PluginTaxInvoice::class);
|
||||||
|
$taxInvoice = $taxInvoiceRepo->findOneBy([
|
||||||
|
'id' => $id,
|
||||||
|
'business' => $businessId
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$taxInvoice) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'فاکتور مالیاتی یافت نشد.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoice = $taxInvoice->getInvoice();
|
||||||
|
if (!$invoice) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'فاکتور یافت نشد.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invoice->getBid()->getId() != $businessId) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'فاکتور متعلق به این کسب و کار نیست.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$buyerInfo = $this->validateBuyerEconomicInfo($invoice);
|
||||||
|
|
||||||
|
if (!$buyerInfo['is_valid']) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'اطلاعات اقتصادی خریدار ناقص است.',
|
||||||
|
'buyer_info' => $buyerInfo,
|
||||||
|
'can_proceed' => false
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'اطلاعات اقتصادی خریدار کامل است.',
|
||||||
|
'buyer_info' => $buyerInfo,
|
||||||
|
'can_proceed' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* بررسی اطلاعات اقتصادی خریدار
|
||||||
|
*/
|
||||||
|
private function validateBuyerEconomicInfo($invoice): array
|
||||||
|
{
|
||||||
|
$buyerPerson = null;
|
||||||
|
$buyerNationalId = null;
|
||||||
|
$buyerEconomicCode = null;
|
||||||
|
$missingFields = [];
|
||||||
|
|
||||||
|
// دریافت شخص خریدار از ردیفهای فاکتور
|
||||||
|
foreach ($invoice->getHesabdariRows() as $row) {
|
||||||
|
if ($row->getPerson()) {
|
||||||
|
$buyerPerson = $row->getPerson();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$buyerPerson) {
|
||||||
|
return [
|
||||||
|
'is_valid' => false,
|
||||||
|
'message' => 'خریدار در فاکتور مشخص نشده است.',
|
||||||
|
'buyer_name' => null,
|
||||||
|
'national_id' => null,
|
||||||
|
'economic_code' => null,
|
||||||
|
'missing_fields' => ['buyer_not_found']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$buyerNationalId = $buyerPerson->getShenasemeli();
|
||||||
|
$buyerEconomicCode = $buyerPerson->getCodeeghtesadi();
|
||||||
|
|
||||||
|
// بررسی شناسه ملی
|
||||||
|
if (empty($buyerNationalId) || trim($buyerNationalId) === '') {
|
||||||
|
$missingFields[] = 'national_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی کد اقتصادی
|
||||||
|
if (empty($buyerEconomicCode) || trim($buyerEconomicCode) === '') {
|
||||||
|
$missingFields[] = 'economic_code';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'is_valid' => empty($missingFields),
|
||||||
|
'buyer_name' => $buyerPerson->getNikename(),
|
||||||
|
'buyer_id' => $buyerPerson->getId(),
|
||||||
|
'buyer_code' => $buyerPerson->getCode(),
|
||||||
|
'national_id' => $buyerNationalId,
|
||||||
|
'economic_code' => $buyerEconomicCode,
|
||||||
|
'missing_fields' => $missingFields
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!empty($missingFields)) {
|
||||||
|
$missingFieldsText = [];
|
||||||
|
if (in_array('national_id', $missingFields)) {
|
||||||
|
$missingFieldsText[] = 'شناسه ملی';
|
||||||
|
}
|
||||||
|
if (in_array('economic_code', $missingFields)) {
|
||||||
|
$missingFieldsText[] = 'کد اقتصادی';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result['message'] = 'اطلاعات اقتصادی خریدار ناقص است. فیلدهای زیر تکمیل نشدهاند: ' . implode('، ', $missingFieldsText);
|
||||||
|
} else {
|
||||||
|
$result['message'] = 'اطلاعات اقتصادی خریدار کامل است.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -253,6 +253,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
bulkLoading: false,
|
bulkLoading: false,
|
||||||
|
checkLoading: false,
|
||||||
selectedInvoices: [],
|
selectedInvoices: [],
|
||||||
invoices: [],
|
invoices: [],
|
||||||
snackbar: {
|
snackbar: {
|
||||||
|
@ -342,7 +343,7 @@ export default {
|
||||||
return 'خطا';
|
return 'خطا';
|
||||||
case 'FAILED':
|
case 'FAILED':
|
||||||
return 'خطا';
|
return 'خطا';
|
||||||
case 'ACCEPTED':
|
case 'SUCCESS':
|
||||||
return 'تایید شده';
|
return 'تایید شده';
|
||||||
default:
|
default:
|
||||||
return 'نامشخص';
|
return 'نامشخص';
|
||||||
|
@ -360,7 +361,7 @@ export default {
|
||||||
return 'grey';
|
return 'grey';
|
||||||
case 'FAILED':
|
case 'FAILED':
|
||||||
return 'error';
|
return 'error';
|
||||||
case 'ACCEPTED':
|
case 'SUCCESS':
|
||||||
return 'success';
|
return 'success';
|
||||||
default:
|
default:
|
||||||
return 'primary';
|
return 'primary';
|
||||||
|
@ -533,13 +534,62 @@ export default {
|
||||||
},
|
},
|
||||||
async performSend(item) {
|
async performSend(item) {
|
||||||
item.sending = true;
|
item.sending = true;
|
||||||
|
try {
|
||||||
|
const validateResponse = await axios.post(`/api/plugins/tax/invoice/validate-buyer-info/${item.id}`);
|
||||||
|
|
||||||
|
if (!validateResponse.data.success && !validateResponse.data.can_proceed) {
|
||||||
|
if (validateResponse.data.buyer_info) {
|
||||||
|
const buyerInfo = validateResponse.data.buyer_info;
|
||||||
|
let message = `اطلاعات اقتصادی خریدار ناقص است.\n\n`;
|
||||||
|
message += `خریدار: ${buyerInfo.buyer_name || 'نامشخص'}`;
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'اطلاعات اقتصادی ناقص',
|
||||||
|
html: message.replace(/\n/g, '<br>'),
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'ارسال بدون اطلاعات خریدار',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'ویرایش خریدار',
|
||||||
|
showDenyButton: true,
|
||||||
|
denyButtonText: 'انصراف'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
this.sendWithoutBuyerInfo(item);
|
||||||
|
} else if (result.dismiss === Swal.DismissReason.cancel && buyerInfo.buyer_code) {
|
||||||
|
this.$router.push(`/acc/persons/mod/${buyerInfo.buyer_code}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'خطا در بررسی اطلاعات',
|
||||||
|
text: validateResponse.data.message || 'خطا در بررسی اطلاعات اقتصادی خریدار',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'باشه'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (validateResponse.data.can_proceed) {
|
||||||
|
this.sendWithoutBuyerInfo(item);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'خطا در ارسال فاکتور',
|
||||||
|
text: 'خطا در ارسال فاکتور: ' + (error.response?.data?.message || error.message),
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'باشه'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
item.sending = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async sendWithoutBuyerInfo(item) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`/api/plugins/tax/invoice/send/${item.id}`);
|
const response = await axios.post(`/api/plugins/tax/invoice/send/${item.id}`);
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'ارسال موفق',
|
title: 'ارسال موفق',
|
||||||
text: 'فاکتور با موفقیت به سامانه مودیان مالیاتی ارسال شد',
|
text: 'فاکتور بدون اطلاعات خریدار به سامانه مودیان مالیاتی ارسال شد',
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
confirmButtonText: 'باشه'
|
confirmButtonText: 'باشه'
|
||||||
});
|
});
|
||||||
|
@ -559,8 +609,6 @@ export default {
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
confirmButtonText: 'باشه'
|
confirmButtonText: 'باشه'
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
item.sending = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sendBulkInvoices() {
|
sendBulkInvoices() {
|
||||||
|
@ -589,8 +637,65 @@ export default {
|
||||||
async performBulkSend(selectedItems) {
|
async performBulkSend(selectedItems) {
|
||||||
this.bulkLoading = true;
|
this.bulkLoading = true;
|
||||||
try {
|
try {
|
||||||
|
const validationPromises = selectedItems.map(item =>
|
||||||
|
axios.post(`/api/plugins/tax/invoice/validate-buyer-info/${item.id}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
const validationResults = await Promise.all(validationPromises);
|
||||||
|
|
||||||
|
const invalidInvoices = [];
|
||||||
|
const validInvoices = [];
|
||||||
|
|
||||||
|
validationResults.forEach((result, index) => {
|
||||||
|
if (!result.data.success) {
|
||||||
|
invalidInvoices.push({
|
||||||
|
...selectedItems[index],
|
||||||
|
buyerInfo: result.data.buyer_info
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
validInvoices.push(selectedItems[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invalidInvoices.length > 0) {
|
||||||
|
let message = `تعداد ${invalidInvoices.length} فاکتور دارای اطلاعات اقتصادی ناقص هستند.\n\n`;
|
||||||
|
message += `خریداران دارای مشکل:\n`;
|
||||||
|
|
||||||
|
const uniqueBuyers = new Map();
|
||||||
|
invalidInvoices.forEach(invoice => {
|
||||||
|
const buyerInfo = invoice.buyerInfo;
|
||||||
|
if (buyerInfo && buyerInfo.buyer_name) {
|
||||||
|
uniqueBuyers.set(buyerInfo.buyer_name, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
uniqueBuyers.forEach((value, buyerName) => {
|
||||||
|
message += `• ${buyerName}\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'اطلاعات اقتصادی ناقص',
|
||||||
|
html: message.replace(/\n/g, '<br>'),
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'ارسال بدون اطلاعات خریدار',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'مشاهده لیست اشخاص',
|
||||||
|
showDenyButton: true,
|
||||||
|
denyButtonText: 'انصراف'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
this.sendBulkWithoutBuyerInfo(selectedItems);
|
||||||
|
} else if (result.dismiss === Swal.DismissReason.cancel) {
|
||||||
|
this.$router.push('/acc/persons/list');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.bulkLoading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await axios.post('/api/plugins/tax/invoice/send-bulk', {
|
const response = await axios.post('/api/plugins/tax/invoice/send-bulk', {
|
||||||
ids: selectedItems.map(item => item.id)
|
ids: validInvoices.map(item => item.id)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
|
@ -649,6 +754,67 @@ export default {
|
||||||
this.bulkLoading = false;
|
this.bulkLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async sendBulkWithoutBuyerInfo(selectedItems) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/plugins/tax/invoice/send-bulk', {
|
||||||
|
ids: selectedItems.map(item => item.id),
|
||||||
|
skip_buyer_validation: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.success) {
|
||||||
|
const summary = response.data.summary;
|
||||||
|
const results = response.data.results;
|
||||||
|
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
const errorMessages = [];
|
||||||
|
|
||||||
|
results.forEach(result => {
|
||||||
|
if (result.success) {
|
||||||
|
successCount++;
|
||||||
|
} else {
|
||||||
|
errorCount++;
|
||||||
|
errorMessages.push(`${result.code}: ${result.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let message = `پردازش ${summary.total} فاکتور مالیاتی بدون اطلاعات خریدار تکمیل شد.\n\n`;
|
||||||
|
message += `✅ موفق: ${successCount} فاکتور\n`;
|
||||||
|
message += `❌ ناموفق: ${errorCount} فاکتور`;
|
||||||
|
|
||||||
|
if (errorCount > 0 && errorMessages.length > 0) {
|
||||||
|
message += `\n\nفاکتورهای ناموفق:\n${errorMessages.slice(0, 5).join('\n')}`;
|
||||||
|
if (errorMessages.length > 5) {
|
||||||
|
message += `\nو ${errorMessages.length - 5} فاکتور دیگر...`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: successCount > 0 ? 'ارسال گروهی تکمیل شد' : 'خطا در ارسال گروهی',
|
||||||
|
html: message.replace(/\n/g, '<br>'),
|
||||||
|
icon: successCount > 0 ? 'success' : 'error',
|
||||||
|
confirmButtonText: 'باشه'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectedInvoices = [];
|
||||||
|
this.loadData();
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'خطا در ارسال گروهی',
|
||||||
|
text: response.data.message || 'خطا در ارسال گروهی فاکتورها',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'باشه'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'خطا در ارسال گروهی',
|
||||||
|
text: 'خطا در ارسال گروهی فاکتورها: ' + (error.response?.data?.message || error.message),
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'باشه'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
isItemSelectable(item) {
|
isItemSelectable(item) {
|
||||||
return item.status === 'pending' || item.status === 'error';
|
return item.status === 'pending' || item.status === 'error';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue