From 17af4bfaa97ff14a1b0e90f79df1fb41816784f9 Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Mon, 26 May 2025 01:49:14 +0000 Subject: [PATCH] bug fix reported --- .../src/Controller/ReportController.php | 33 ++++- hesabixCore/src/Controller/SellController.php | 12 +- .../src/Repository/CommodityRepository.php | 2 +- webUI/src/components/forms/Hnumberinput.vue | 111 ++++++++++++---- webUI/src/i18n/fa_lang.ts | 22 +++- .../acc/reports/persons/buysellByPerson.vue | 68 +++++----- webUI/src/views/acc/sell/mod.vue | 122 ++++++++++++------ .../views/user/manager/reportchange/mod.vue | 16 +-- 8 files changed, 269 insertions(+), 117 deletions(-) diff --git a/hesabixCore/src/Controller/ReportController.php b/hesabixCore/src/Controller/ReportController.php index 7d65782..6c1faaf 100644 --- a/hesabixCore/src/Controller/ReportController.php +++ b/hesabixCore/src/Controller/ReportController.php @@ -186,9 +186,38 @@ class ReportController extends AbstractController } } $labels = [ - 'کد حسابداری' + 'ردیف', + 'کد کالا', + 'نام کالا', + 'واحد', + 'تعداد', + 'قیمت واحد', + 'قیمت کل', + 'تاریخ', + 'شماره سند', + 'نوع سند', + 'خدمات' ]; - return new BinaryFileResponse($provider->createExcellFromArray($response)); + + // اضافه کردن شماره ردیف به داده‌ها + $responseWithRow = []; + foreach ($response as $index => $item) { + $responseWithRow[] = [ + 'row' => $index + 1, + 'code' => $item['code'], + 'name' => $item['name'], + 'unit' => $item['unit'], + 'count' => $item['count'], + 'priceOne' => $item['priceOne'], + 'priceAll' => $item['priceAll'], + 'date' => $item['date'], + 'docCode' => $item['docCode'], + 'type' => $item['type'], + 'khadamat' => $item['khadamat'] + ]; + } + + return new BinaryFileResponse($provider->createExcellFromArray($responseWithRow, $labels)); } #[Route('/api/report/commodity/buysell', name: 'app_report_commodity_buysell')] diff --git a/hesabixCore/src/Controller/SellController.php b/hesabixCore/src/Controller/SellController.php index 94888c2..bea236f 100644 --- a/hesabixCore/src/Controller/SellController.php +++ b/hesabixCore/src/Controller/SellController.php @@ -1219,14 +1219,16 @@ class SellController extends AbstractController $itemDiscount = $row->getDiscount() ?? 0; $itemDiscountType = $row->getDiscountType() ?? 'fixed'; $itemDiscountPercent = $row->getDiscountPercent() ?? 0; + $itemTax = $row->getTax() ?? 0; // محاسبه تخفیف سطری if ($itemDiscountType === 'percent') { $itemDiscount = round(($basePrice * $itemDiscountPercent) / 100); } - $itemTotal = $basePrice - $itemDiscount; - $totalInvoice += $itemTotal; + // محاسبه قیمت خالص (بدون مالیات) + $netPrice = $basePrice - $itemDiscount; + $totalInvoice += $netPrice; $items[] = [ 'name' => [ @@ -1235,13 +1237,13 @@ class SellController extends AbstractController 'code' => $row->getCommodity()->getCode() ], 'count' => $row->getCommdityCount(), - 'price' => $row->getCommdityCount() > 0 ? $basePrice / $row->getCommdityCount() : 0, + 'price' => $row->getCommdityCount() > 0 ? $netPrice / $row->getCommdityCount() : 0, 'discountPercent' => $itemDiscountPercent, 'discountAmount' => $itemDiscount, - 'total' => $itemTotal, + 'total' => $netPrice, 'description' => $row->getDes(), 'showPercentDiscount' => $itemDiscountType === 'percent', - 'tax' => $row->getTax() ?? 0 + 'tax' => $itemTax ]; } } diff --git a/hesabixCore/src/Repository/CommodityRepository.php b/hesabixCore/src/Repository/CommodityRepository.php index bed0b10..8cbde62 100644 --- a/hesabixCore/src/Repository/CommodityRepository.php +++ b/hesabixCore/src/Repository/CommodityRepository.php @@ -62,7 +62,7 @@ class CommodityRepository extends ServiceEntityRepository { return $this->createQueryBuilder('p') ->where('p.bid = :val') - ->andWhere("p.name LIKE :search OR p.barcodes LIKE :search") + ->andWhere("p.name LIKE :search OR p.barcodes LIKE :search OR p.code LIKE :search") ->setParameter('val', $bid) ->setParameter('search', '%' . $search . '%') ->setMaxResults($maxResults) diff --git a/webUI/src/components/forms/Hnumberinput.vue b/webUI/src/components/forms/Hnumberinput.vue index 1b9017a..c9a1dd4 100644 --- a/webUI/src/components/forms/Hnumberinput.vue +++ b/webUI/src/components/forms/Hnumberinput.vue @@ -3,10 +3,11 @@ v-model="inputValue" v-bind="$attrs" :class="$attrs.class" - type="text" + type="number" :rules="combinedRules" :error-messages="errorMessages" - @keypress="restrictToNumbers" + @keydown="restrictToNumbers" + @input="handleInput" dir="ltr" dense :hide-details="$attrs['hide-details'] || 'auto'" @@ -34,6 +35,10 @@ export default { allowDecimal: { type: Boolean, default: false + }, + allowNegative: { + type: Boolean, + default: false } }, @@ -47,7 +52,17 @@ export default { computed: { combinedRules() { return [ - v => !v || (this.allowDecimal ? /^\d*\.?\d*$/.test(v.replace(/[^0-9.]/g, '')) : /^\d+$/.test(v.replace(/[^0-9]/g, ''))) || this.$t('numberinput.invalid_number'), + v => { + if (!v && v !== '0') return true // اجازه خالی بودن + const pattern = this.allowDecimal + ? this.allowNegative + ? /^-?\d*\.?\d*$/ + : /^\d*\.?\d*$/ + : this.allowNegative + ? /^-?\d+$/ + : /^\d+$/ + return pattern.test(v) || this.$t('numberinput.invalid_number') + }, ...this.rules ] } @@ -57,48 +72,98 @@ export default { modelValue: { immediate: true, handler(newVal) { - if (newVal !== null && newVal !== undefined) { - const cleaned = String(newVal).replace(this.allowDecimal ? /[^0-9.]/g : /[^0-9]/g, '') - this.inputValue = cleaned ? (this.allowDecimal ? cleaned : Number(cleaned).toLocaleString('en-US')) : '' - } else { + if (newVal === null || newVal === undefined) { this.inputValue = '' + } else { + const cleaned = String(newVal).replace(this.allowDecimal ? /[^0-9.-]/g : /[^0-9-]/g, '') + this.inputValue = cleaned } } }, inputValue(newVal) { if (newVal === '' || newVal === null || newVal === undefined) { - this.$emit('update:modelValue', 0) + this.$emit('update:modelValue', null) + this.errorMessages = [] + return + } + + const cleaned = String(newVal).replace(this.allowDecimal ? /[^0-9.-]/g : /[^0-9-]/g, '') + const pattern = this.allowDecimal + ? this.allowNegative + ? /^-?\d*\.?\d*$/ + : /^\d*\.?\d*$/ + : this.allowNegative + ? /^-?\d+$/ + : /^\d+$/ + + if (pattern.test(cleaned)) { + let numericValue + if (this.allowDecimal) { + numericValue = cleaned === '' || cleaned === '-' ? null : parseFloat(cleaned) + } else { + numericValue = cleaned === '' || cleaned === '-' ? null : parseInt(cleaned, 10) + } + this.$emit('update:modelValue', isNaN(numericValue) ? null : numericValue) this.errorMessages = [] } else { - const cleaned = String(newVal).replace(this.allowDecimal ? /[^0-9.]/g : /[^0-9]/g, '') - if (this.allowDecimal ? /^\d*\.?\d*$/.test(cleaned) : /^\d+$/.test(cleaned)) { - const numericValue = cleaned ? (this.allowDecimal ? parseFloat(cleaned) : Number(cleaned)) : 0 - this.$emit('update:modelValue', numericValue) - this.errorMessages = [] - } else { - this.errorMessages = [this.$t('numberinput.invalid_number')] - } + this.errorMessages = [this.$t('numberinput.invalid_number')] } } }, methods: { restrictToNumbers(event) { - const charCode = event.charCode + const key = event.key + const input = this.inputValue || '' + + // اجازه دادن به کلیدهای کنترلی + if ( + ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Enter'].includes(key) || + (event.ctrlKey || event.metaKey) + ) { + return + } + if (this.allowDecimal) { - // اجازه ورود اعداد و نقطه اعشاری - if ((charCode < 48 || charCode > 57) && charCode !== 46) { + // اجازه ورود اعداد، ممیز، کاما (برای کیبوردهای محلی) و (در صورت اجازه) منفی + if (!/[0-9.,]/.test(key) && (!this.allowNegative || key !== '-')) { event.preventDefault() } - // جلوگیری از ورود بیش از یک نقطه اعشاری - if (charCode === 46 && this.inputValue.includes('.')) { + // جلوگیری از بیش از یک ممیز + if ((key === '.' || key === ',') && input.includes('.')) { + event.preventDefault() + } + // جلوگیری از ممیز در ابتدا یا بعد از منفی + if ((key === '.' || key === ',') && (input === '' || input === '-')) { + event.preventDefault() + } + // جلوگیری از بیش از یک منفی + if (key === '-' && (input.includes('-') || !this.allowNegative)) { + event.preventDefault() + } + // منفی فقط در ابتدا + if (key === '-' && input !== '') { event.preventDefault() } } else { - // فقط اجازه ورود اعداد - if (charCode < 48 || charCode > 57) { + // فقط اعداد و (در صورت اجازه) منفی + if (!/[0-9]/.test(key) && (!this.allowNegative || key !== '-')) { event.preventDefault() } + // جلوگیری از بیش از یک منفی + if (key === '-' && (input.includes('-') || !this.allowNegative)) { + event.preventDefault() + } + // منفی فقط در ابتدا + if (key === '-' && input !== '') { + event.preventDefault() + } + } + }, + handleInput(event) { + // تبدیل کاما به ممیز برای کیبوردهای محلی + if (this.allowDecimal && event.target.value.includes(',')) { + this.inputValue = event.target.value.replace(',', '.') } } } diff --git a/webUI/src/i18n/fa_lang.ts b/webUI/src/i18n/fa_lang.ts index a2d03c9..41010a8 100644 --- a/webUI/src/i18n/fa_lang.ts +++ b/webUI/src/i18n/fa_lang.ts @@ -194,6 +194,7 @@ const fa_lang = { repservice_reqs: "درخواست‌ها", hrm: 'منابع انسانی', hrm_docs: 'سند حقوق', + buysellByPerson: "گزارش خرید و فروش های اشخاص", }, time: { month: "{id} ماه", @@ -524,7 +525,26 @@ const fa_lang = { description: "توضیحات الزامی است", person: "انتخاب شخص الزامی است" } - } + }, + buysell_report: { + person: "شخص", + type: "نوع", + date_start: "تاریخ شروع", + date_end: "تاریخ پایان", + search: "جست و جو ...", + goods: "کالا", + services: "خدمات", + unit: "واحد شمارش", + count: "تعداد", + unit_price: "مبلغ فی", + total_price: "مبلغ کل", + cumulative: "تجمعی", + buy: "خرید", + sell: "فروش", + return_buy: "برگشت از خرید", + return_sell: "برگشت از فروش", + all_types: "همه موارد" + }, }, app: { loading: "در حال بارگذاری...", diff --git a/webUI/src/views/acc/reports/persons/buysellByPerson.vue b/webUI/src/views/acc/reports/persons/buysellByPerson.vue index 52c8686..253b5b6 100644 --- a/webUI/src/views/acc/reports/persons/buysellByPerson.vue +++ b/webUI/src/views/acc/reports/persons/buysellByPerson.vue @@ -1,36 +1,40 @@