diff --git a/hesabixCore/src/Controller/Plugins/Hrm/AttendanceController.php b/hesabixCore/src/Controller/Plugins/Hrm/AttendanceController.php index c3b74bc..75ba0bb 100644 --- a/hesabixCore/src/Controller/Plugins/Hrm/AttendanceController.php +++ b/hesabixCore/src/Controller/Plugins/Hrm/AttendanceController.php @@ -281,13 +281,206 @@ class AttendanceController extends AbstractController throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.'); } - // دریافت نوع پرسنل "کارمند" یا "employee" - $employeeType = $this->entityManager->getRepository(PersonType::class)->findOneBy(['code' => 'employee']); + $params = []; + if ($content = $request->getContent()) { + $params = json_decode($content, true); + } + + $search = $params['search'] ?? ''; + $page = $params['page'] ?? 1; + $limit = $params['limit'] ?? 20; + + // دریافت نوع پرسنل "کارمند" - کد صحیح در دیتابیس: emplyee + $employeeType = $this->entityManager->getRepository(PersonType::class)->findOneBy(['code' => 'emplyee']); if (!$employeeType) { - // اگر نوع "employee" وجود نداشت، نوع "کارمند" را جستجو کن + // اگر نوع "emplyee" وجود نداشت، نوع "کارمند" را جستجو کن $employeeType = $this->entityManager->getRepository(PersonType::class)->findOneBy(['code' => 'کارمند']); } + $qb = $this->entityManager->createQueryBuilder(); + $qb->select('p') + ->from(Person::class, 'p') + ->where('p.bid = :bid') + ->setParameter('bid', $acc['bid']); + + // فقط پرسنلهایی که نوع پرسنل دارند + if ($employeeType) { + $qb->join('p.type', 't') + ->andWhere('t = :employeeType') + ->setParameter('employeeType', $employeeType); + } else { + // اگر نوع کارمند پیدا نشد، حداقل پرسنلهایی که نوع دارند را برگردان + $qb->join('p.type', 't'); + } + + // اعمال فیلتر جستجو + if (!empty($search)) { + $qb->andWhere('p.nikename LIKE :search OR p.name LIKE :search OR p.code LIKE :search') + ->setParameter('search', '%' . $search . '%'); + } + + $qb->orderBy('p.nikename', 'ASC'); + + // محاسبه تعداد کل + $countQb = clone $qb; + $totalCount = $countQb->select('COUNT(p.id)')->getQuery()->getSingleScalarResult(); + + // اعمال صفحهبندی + $qb->setFirstResult(($page - 1) * $limit) + ->setMaxResults($limit); + + $employees = $qb->getQuery()->getResult(); + + $result = []; + foreach ($employees as $employee) { + $result[] = [ + 'id' => $employee->getId(), + 'name' => $employee->getNikename(), + 'code' => $employee->getCode(), + 'mobile' => $employee->getMobile(), + 'tel' => $employee->getTel(), + 'email' => $employee->getEmail(), + 'company' => $employee->getCompany(), + 'address' => $employee->getAddress(), + ]; + } + + return $this->json([ + 'items' => $result, + 'total' => $totalCount, + 'page' => $page, + 'limit' => $limit + ]); + + } catch (\Exception $e) { + return $this->json(['error' => $e->getMessage()], 500); + } + } + + #[Route('/api/hrm/attendance/employees/search', name: 'hrm_attendance_employees_search', methods: ['POST'])] + public function searchEmployees(Request $request, Access $access): JsonResponse + { + try { + $acc = $access->hasRole('plugHrmAttendance'); + if (!$acc) { + throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.'); + } + + $params = []; + if ($content = $request->getContent()) { + $params = json_decode($content, true); + } + + $search = $params['search'] ?? ''; + $page = $params['page'] ?? 1; + $limit = $params['limit'] ?? 10; + + // فیلترهای پیشرفته + $code = $params['code'] ?? ''; + $mobile = $params['mobile'] ?? ''; + $company = $params['company'] ?? ''; + $email = $params['email'] ?? ''; + + // دریافت نوع پرسنل "کارمند" - کد صحیح در دیتابیس: emplyee + $employeeType = $this->entityManager->getRepository(PersonType::class)->findOneBy(['code' => 'emplyee']); + if (!$employeeType) { + $employeeType = $this->entityManager->getRepository(PersonType::class)->findOneBy(['code' => 'کارمند']); + } + + $qb = $this->entityManager->createQueryBuilder(); + $qb->select('p') + ->from(Person::class, 'p') + ->where('p.bid = :bid') + ->setParameter('bid', $acc['bid']); + + // فقط پرسنلهایی که نوع پرسنل دارند + if ($employeeType) { + $qb->join('p.type', 't') + ->andWhere('t = :employeeType') + ->setParameter('employeeType', $employeeType); + } else { + // اگر نوع کارمند پیدا نشد، حداقل پرسنلهایی که نوع دارند را برگردان + $qb->join('p.type', 't'); + } + + // اعمال فیلتر جستجو عمومی + if (!empty($search)) { + $qb->andWhere('p.nikename LIKE :search OR p.name LIKE :search OR p.code LIKE :search OR p.mobile LIKE :search') + ->setParameter('search', '%' . $search . '%'); + } + + // اعمال فیلترهای پیشرفته + if (!empty($code)) { + $qb->andWhere('p.code LIKE :code') + ->setParameter('code', '%' . $code . '%'); + } + + if (!empty($mobile)) { + $qb->andWhere('p.mobile LIKE :mobile') + ->setParameter('mobile', '%' . $mobile . '%'); + } + + if (!empty($company)) { + $qb->andWhere('p.company LIKE :company') + ->setParameter('company', '%' . $company . '%'); + } + + if (!empty($email)) { + $qb->andWhere('p.email LIKE :email') + ->setParameter('email', '%' . $email . '%'); + } + + $qb->orderBy('p.nikename', 'ASC'); + + // محاسبه تعداد کل + $countQb = clone $qb; + $totalCount = $countQb->select('COUNT(p.id)')->getQuery()->getSingleScalarResult(); + + // اعمال صفحهبندی + $qb->setFirstResult(($page - 1) * $limit) + ->setMaxResults($limit); + + $employees = $qb->getQuery()->getResult(); + + $result = []; + foreach ($employees as $employee) { + $result[] = [ + 'id' => $employee->getId(), + 'name' => $employee->getNikename(), + 'code' => $employee->getCode(), + 'mobile' => $employee->getMobile(), + 'tel' => $employee->getTel(), + 'email' => $employee->getEmail(), + 'company' => $employee->getCompany(), + 'address' => $employee->getAddress(), + 'fullName' => $employee->getName(), + ]; + } + + return $this->json([ + 'items' => $result, + 'total' => $totalCount, + 'page' => $page, + 'limit' => $limit + ]); + + } catch (\Exception $e) { + return $this->json(['error' => $e->getMessage()], 500); + } + } + + #[Route('/api/hrm/attendance/employees/test', name: 'hrm_attendance_employees_test', methods: ['GET'])] + public function testEmployees(Access $access): JsonResponse + { + try { + $acc = $access->hasRole('plugHrmAttendance'); + if (!$acc) { + throw $this->createAccessDeniedException('شما دسترسی لازم را ندارید.'); + } + + // دریافت نوع پرسنل "کارمند" + $employeeType = $this->entityManager->getRepository(PersonType::class)->findOneBy(['code' => 'emplyee']); + $qb = $this->entityManager->createQueryBuilder(); $qb->select('p') ->from(Person::class, 'p') @@ -298,10 +491,11 @@ class AttendanceController extends AbstractController $qb->join('p.type', 't') ->andWhere('t = :employeeType') ->setParameter('employeeType', $employeeType); + } else { + $qb->join('p.type', 't'); } - $qb->orderBy('p.nikename', 'ASC'); - + $qb->setMaxResults(5); $employees = $qb->getQuery()->getResult(); $result = []; @@ -310,10 +504,18 @@ class AttendanceController extends AbstractController 'id' => $employee->getId(), 'name' => $employee->getNikename(), 'code' => $employee->getCode(), + 'bid' => $employee->getBid()->getId(), + 'hasType' => $employee->getType()->count() > 0 ]; } - return $this->json($result); + return $this->json([ + 'business_id' => $acc['bid'], + 'employee_type_found' => $employeeType ? true : false, + 'employee_type_code' => $employeeType ? $employeeType->getCode() : null, + 'total_employees' => count($result), + 'employees' => $result + ]); } catch (\Exception $e) { return $this->json(['error' => $e->getMessage()], 500); diff --git a/webUI/src/components/forms/HemployeeAdvancedSearch.vue b/webUI/src/components/forms/HemployeeAdvancedSearch.vue new file mode 100644 index 0000000..e7b723d --- /dev/null +++ b/webUI/src/components/forms/HemployeeAdvancedSearch.vue @@ -0,0 +1,469 @@ + + + + + + + {{ menu ? 'mdi-chevron-up' : 'mdi-chevron-down' }} + + + + + + + جستجوی پرسنل + + + + + + + + + mdi-filter-variant + فیلترهای پیشرفته + + + + + + + + + + + + + + + + + + + + اعمال فیلترها + + + پاک کردن + + + + + + + + + + + + + + + + + + {{ getInitials(item.name) }} + + + + + {{ item.name }} + ({{ item.code }}) + + + + + mdi-cellphone + {{ item.mobile || 'بدون موبایل' }} + + + mdi-domain + {{ item.company }} + + + mdi-email + {{ item.email }} + + + + + + کارمند + + + + + + + + mdi-account-search + نتیجهای یافت نشد + لطفاً عبارت جستجو را تغییر دهید + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webUI/src/components/forms/HemployeeSearch.vue b/webUI/src/components/forms/HemployeeSearch.vue new file mode 100644 index 0000000..589d8f6 --- /dev/null +++ b/webUI/src/components/forms/HemployeeSearch.vue @@ -0,0 +1,304 @@ + + + + + + + {{ menu ? 'mdi-chevron-up' : 'mdi-chevron-down' }} + + + + + + + + + + + + + + mdi-account-tie + {{ item.mobile || 'بدون موبایل' }} + + {{ item.code }} + + + {{ item.name }} + + کارمند + + + + + + + + + نتیجهای یافت نشد + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webUI/src/i18n/en_lang.ts b/webUI/src/i18n/en_lang.ts index 64c51b3..e751ef7 100755 --- a/webUI/src/i18n/en_lang.ts +++ b/webUI/src/i18n/en_lang.ts @@ -8,6 +8,10 @@ const en_lang = { logout_loading: "you logged out ..." }, dialog:{ + back: "Back", + add_new: "Add New", + delete: "Delete", + manage_columns: "Manage Columns", ok: "Ok", cancel: "Cancel", save: "Save", @@ -89,6 +93,12 @@ const en_lang = { inquiry: "Inquiries", hrm: 'HR & Payroll', hrm_docs: 'Payroll Document', + hrm_attendance: 'Personnel Attendance', + hrm_attendance_list: 'Attendance List', + hrm_attendance_add: 'Add Attendance', + hrm_attendance_edit: 'Edit Attendance', + hrm_attendance_view: 'View Attendance', + hrm_attendance_reports: 'Attendance Reports', warranty_system: 'Warranty System', warranty_serials: 'Warranty Serials', business_switcher: 'Switch Business', diff --git a/webUI/src/i18n/fa_lang.ts b/webUI/src/i18n/fa_lang.ts index 5e8ed7b..374f35d 100755 --- a/webUI/src/i18n/fa_lang.ts +++ b/webUI/src/i18n/fa_lang.ts @@ -40,6 +40,7 @@ const fa_lang = { credit_balance: "تراز بستانکار", operations: "عملیات", rows_per_page: "تعداد سطر در هر صفحه", + "تعداد سطر": "تعداد سطر", no_data: "اطلاعاتی برای نمایش وجود ندارد", of: "از", date: "تاریخ", @@ -308,6 +309,10 @@ const fa_lang = { fetch_data_error: "خطا در گرفتن داده از {url}" }, dialog: { + back: "بازگشت", + add_new: "افزودن جدید", + delete: "حذف", + manage_columns: "مدیریت ستونها", person_with_det_report: 'گزارش تفضیلی اشخاص', change_password_label: 'تغییر کلمه عبور', download: 'دانلود', @@ -406,7 +411,6 @@ const fa_lang = { filter: "فیلتر", filters: "فیلترها", commodity_not_found: "کالا یافت نشد", - add_new: "افزودن مورد جدید", fiscal_year: "سال مالی", currency: "واحد پولی", notifications: "اعلانات", @@ -431,7 +435,6 @@ const fa_lang = { payment: "ثبت پرداخت", exit: "خروج از حساب کاربری", complete_all: "موارد الزامی را تکمیل کنید", - back: "صفحه قبل", search: "جست و جو ...", general: "عمومی", prices: "قیمتها", @@ -488,7 +491,6 @@ const fa_lang = { system: "سیستم", database: "بانک اطلاعاتی", edit: "ویرایش", - delete: "حذف", each: "هر", logout: "خروج", import_excel: "درون ریزی از اکسل", @@ -519,12 +521,18 @@ const fa_lang = { error_operation: "در انجام عملیات خطایی به وجود آمد.در صورت تکرار خطا با پشتیبان نرم افزار تماس بگیرید.", "success": "موفقیت", "error_unknown": "خطای ناشناختهای رخ داد", - "manage_columns": "مدیریت ستونها", customize_columns: "شخصیسازی ستونها", close_dialog: "بستن", presell_info: "اطلاعات پیش فاکتور", financial_info: "اطلاعات مالی", invoice_items: "اقلام فاکتور", + select_file: "انتخاب فایل", + select_file_first: "لطفاً فایل را انتخاب کنید", + file_format: "فرمت فایل", + clear: "پاک کردن", + yes: "بله", + no: "خیر", + no_items_selected: "هیچ موردی انتخاب نشده است", hrm: { title: "سند حقوق", date: "تاریخ", @@ -549,6 +557,72 @@ const fa_lang = { date: "تاریخ الزامی است", description: "توضیحات الزامی است", person: "انتخاب شخص الزامی است" + }, + attendance: { + title: "تردد پرسنل", + list: "لیست ترددها", + add: "افزودن تردد", + edit: "ویرایش تردد", + view: "مشاهده تردد", + reports: "گزارشات تردد", + employee: "پرسنل", + date: "تاریخ", + time: "زمان", + type: "نوع", + entry: "ورود", + exit: "خروج", + total_hours: "ساعات کل کار", + overtime_hours: "ساعات اضافهکاری", + description: "توضیحات", + status: "وضعیت", + actions: "عملیات", + created_at: "تاریخ ثبت", + person_name: "نام پرسنل", + person_code: "کد پرسنل", + work_hours: "ساعات کار", + overtime: "اضافهکاری", + notes: "یادداشت", + import_excel: "واردات از اکسل", + export_excel: "خروجی اکسل", + generate_report: "تولید گزارش", + daily_report: "گزارش روزانه", + monthly_report: "گزارش ماهانه", + overtime_report: "گزارش اضافهکاری", + absence_report: "گزارش تاخیر و غیبت", + from_date: "از تاریخ", + to_date: "تا تاریخ", + filter_by_person: "فیلتر بر اساس پرسنل", + all_persons: "همه پرسنل", + report_filters: "فیلترهای گزارش", + report_type: "نوع گزارش", + summary: { + total_days: "کل روزهای کاری", + total_hours: "کل ساعات کار", + total_overtime: "کل اضافهکاری", + average_hours: "میانگین ساعات روزانه" + }, + statuses: { + full_attendance: "حضور کامل", + part_time: "نیمه وقت", + late: "تاخیر", + absent: "غیبت" + }, + messages: { + no_data: "هیچ دادهای برای نمایش وجود ندارد", + select_date_range: "لطفاً بازه زمانی را مشخص کنید", + import_success: "رکوردها با موفقیت وارد شدند", + export_success: "فایل با موفقیت دانلود شد", + delete_confirm: "آیا برای حذف موارد انتخاب شده مطمئن هستید؟", + delete_success: "موارد انتخاب شده با موفقیت حذف شدند", + save_success: "تردد با موفقیت ذخیره شد", + edit_success: "تردد با موفقیت ویرایش شد", + load_error: "خطا در بارگذاری اطلاعات", + save_error: "خطا در ذخیره اطلاعات", + delete_error: "خطا در حذف اطلاعات", + import_error: "خطا در واردات فایل", + export_error: "خطا در خروجی فایل", + report_error: "خطا در تولید گزارش" + } } }, buysell_report: { diff --git a/webUI/src/views/acc/plugins/hrm/attendance/list.vue b/webUI/src/views/acc/plugins/hrm/attendance/list.vue index 00f3d32..a257cde 100644 --- a/webUI/src/views/acc/plugins/hrm/attendance/list.vue +++ b/webUI/src/views/acc/plugins/hrm/attendance/list.vue @@ -1,23 +1,46 @@ - - - - مدیریت تردد پرسنل - + + + + + + + + - - افزودن تردد - - - واردات از اکسل - - - خروجی اکسل - - - گزارشات - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,14 +55,11 @@ - @@ -59,7 +79,7 @@ {{ formatDate(item.date) }} @@ -120,10 +142,10 @@ /> فرمت فایل: - ستون A: کد پرسنل - ستون B: تاریخ (YYYY/MM/DD) - ستون C: زمان (HH:MM) - ستون D: نوع (ورود/خروج) + کد پرسنل: A + تاریخ: B (YYYY/MM/DD) + زمان: C (HH:MM) + نوع: D (ورود/خروج) @@ -136,6 +158,27 @@ + + + + + {{ $t('dialog.manage_columns') }} + + + mdi-close + + + + + + + + + + + + {{ snackbar.text }} @@ -145,12 +188,15 @@