From e084e31db553320d4ecd9f9df73e4e61137a0139 Mon Sep 17 00:00:00 2001 From: Anoop Date: Fri, 23 Sep 2022 16:17:16 +0530 Subject: [PATCH] Initial Commit --- .flake8 | 37 + .git-blame-ignore-revs | 15 + .github/CODEOWNERS | 7 + .github/helper/.flake8_strict | 73 + .github/helper/documentation.py | 54 + .github/helper/install.sh | 52 + .github/helper/site_config.json | 16 + .github/helper/translation.py | 60 + .github/workflows/ci.yml | 102 + .github/workflows/docs_checker.yml | 25 + .github/workflows/linters.yml | 29 + .gitignore | 9 + .pre-commit-config.yaml | 44 + .semgrepignore | 1 + MANIFEST.in | 18 + README.md | 52 + codecov.yml | 24 + hrms.png | Bin 0 -> 1180791 bytes hrms/__init__.py | 1 + hrms/config/__init__.py | 0 hrms/config/desktop.py | 5 + hrms/config/docs.py | 11 + .../employee_boarding_controller.py | 198 ++ hrms/controllers/employee_reminders.py | 275 +++ .../tests/test_employee_reminders.py | 269 +++ hrms/hooks.py | 301 +++ hrms/hr/README.md | 6 + hrms/hr/__init__.py | 0 .../attendance_count/attendance_count.json | 28 + .../claims_by_type/claims_by_type.json | 33 + .../department_wise_employee_count.json | 30 + .../department_wise_expense_claims.json | 33 + .../department_wise_openings.json | 31 + .../department_wise_timesheet_hours.json | 34 + .../designation_wise_employee_count.json | 30 + .../designation_wise_openings.json | 31 + .../employee_advance_status.json | 33 + .../employees_by_age/employees_by_age.json | 33 + .../employees_by_branch.json | 30 + .../employees_by_grade.json | 30 + .../employees_by_type/employees_by_type.json | 30 + .../expense_claims/expense_claims.json | 33 + .../gender_diversity_ratio.json | 29 + .../grievance_type/grievance_type.json | 33 + .../hiring_vs_attrition_count.json | 33 + .../interview_status/interview_status.json | 33 + .../job_applicant_pipeline.json | 33 + .../job_applicant_source.json | 33 + .../job_applicants_by_country.json | 32 + .../job_application_frequency.json | 32 + .../job_application_status.json | 30 + .../job_offer_status/job_offer_status.json | 33 + .../shift_assignment_breakup.json | 32 + .../timesheet_activity_breakup.json | 34 + .../training_type/training_type.json | 32 + .../y_o_y_promotions/y_o_y_promotions.json | 31 + .../y_o_y_transfers/y_o_y_transfers.json | 31 + hrms/hr/dashboard_chart_source/__init__.py | 0 .../employees_by_age/__init__.py | 0 .../employees_by_age/employees_by_age.js | 14 + .../employees_by_age/employees_by_age.json | 13 + .../employees_by_age/employees_by_age.py | 87 + .../hiring_vs_attrition_count/__init__.py | 0 .../hiring_vs_attrition_count.js | 35 + .../hiring_vs_attrition_count.json | 13 + .../hiring_vs_attrition_count.py | 69 + hrms/hr/doctype/__init__.py | 0 .../hr/doctype/appointment_letter/__init__.py | 0 .../appointment_letter/appointment_letter.js | 30 + .../appointment_letter.json | 128 ++ .../appointment_letter/appointment_letter.py | 29 + .../test_appointment_letter.py | 9 + .../appointment_letter_content/__init__.py | 0 .../appointment_letter_content.json | 39 + .../appointment_letter_content.py | 10 + .../appointment_letter_template/__init__.py | 0 .../appointment_letter_template.js | 8 + .../appointment_letter_template.json | 81 + .../appointment_letter_template.py | 10 + .../test_appointment_letter_template.py | 9 + hrms/hr/doctype/appraisal/README.md | 1 + hrms/hr/doctype/appraisal/__init__.py | 0 hrms/hr/doctype/appraisal/appraisal.js | 79 + hrms/hr/doctype/appraisal/appraisal.json | 254 +++ hrms/hr/doctype/appraisal/appraisal.py | 94 + hrms/hr/doctype/appraisal/test_appraisal.py | 10 + hrms/hr/doctype/appraisal_goal/README.md | 1 + hrms/hr/doctype/appraisal_goal/__init__.py | 0 .../appraisal_goal/appraisal_goal.json | 220 ++ .../doctype/appraisal_goal/appraisal_goal.py | 9 + hrms/hr/doctype/appraisal_template/README.md | 1 + .../hr/doctype/appraisal_template/__init__.py | 0 .../appraisal_template/appraisal_template.js | 8 + .../appraisal_template.json | 170 ++ .../appraisal_template/appraisal_template.py | 21 + .../appraisal_template_dashboard.py | 7 + .../test_appraisal_template.py | 10 + .../doctype/appraisal_template_goal/README.md | 1 + .../appraisal_template_goal/__init__.py | 0 .../appraisal_template_goal.json | 91 + .../appraisal_template_goal.py | 9 + hrms/hr/doctype/attendance/README.md | 1 + hrms/hr/doctype/attendance/__init__.py | 0 hrms/hr/doctype/attendance/attendance.js | 15 + hrms/hr/doctype/attendance/attendance.json | 260 +++ hrms/hr/doctype/attendance/attendance.py | 390 ++++ .../doctype/attendance/attendance_calendar.js | 12 + .../attendance/attendance_dashboard.py | 2 + hrms/hr/doctype/attendance/attendance_list.js | 164 ++ hrms/hr/doctype/attendance/test_attendance.py | 233 +++ hrms/hr/doctype/attendance/test_records.json | 10 + .../hr/doctype/attendance_request/__init__.py | 0 .../attendance_request/attendance_request.js | 14 + .../attendance_request.json | 190 ++ .../attendance_request/attendance_request.py | 79 + .../attendance_request_dashboard.py | 2 + .../test_attendance_request.py | 100 + .../compensatory_leave_request/__init__.py | 0 .../compensatory_leave_request.js | 22 + .../compensatory_leave_request.json | 575 ++++++ .../compensatory_leave_request.py | 155 ++ .../test_compensatory_leave_request.py | 157 ++ .../hr/doctype/daily_work_summary/__init__.py | 0 .../daily_work_summary/daily_work_summary.js | 8 + .../daily_work_summary.json | 175 ++ .../daily_work_summary/daily_work_summary.py | 120 ++ .../test_daily_work_summary.py | 105 + .../test_data/test-reply.raw | 75 + .../daily_work_summary_group/__init__.py | 0 .../daily_work_summary_group.js | 12 + .../daily_work_summary_group.json | 309 +++ .../daily_work_summary_group.py | 56 + .../daily_work_summary_group_user/__init__.py | 0 .../daily_work_summary_group_user.json | 106 + .../daily_work_summary_group_user.py | 9 + .../doctype/department_approver/__init__.py | 0 .../department_approver.json | 76 + .../department_approver.py | 93 + hrms/hr/doctype/designation_skill/__init__.py | 0 .../designation_skill/designation_skill.json | 74 + .../designation_skill/designation_skill.py | 10 + hrms/hr/doctype/employee_advance/__init__.py | 0 .../employee_advance/employee_advance.js | 225 ++ .../employee_advance/employee_advance.json | 277 +++ .../employee_advance/employee_advance.py | 324 +++ .../employee_advance_dashboard.py | 9 + .../employee_advance/test_employee_advance.py | 258 +++ .../employee_attendance_tool/__init__.py | 0 .../employee_attendance_tool.css | 21 + .../employee_attendance_tool.js | 269 +++ .../employee_attendance_tool.json | 275 +++ .../employee_attendance_tool.py | 69 + .../employee_boarding_activity/__init__.py | 0 .../employee_boarding_activity.json | 108 + .../employee_boarding_activity.py | 9 + hrms/hr/doctype/employee_checkin/__init__.py | 0 .../employee_checkin/employee_checkin.js | 10 + .../employee_checkin/employee_checkin.json | 208 ++ .../employee_checkin/employee_checkin.py | 278 +++ .../employee_checkin/test_employee_checkin.py | 347 ++++ hrms/hr/doctype/employee_grade/__init__.py | 0 .../doctype/employee_grade/employee_grade.js | 29 + .../employee_grade/employee_grade.json | 89 + .../doctype/employee_grade/employee_grade.py | 9 + .../employee_grade_dashboard.py | 9 + .../employee_grade/test_employee_grade.py | 8 + .../hr/doctype/employee_grievance/__init__.py | 0 .../employee_grievance/employee_grievance.js | 39 + .../employee_grievance.json | 261 +++ .../employee_grievance/employee_grievance.py | 16 + .../employee_grievance_list.js | 12 + .../test_employee_grievance.py | 55 + .../employee_health_insurance/__init__.py | 0 .../employee_health_insurance.js | 8 + .../employee_health_insurance.json | 113 + .../employee_health_insurance.py | 9 + .../test_employee_health_insurance.py | 8 + .../hr/doctype/employee_loan/employee_loan.js | 121 ++ .../doctype/employee_onboarding/__init__.py | 0 .../employee_onboarding.js | 83 + .../employee_onboarding.json | 205 ++ .../employee_onboarding.py | 86 + .../employee_onboarding_list.js | 7 + .../test_employee_onboarding.py | 135 ++ .../employee_onboarding_template/__init__.py | 0 .../employee_onboarding_template.js | 14 + .../employee_onboarding_template.json | 292 +++ .../employee_onboarding_template.py | 9 + .../employee_onboarding_template_dashboard.py | 7 + .../test_employee_onboarding_template.py | 8 + .../hr/doctype/employee_promotion/__init__.py | 0 .../employee_promotion/employee_promotion.js | 10 + .../employee_promotion.json | 172 ++ .../employee_promotion/employee_promotion.py | 42 + .../test_employee_promotion.py | 97 + .../employee_property_history/__init__.py | 0 .../employee_property_history.json | 161 ++ .../employee_property_history.py | 9 + hrms/hr/doctype/employee_referral/__init__.py | 0 .../employee_referral/employee_referral.js | 66 + .../employee_referral/employee_referral.json | 305 +++ .../employee_referral/employee_referral.py | 78 + .../employee_referral_dashboard.py | 8 + .../employee_referral_list.js | 14 + .../test_employee_referral.py | 71 + .../doctype/employee_separation/__init__.py | 0 .../employee_separation.js | 49 + .../employee_separation.json | 185 ++ .../employee_separation.py | 19 + .../employee_separation_list.js | 7 + .../test_employee_separation.py | 48 + .../employee_separation_template/__init__.py | 0 .../employee_separation_template.js | 8 + .../employee_separation_template.json | 292 +++ .../employee_separation_template.py | 9 + .../employee_separation_template_dashboard.py | 7 + .../test_employee_separation_template.py | 8 + hrms/hr/doctype/employee_skill/__init__.py | 0 .../employee_skill/employee_skill.json | 141 ++ .../doctype/employee_skill/employee_skill.py | 10 + .../hr/doctype/employee_skill_map/__init__.py | 0 .../employee_skill_map/employee_skill_map.js | 21 + .../employee_skill_map.json | 88 + .../employee_skill_map/employee_skill_map.py | 10 + hrms/hr/doctype/employee_training/__init__.py | 0 .../employee_training/employee_training.json | 108 + .../employee_training/employee_training.py | 10 + hrms/hr/doctype/employee_transfer/__init__.py | 0 .../employee_transfer/employee_transfer.js | 10 + .../employee_transfer/employee_transfer.json | 528 +++++ .../employee_transfer/employee_transfer.py | 76 + .../test_employee_transfer.py | 141 ++ hrms/hr/doctype/employment_type/README.md | 3 + hrms/hr/doctype/employment_type/__init__.py | 0 .../employment_type/employment_type.json | 113 + .../employment_type/employment_type.py | 9 + .../employment_type/test_employment_type.py | 6 + .../doctype/employment_type/test_records.json | 6 + hrms/hr/doctype/exit_interview/__init__.py | 0 .../doctype/exit_interview/exit_interview.js | 38 + .../exit_interview/exit_interview.json | 246 +++ .../doctype/exit_interview/exit_interview.py | 146 ++ .../exit_interview/exit_interview_list.js | 27 + ...t_questionnaire_notification_template.html | 16 + .../exit_interview/test_exit_interview.py | 127 ++ .../hr/doctype/expected_skill_set/__init__.py | 0 .../expected_skill_set.json | 40 + .../expected_skill_set/expected_skill_set.py | 10 + hrms/hr/doctype/expense_claim/README.md | 1 + hrms/hr/doctype/expense_claim/__init__.py | 0 .../hr/doctype/expense_claim/expense_claim.js | 469 +++++ .../doctype/expense_claim/expense_claim.json | 450 ++++ .../hr/doctype/expense_claim/expense_claim.py | 542 +++++ .../expense_claim/expense_claim_dashboard.py | 12 + .../expense_claim/expense_claim_list.js | 12 + .../expense_claim/test_expense_claim.py | 463 +++++ .../doctype/expense_claim_account/__init__.py | 0 .../expense_claim_account.json | 89 + .../expense_claim_account.py | 9 + .../doctype/expense_claim_advance/__init__.py | 0 .../expense_claim_advance.json | 100 + .../expense_claim_advance.py | 9 + .../hr/doctype/expense_claim_detail/README.md | 1 + .../doctype/expense_claim_detail/__init__.py | 0 .../expense_claim_detail.json | 130 ++ .../expense_claim_detail.py | 9 + hrms/hr/doctype/expense_claim_type/README.md | 1 + .../hr/doctype/expense_claim_type/__init__.py | 0 .../expense_claim_type/expense_claim_type.js | 17 + .../expense_claim_type.json | 74 + .../expense_claim_type/expense_claim_type.py | 30 + .../test_expense_claim_type.py | 10 + .../expense_taxes_and_charges/__init__.py | 0 .../expense_taxes_and_charges.json | 112 + .../expense_taxes_and_charges.py | 10 + .../doctype/full_and_final_asset/__init__.py | 0 .../full_and_final_asset.js | 8 + .../full_and_final_asset.json | 64 + .../full_and_final_asset.py | 9 + .../test_full_and_final_asset.py | 9 + .../__init__.py | 0 .../full_and_final_outstanding_statement.json | 96 + .../full_and_final_outstanding_statement.py | 9 + .../full_and_final_statement/__init__.py | 0 .../full_and_final_statement.js | 115 ++ .../full_and_final_statement.json | 231 +++ .../full_and_final_statement.py | 211 ++ .../full_and_final_statement_list.js | 11 + .../test_full_and_final_statement.py | 81 + hrms/hr/doctype/grievance_type/__init__.py | 0 .../doctype/grievance_type/grievance_type.js | 8 + .../grievance_type/grievance_type.json | 74 + .../doctype/grievance_type/grievance_type.py | 9 + .../grievance_type/test_grievance_type.py | 9 + hrms/hr/doctype/hr_settings/__init__.py | 0 hrms/hr/doctype/hr_settings/hr_settings.js | 23 + hrms/hr/doctype/hr_settings/hr_settings.json | 279 +++ hrms/hr/doctype/hr_settings/hr_settings.py | 85 + .../doctype/hr_settings/test_hr_settings.py | 8 + .../identification_document_type/__init__.py | 0 .../identification_document_type.js | 8 + .../identification_document_type.json | 93 + .../identification_document_type.py | 9 + .../test_identification_document_type.py | 8 + hrms/hr/doctype/interest/__init__.py | 0 hrms/hr/doctype/interest/interest.js | 8 + hrms/hr/doctype/interest/interest.json | 144 ++ hrms/hr/doctype/interest/interest.py | 9 + hrms/hr/doctype/interest/test_interest.py | 10 + hrms/hr/doctype/interview/__init__.py | 0 hrms/hr/doctype/interview/interview.js | 237 +++ hrms/hr/doctype/interview/interview.json | 254 +++ hrms/hr/doctype/interview/interview.py | 360 ++++ .../doctype/interview/interview_calendar.js | 14 + .../interview_feedback_reminder_template.html | 5 + hrms/hr/doctype/interview/interview_list.js | 12 + ...erview_reminder_notification_template.html | 5 + hrms/hr/doctype/interview/test_interview.py | 215 ++ hrms/hr/doctype/interview_detail/__init__.py | 0 .../interview_detail/interview_detail.js | 8 + .../interview_detail/interview_detail.json | 74 + .../interview_detail/interview_detail.py | 10 + .../interview_detail/test_interview_detail.py | 9 + .../hr/doctype/interview_feedback/__init__.py | 0 .../interview_feedback/interview_feedback.js | 54 + .../interview_feedback.json | 171 ++ .../interview_feedback/interview_feedback.py | 91 + .../test_interview_feedback.py | 113 + hrms/hr/doctype/interview_round/__init__.py | 0 .../interview_round/interview_round.js | 24 + .../interview_round/interview_round.json | 118 ++ .../interview_round/interview_round.py | 29 + .../interview_round/test_interview_round.py | 10 + hrms/hr/doctype/interview_type/__init__.py | 0 .../doctype/interview_type/interview_type.js | 8 + .../interview_type/interview_type.json | 73 + .../doctype/interview_type/interview_type.py | 10 + .../interview_type/test_interview_type.py | 9 + hrms/hr/doctype/interviewer/__init__.py | 0 hrms/hr/doctype/interviewer/interviewer.json | 31 + hrms/hr/doctype/interviewer/interviewer.py | 10 + hrms/hr/doctype/job_applicant/README.md | 1 + hrms/hr/doctype/job_applicant/__init__.py | 0 .../hr/doctype/job_applicant/job_applicant.js | 105 + .../doctype/job_applicant/job_applicant.json | 221 ++ .../hr/doctype/job_applicant/job_applicant.py | 109 + .../job_applicant_dashboard.html | 52 + .../job_applicant/job_applicant_dashboard.py | 9 + .../job_applicant/job_applicant_list.js | 15 + .../job_applicant/test_job_applicant.py | 59 + .../doctype/job_applicant_source/__init__.py | 0 .../job_applicant_source.js | 8 + .../job_applicant_source.json | 124 ++ .../job_applicant_source.py | 9 + .../test_job_applicant_source.py | 8 + hrms/hr/doctype/job_offer/__init__.py | 0 hrms/hr/doctype/job_offer/job_offer.js | 51 + hrms/hr/doctype/job_offer/job_offer.json | 190 ++ hrms/hr/doctype/job_offer/job_offer.py | 125 ++ hrms/hr/doctype/job_offer/job_offer_list.js | 15 + hrms/hr/doctype/job_offer/test_job_offer.py | 113 + hrms/hr/doctype/job_offer_term/__init__.py | 0 .../job_offer_term/job_offer_term.json | 130 ++ .../doctype/job_offer_term/job_offer_term.py | 9 + hrms/hr/doctype/job_opening/README.md | 1 + hrms/hr/doctype/job_opening/__init__.py | 0 hrms/hr/doctype/job_opening/job_opening.js | 46 + hrms/hr/doctype/job_opening/job_opening.json | 188 ++ hrms/hr/doctype/job_opening/job_opening.py | 100 + .../job_opening/job_opening_dashboard.py | 5 + .../templates/job_opening_row.html | 18 + .../doctype/job_opening/test_job_opening.py | 80 + hrms/hr/doctype/leave_allocation/README.md | 1 + hrms/hr/doctype/leave_allocation/__init__.py | 0 .../leave_allocation/leave_allocation.js | 131 ++ .../leave_allocation/leave_allocation.json | 286 +++ .../leave_allocation/leave_allocation.py | 370 ++++ .../leave_allocation_dashboard.py | 6 + .../leave_allocation/leave_allocation_list.js | 11 + .../leave_allocation/test_leave_allocation.py | 434 ++++ .../leave_allocation/test_records.json | 20 + hrms/hr/doctype/leave_application/README.md | 1 + hrms/hr/doctype/leave_application/__init__.py | 0 .../leave_application/leave_application.js | 270 +++ .../leave_application/leave_application.json | 338 +++ .../leave_application/leave_application.py | 1247 +++++++++++ .../leave_application_calendar.js | 22 + .../leave_application_dashboard.html | 29 + .../leave_application_dashboard.py | 9 + .../leave_application_email_template.html | 30 + .../leave_application_list.js | 14 + .../test_leave_application.py | 1140 ++++++++++ .../leave_application/test_records.json | 1 + hrms/hr/doctype/leave_block_list/README.md | 1 + hrms/hr/doctype/leave_block_list/__init__.py | 0 .../leave_block_list/leave_block_list.js | 8 + .../leave_block_list/leave_block_list.json | 248 +++ .../leave_block_list/leave_block_list.py | 78 + .../leave_block_list_dashboard.py | 2 + .../leave_block_list/test_leave_block_list.py | 50 + .../leave_block_list/test_records.json | 27 + .../doctype/leave_block_list_allow/README.md | 1 + .../leave_block_list_allow/__init__.py | 0 .../leave_block_list_allow.json | 60 + .../leave_block_list_allow.py | 11 + .../doctype/leave_block_list_date/README.md | 1 + .../doctype/leave_block_list_date/__init__.py | 0 .../leave_block_list_date.json | 85 + .../leave_block_list_date.py | 11 + hrms/hr/doctype/leave_control_panel/README.md | 1 + .../doctype/leave_control_panel/__init__.py | 0 .../leave_control_panel.js | 24 + .../leave_control_panel.json | 158 ++ .../leave_control_panel.py | 61 + .../test_leave_control_panel.py | 8 + hrms/hr/doctype/leave_encashment/__init__.py | 0 .../leave_encashment/leave_encashment.js | 64 + .../leave_encashment/leave_encashment.json | 228 ++ .../leave_encashment/leave_encashment.py | 190 ++ .../leave_encashment/test_leave_encashment.py | 160 ++ .../hr/doctype/leave_ledger_entry/__init__.py | 0 .../leave_ledger_entry/leave_ledger_entry.js | 8 + .../leave_ledger_entry.json | 190 ++ .../leave_ledger_entry/leave_ledger_entry.py | 227 ++ .../leave_ledger_entry_list.js | 13 + .../test_leave_ledger_entry.py | 9 + hrms/hr/doctype/leave_period/__init__.py | 0 hrms/hr/doctype/leave_period/leave_period.js | 20 + .../hr/doctype/leave_period/leave_period.json | 108 + hrms/hr/doctype/leave_period/leave_period.py | 20 + .../leave_period/leave_period_dashboard.py | 8 + .../doctype/leave_period/test_leave_period.py | 40 + hrms/hr/doctype/leave_policy/__init__.py | 0 hrms/hr/doctype/leave_policy/leave_policy.js | 31 + .../hr/doctype/leave_policy/leave_policy.json | 107 + hrms/hr/doctype/leave_policy/leave_policy.py | 22 + .../leave_policy/leave_policy_dashboard.py | 10 + .../doctype/leave_policy/test_leave_policy.py | 39 + .../leave_policy_assignment/__init__.py | 0 .../leave_policy_assignment.js | 59 + .../leave_policy_assignment.json | 171 ++ .../leave_policy_assignment.py | 257 +++ .../leave_policy_assignment_dashboard.py | 10 + .../leave_policy_assignment_list.js | 117 ++ .../test_leave_policy_assignment.py | 376 ++++ .../doctype/leave_policy_detail/__init__.py | 0 .../leave_policy_detail.js | 8 + .../leave_policy_detail.json | 104 + .../leave_policy_detail.py | 9 + .../test_leave_policy_detail.py | 8 + hrms/hr/doctype/leave_type/README.md | 6 + hrms/hr/doctype/leave_type/__init__.py | 0 hrms/hr/doctype/leave_type/leave_type.js | 38 + hrms/hr/doctype/leave_type/leave_type.json | 265 +++ hrms/hr/doctype/leave_type/leave_type.py | 33 + .../leave_type/leave_type_dashboard.py | 10 + hrms/hr/doctype/leave_type/test_leave_type.py | 32 + hrms/hr/doctype/leave_type/test_records.json | 27 + hrms/hr/doctype/offer_term/__init__.py | 0 hrms/hr/doctype/offer_term/offer_term.js | 8 + hrms/hr/doctype/offer_term/offer_term.json | 84 + hrms/hr/doctype/offer_term/offer_term.py | 9 + hrms/hr/doctype/offer_term/test_offer_term.py | 10 + hrms/hr/doctype/purpose_of_travel/__init__.py | 0 .../purpose_of_travel/purpose_of_travel.js | 8 + .../purpose_of_travel/purpose_of_travel.json | 93 + .../purpose_of_travel/purpose_of_travel.py | 9 + .../test_purpose_of_travel.py | 8 + hrms/hr/doctype/shift_assignment/__init__.py | 0 .../shift_assignment/shift_assignment.js | 8 + .../shift_assignment/shift_assignment.json | 155 ++ .../shift_assignment/shift_assignment.py | 518 +++++ .../shift_assignment_calendar.js | 13 + .../shift_assignment/test_shift_assignment.py | 172 ++ hrms/hr/doctype/shift_request/__init__.py | 0 .../hr/doctype/shift_request/shift_request.js | 17 + .../doctype/shift_request/shift_request.json | 155 ++ .../hr/doctype/shift_request/shift_request.py | 140 ++ .../shift_request/shift_request_dashboard.py | 7 + .../shift_request/test_shift_request.py | 259 +++ hrms/hr/doctype/shift_type/__init__.py | 0 hrms/hr/doctype/shift_type/shift_type.js | 35 + hrms/hr/doctype/shift_type/shift_type.json | 204 ++ hrms/hr/doctype/shift_type/shift_type.py | 200 ++ .../shift_type/shift_type_dashboard.py | 8 + hrms/hr/doctype/shift_type/test_records.json | 8 + hrms/hr/doctype/shift_type/test_shift_type.py | 383 ++++ hrms/hr/doctype/skill/__init__.py | 0 hrms/hr/doctype/skill/skill.js | 8 + hrms/hr/doctype/skill/skill.json | 119 ++ hrms/hr/doctype/skill/skill.py | 10 + hrms/hr/doctype/skill_assessment/__init__.py | 0 .../skill_assessment/skill_assessment.json | 41 + .../skill_assessment/skill_assessment.py | 10 + hrms/hr/doctype/staffing_plan/__init__.py | 0 .../hr/doctype/staffing_plan/staffing_plan.js | 106 + .../doctype/staffing_plan/staffing_plan.json | 403 ++++ .../hr/doctype/staffing_plan/staffing_plan.py | 222 ++ .../staffing_plan/staffing_plan_dashboard.py | 5 + .../staffing_plan/test_staffing_plan.py | 98 + .../doctype/staffing_plan_detail/__init__.py | 0 .../staffing_plan_detail.json | 79 + .../staffing_plan_detail.py | 9 + hrms/hr/doctype/training_event/__init__.py | 0 .../training_event/test_training_event.py | 64 + .../doctype/training_event/training_event.js | 48 + .../training_event/training_event.json | 231 +++ .../doctype/training_event/training_event.py | 38 + .../training_event/training_event_calendar.js | 14 + .../training_event_dashboard.py | 7 + .../training_event_employee/__init__.py | 0 .../training_event_employee.json | 81 + .../training_event_employee.py | 9 + hrms/hr/doctype/training_feedback/__init__.py | 0 .../test_training_feedback.py | 72 + .../training_feedback/training_feedback.js | 10 + .../training_feedback/training_feedback.json | 143 ++ .../training_feedback/training_feedback.py | 47 + hrms/hr/doctype/training_program/__init__.py | 0 .../training_program/test_training_program.py | 8 + .../training_program/training_program.js | 5 + .../training_program/training_program.json | 454 ++++ .../training_program/training_program.py | 9 + .../training_program_dashboard.py | 10 + hrms/hr/doctype/training_result/__init__.py | 0 .../training_result/test_training_result.py | 10 + .../training_result/training_result.js | 34 + .../training_result/training_result.json | 83 + .../training_result/training_result.py | 34 + .../training_result_employee/__init__.py | 0 .../training_result_employee.json | 334 +++ .../training_result_employee.py | 9 + hrms/hr/doctype/travel_itinerary/__init__.py | 0 .../travel_itinerary/travel_itinerary.json | 512 +++++ .../travel_itinerary/travel_itinerary.py | 9 + hrms/hr/doctype/travel_request/__init__.py | 0 .../travel_request/test_travel_request.py | 8 + .../doctype/travel_request/travel_request.js | 8 + .../travel_request/travel_request.json | 245 +++ .../doctype/travel_request/travel_request.py | 12 + .../travel_request_costing/__init__.py | 0 .../travel_request_costing.json | 257 +++ .../travel_request_costing.py | 9 + hrms/hr/doctype/upload_attendance/README.md | 1 + hrms/hr/doctype/upload_attendance/__init__.py | 0 .../test_upload_attendance.py | 43 + .../upload_attendance/upload_attendance.js | 69 + .../upload_attendance/upload_attendance.json | 285 +++ .../upload_attendance/upload_attendance.py | 223 ++ hrms/hr/doctype/vehicle_log/__init__.py | 0 .../doctype/vehicle_log/test_vehicle_log.py | 132 ++ hrms/hr/doctype/vehicle_log/vehicle_log.js | 26 + hrms/hr/doctype/vehicle_log/vehicle_log.json | 191 ++ hrms/hr/doctype/vehicle_log/vehicle_log.py | 53 + hrms/hr/doctype/vehicle_service/__init__.py | 0 .../vehicle_service/vehicle_service.json | 57 + .../vehicle_service/vehicle_service.py | 9 + hrms/hr/employee_property_update.js | 174 ++ .../hr_dashboard/attendance/attendance.json | 46 + .../employee_lifecycle.json | 49 + .../expense_claims/expense_claims.json | 43 + .../human_resource/human_resource.json | 65 + .../hr_dashboard/recruitment/recruitment.json | 75 + .../human_resource/human_resource.json | 47 + hrms/hr/notification/__init__.py | 0 .../exit_interview_scheduled/__init__.py | 0 .../exit_interview_scheduled.json | 29 + .../exit_interview_scheduled.md | 37 + .../exit_interview_scheduled.py | 6 + .../training_feedback/__init__.py | 0 .../training_feedback/training_feedback.html | 6 + .../training_feedback/training_feedback.json | 25 + .../training_feedback/training_feedback.md | 9 + .../training_feedback/training_feedback.py | 3 + .../training_scheduled/__init__.py | 0 .../training_scheduled.html | 44 + .../training_scheduled.json | 28 + .../training_scheduled/training_scheduled.md | 47 + .../training_scheduled/training_scheduled.py | 3 + .../accepted_job_applicants.json | 25 + .../applicant_to_hire_percentage.json | 21 + .../approved_claims_(this_month).json | 25 + .../early_exit_(this_month).json | 25 + .../employee_exits_(this_year).json | 21 + .../employees_joining_(next_quarter).json | 24 + .../employees_relieving_(next_quarter).json | 24 + .../expense_claims_(this_month).json | 24 + .../job_offer_acceptance_rate.json | 22 + .../job_offers_(this_month).json | 24 + .../job_openings/job_openings.json | 24 + .../late_entry_(this_month).json | 25 + .../new_hires_(this_year).json | 21 + .../onboardings_(this_month).json | 24 + .../promotions_(this_month).json | 24 + .../rejected_claims_(this_month).json | 25 + .../rejected_job_applicants.json | 25 + .../separations_(this_month).json | 24 + .../total_absent_(this_month).json | 25 + .../total_applicants_(this_month).json | 21 + .../total_employees/total_employees.json | 21 + .../total_present_(this_month).json | 25 + .../trainings_(this_month).json | 24 + .../transfers_(this_month).json | 24 + .../create_department/create_department.json | 19 + .../create_designation.json | 19 + .../create_employee/create_employee.json | 21 + .../create_holiday_list.json | 21 + .../create_leave_allocation.json | 21 + .../create_leave_application.json | 21 + .../create_leave_type/create_leave_type.json | 21 + .../data_import/data_import.json | 21 + .../hr_settings/hr_settings.json | 21 + hrms/hr/page/__init__.py | 0 hrms/hr/page/organizational_chart/__init__.py | 0 .../organizational_chart.js | 23 + .../organizational_chart.json | 26 + .../organizational_chart.py | 45 + hrms/hr/page/team_updates/__init__.py | 0 .../hr/page/team_updates/team_update_row.html | 15 + hrms/hr/page/team_updates/team_updates.css | 57 + hrms/hr/page/team_updates/team_updates.js | 80 + hrms/hr/page/team_updates/team_updates.json | 25 + hrms/hr/page/team_updates/team_updates.py | 25 + hrms/hr/print_format/__init__.py | 0 hrms/hr/print_format/job_offer/__init__.py | 0 hrms/hr/print_format/job_offer/job_offer.json | 21 + .../standard_appointment_letter/__init__.py | 0 .../standard_appointment_letter.html | 38 + .../standard_appointment_letter.json | 23 + hrms/hr/report/__init__.py | 0 .../daily_work_summary_replies/__init__.py | 0 .../daily_work_summary_replies.js | 21 + .../daily_work_summary_replies.json | 25 + .../daily_work_summary_replies.py | 61 + .../employee_advance_summary/__init__.py | 0 .../employee_advance_summary.js | 40 + .../employee_advance_summary.json | 26 + .../employee_advance_summary.py | 105 + hrms/hr/report/employee_analytics/__init__.py | 0 .../employee_analytics/employee_analytics.js | 23 + .../employee_analytics.json | 30 + .../employee_analytics/employee_analytics.py | 99 + hrms/hr/report/employee_birthday/__init__.py | 0 .../employee_birthday/employee_birthday.js | 22 + .../employee_birthday/employee_birthday.json | 29 + .../employee_birthday/employee_birthday.py | 65 + hrms/hr/report/employee_exits/__init__.py | 0 .../report/employee_exits/employee_exits.js | 77 + .../report/employee_exits/employee_exits.json | 33 + .../report/employee_exits/employee_exits.py | 237 +++ .../employee_exits/test_employee_exits.py | 246 +++ .../__init__.py | 0 ...ee_hours_utilization_based_on_timesheet.js | 48 + ..._hours_utilization_based_on_timesheet.json | 29 + ...ee_hours_utilization_based_on_timesheet.py | 261 +++ .../test_employee_util.py | 200 ++ .../report/employee_information/__init__.py | 0 .../employee_information.json | 27 + .../report/employee_leave_balance/__init__.py | 0 .../employee_leave_balance.js | 71 + .../employee_leave_balance.json | 26 + .../employee_leave_balance.py | 307 +++ .../test_employee_leave_balance.py | 245 +++ .../__init__.py | 0 .../employee_leave_balance_summary.js | 48 + .../employee_leave_balance_summary.json | 33 + .../employee_leave_balance_summary.py | 85 + .../test_employee_leave_balance_summary.py | 180 ++ .../__init__.py | 0 .../employees_working_on_a_holiday.js | 27 + .../employees_working_on_a_holiday.json | 29 + .../employees_working_on_a_holiday.py | 70 + .../monthly_attendance_sheet/__init__.py | 0 .../monthly_attendance_sheet.js | 101 + .../monthly_attendance_sheet.json | 29 + .../monthly_attendance_sheet.py | 620 ++++++ .../test_monthly_attendance_sheet.py | 251 +++ .../report/project_profitability/__init__.py | 0 .../project_profitability.js | 48 + .../project_profitability.json | 44 + .../project_profitability.py | 200 ++ .../test_project_profitability.py | 72 + .../report/recruitment_analytics/__init__.py | 0 .../recruitment_analytics.js | 23 + .../recruitment_analytics.json | 27 + .../recruitment_analytics.py | 198 ++ .../report/unpaid_expense_claim/__init__.py | 0 .../unpaid_expense_claim.js | 13 + .../unpaid_expense_claim.json | 29 + .../unpaid_expense_claim.py | 49 + hrms/hr/report/vehicle_expenses/__init__.py | 0 .../vehicle_expenses/test_vehicle_expenses.py | 78 + .../vehicle_expenses/vehicle_expenses.js | 51 + .../vehicle_expenses/vehicle_expenses.json | 26 + .../vehicle_expenses/vehicle_expenses.py | 177 ++ hrms/hr/utils.py | 676 ++++++ hrms/hr/web_form/__init__.py | 0 hrms/hr/web_form/job_application/__init__.py | 0 .../job_application/job_application.js | 3 + .../job_application/job_application.json | 200 ++ .../job_application/job_application.py | 3 + .../employee_lifecycle.json | 353 ++++ .../expense_claims/expense_claims.json | 285 +++ hrms/hr/workspace/hr/hr.json | 342 +++ hrms/hr/workspace/leaves/leaves.json | 205 ++ .../hr/workspace/performance/performance.json | 120 ++ .../hr/workspace/recruitment/recruitment.json | 227 ++ .../shift_&_attendance.json | 235 +++ hrms/hrms.png | Bin 0 -> 1180791 bytes hrms/modules.txt | 2 + hrms/overrides/company.py | 101 + hrms/overrides/dashboard_overrides.py | 76 + hrms/overrides/employee_master.py | 116 ++ hrms/overrides/employee_payment_entry.py | 263 +++ hrms/overrides/employee_project.py | 56 + hrms/overrides/employee_timesheet.py | 18 + hrms/patches.txt | 5 + .../add_expense_claim_default_account.py | 16 + .../post_install/create_country_fixtures.py | 9 + ...lete_employee_transfer_property_doctype.py | 5 + .../drop_column_max_days_allowed.py | 7 + .../generate_leave_ledger_entries.py | 107 + ...ry_settings_to_daily_work_summary_group.py | 58 + ...rts_and_notification_from_hr_to_payroll.py | 50 + ...ve_due_advance_amount_to_pending_amount.py | 18 + .../move_leave_approvers_from_employee.py | 34 + ...oll_setting_separately_from_hr_settings.py | 30 + ..._from_payroll_period_to_income_tax_slab.py | 146 ++ .../remove_denied_leaves_from_leave_ledger.py | 35 + .../remove_duplicate_leave_ledger_entries.py | 55 + ...onal_salary_component_additional_salary.py | 11 + .../post_install/rename_depends_on_lwp.py | 14 + .../rename_field_max_days_allowed.py | 18 + .../rename_offer_letter_to_job_offer.py | 11 + .../rename_stop_to_send_birthday_reminders.py | 21 + .../set_company_in_leave_ledger_entry.py | 20 + .../set_department_for_doctypes.py | 37 + .../set_employee_preferred_emails.py | 20 + .../set_job_offer_applicant_email.py | 14 + .../post_install/set_payroll_cost_centers.py | 29 + .../post_install/set_payroll_entry_status.py | 18 + .../set_salary_details_submittable.py | 11 + .../set_training_event_attendance.py | 27 + .../update_employee_advance_status.py | 29 + ..._expense_claim_status_for_paid_advances.py | 25 + ...date_reason_for_resignation_in_employee.py | 17 + ...start_end_date_for_old_shift_assignment.py | 15 + .../updates_for_multi_currency_payroll.py | 139 ++ .../patches/v1_0/rearrange_employee_fields.py | 153 ++ hrms/payroll/__init__.py | 0 .../department_wise_salary(last_month).json | 30 + .../designation_wise_salary(last_month).json | 30 + .../outgoing_salary/outgoing_salary.json | 29 + hrms/payroll/data/salary_components.json | 27 + hrms/payroll/doctype/__init__.py | 0 .../doctype/additional_salary/__init__.py | 0 .../additional_salary/additional_salary.js | 84 + .../additional_salary/additional_salary.json | 249 +++ .../additional_salary/additional_salary.py | 213 ++ .../test_additional_salary.py | 104 + .../employee_benefit_application/__init__.py | 0 .../employee_benefit_application.js | 127 ++ .../employee_benefit_application.json | 222 ++ .../employee_benefit_application.py | 371 ++++ .../test_employee_benefit_application.py | 85 + .../__init__.py | 0 .../employee_benefit_application_detail.json | 60 + .../employee_benefit_application_detail.py | 10 + .../employee_benefit_claim/__init__.py | 0 .../employee_benefit_claim.js | 34 + .../employee_benefit_claim.json | 218 ++ .../employee_benefit_claim.py | 241 +++ .../test_employee_benefit_claim.py | 8 + .../doctype/employee_cost_center/__init__.py | 0 .../employee_cost_center.json | 43 + .../employee_cost_center.py | 9 + .../doctype/employee_incentive/__init__.py | 0 .../employee_incentive/employee_incentive.js | 69 + .../employee_incentive.json | 146 ++ .../employee_incentive/employee_incentive.py | 38 + .../test_employee_incentive.py | 8 + .../doctype/employee_other_income/__init__.py | 0 .../employee_other_income.js | 8 + .../employee_other_income.json | 139 ++ .../employee_other_income.py | 10 + .../test_employee_other_income.py | 9 + .../__init__.py | 0 .../employee_tax_exemption_category.js | 8 + .../employee_tax_exemption_category.json | 74 + .../employee_tax_exemption_category.py | 9 + .../test_employee_tax_exemption_category.py | 8 + .../__init__.py | 0 .../employee_tax_exemption_declaration.js | 72 + .../employee_tax_exemption_declaration.json | 196 ++ .../employee_tax_exemption_declaration.py | 78 + ...test_employee_tax_exemption_declaration.py | 491 +++++ .../__init__.py | 0 ...ee_tax_exemption_declaration_category.json | 63 + ...oyee_tax_exemption_declaration_category.py | 10 + .../__init__.py | 0 ...employee_tax_exemption_proof_submission.js | 83 + ...ployee_tax_exemption_proof_submission.json | 219 ++ ...employee_tax_exemption_proof_submission.py | 53 + ...employee_tax_exemption_proof_submission.py | 137 ++ .../__init__.py | 0 ...tax_exemption_proof_submission_detail.json | 68 + ...e_tax_exemption_proof_submission_detail.py | 10 + .../__init__.py | 0 .../employee_tax_exemption_sub_category.js | 8 + .../employee_tax_exemption_sub_category.json | 86 + .../employee_tax_exemption_sub_category.py | 21 + ...est_employee_tax_exemption_sub_category.py | 8 + hrms/payroll/doctype/gratuity/__init__.py | 0 hrms/payroll/doctype/gratuity/gratuity.js | 73 + hrms/payroll/doctype/gratuity/gratuity.json | 234 +++ hrms/payroll/doctype/gratuity/gratuity.py | 356 ++++ .../doctype/gratuity/gratuity_dashboard.py | 11 + .../payroll/doctype/gratuity/gratuity_list.js | 12 + .../payroll/doctype/gratuity/test_gratuity.py | 237 +++ .../gratuity_applicable_component/__init__.py | 0 .../gratuity_applicable_component.json | 32 + .../gratuity_applicable_component.py | 10 + .../payroll/doctype/gratuity_rule/__init__.py | 0 .../doctype/gratuity_rule/gratuity_rule.js | 40 + .../doctype/gratuity_rule/gratuity_rule.json | 114 + .../doctype/gratuity_rule/gratuity_rule.py | 42 + .../gratuity_rule/gratuity_rule_dashboard.py | 8 + .../gratuity_rule/test_gratuity_rule.py | 9 + .../doctype/gratuity_rule_slab/__init__.py | 0 .../gratuity_rule_slab.json | 50 + .../gratuity_rule_slab/gratuity_rule_slab.py | 10 + .../doctype/income_tax_slab/__init__.py | 0 .../income_tax_slab/income_tax_slab.js | 8 + .../income_tax_slab/income_tax_slab.json | 162 ++ .../income_tax_slab/income_tax_slab.py | 14 + .../income_tax_slab/test_income_tax_slab.py | 9 + .../income_tax_slab_other_charges/__init__.py | 0 .../income_tax_slab_other_charges.json | 75 + .../income_tax_slab_other_charges.py | 10 + .../payroll_employee_detail/__init__.py | 0 .../payroll_employee_detail.json | 65 + .../payroll_employee_detail.py | 9 + .../payroll/doctype/payroll_entry/__init__.py | 0 .../doctype/payroll_entry/payroll_entry.js | 418 ++++ .../doctype/payroll_entry/payroll_entry.json | 342 +++ .../doctype/payroll_entry/payroll_entry.py | 1063 ++++++++++ .../payroll_entry/payroll_entry_dashboard.py | 9 + .../payroll_entry/payroll_entry_list.js | 18 + .../payroll_entry/test_payroll_entry.py | 490 +++++ .../doctype/payroll_period/__init__.py | 0 .../doctype/payroll_period/payroll_period.js | 8 + .../payroll_period/payroll_period.json | 103 + .../doctype/payroll_period/payroll_period.py | 130 ++ .../payroll_period_dashboard.py | 7 + .../payroll_period/test_payroll_period.py | 8 + .../doctype/payroll_period_date/__init__.py | 0 .../payroll_period_date.json | 39 + .../payroll_period_date.py | 10 + .../doctype/payroll_settings/__init__.py | 0 .../payroll_settings/payroll_settings.js | 19 + .../payroll_settings/payroll_settings.json | 119 ++ .../payroll_settings/payroll_settings.py | 45 + .../payroll_settings/test_payroll_settings.py | 9 + .../doctype/retention_bonus/__init__.py | 0 .../retention_bonus/retention_bonus.js | 43 + .../retention_bonus/retention_bonus.json | 173 ++ .../retention_bonus/retention_bonus.py | 69 + .../retention_bonus/test_retention_bonus.py | 8 + .../doctype/salary_component/README.md | 1 + .../doctype/salary_component/__init__.py | 0 .../salary_component/salary_component.js | 69 + .../salary_component/salary_component.json | 272 +++ .../salary_component/salary_component.py | 24 + .../salary_component/test_records.json | 36 + .../salary_component/test_salary_component.py | 24 + .../salary_component_account/__init__.py | 0 .../salary_component_account.json | 38 + .../salary_component_account.py | 9 + .../payroll/doctype/salary_detail/__init__.py | 0 .../doctype/salary_detail/salary_detail.json | 260 +++ .../doctype/salary_detail/salary_detail.py | 9 + hrms/payroll/doctype/salary_slip/README.md | 1 + hrms/payroll/doctype/salary_slip/__init__.py | 0 .../doctype/salary_slip/salary_slip.js | 290 +++ .../doctype/salary_slip/salary_slip.json | 685 ++++++ .../doctype/salary_slip/salary_slip.py | 1827 +++++++++++++++++ .../doctype/salary_slip/salary_slip_list.js | 3 + .../doctype/salary_slip/test_salary_slip.py | 1715 ++++++++++++++++ .../doctype/salary_slip_leave/__init__.py | 0 .../salary_slip_leave/salary_slip_leave.json | 79 + .../salary_slip_leave/salary_slip_leave.py | 10 + .../doctype/salary_slip_loan/__init__.py | 0 .../salary_slip_loan/salary_slip_loan.json | 101 + .../salary_slip_loan/salary_slip_loan.py | 10 + .../doctype/salary_slip_timesheet/__init__.py | 0 .../salary_slip_timesheet.json | 40 + .../salary_slip_timesheet.py | 10 + .../doctype/salary_structure/README.md | 1 + .../doctype/salary_structure/__init__.py | 0 .../condition_and_formula_help.html | 47 + .../salary_structure/salary_structure.js | 349 ++++ .../salary_structure/salary_structure.json | 278 +++ .../salary_structure/salary_structure.py | 366 ++++ .../salary_structure_dashboard.py | 9 + .../salary_structure/test_salary_structure.py | 286 +++ .../salary_structure_assignment/__init__.py | 0 .../salary_structure_assignment.js | 76 + .../salary_structure_assignment.json | 231 +++ .../salary_structure_assignment.py | 134 ++ .../test_salary_structure_assignment.py | 8 + .../doctype/taxable_salary_slab/__init__.py | 0 .../taxable_salary_slab.json | 68 + .../taxable_salary_slab.py | 10 + .../module_onboarding/payroll/payroll.json | 50 + hrms/payroll/notification/as | 1 + .../notification/retention_bonus/__init__.py | 0 .../retention_bonus/retention_bonus.json | 27 + .../retention_bonus/retention_bonus.md | 3 + .../retention_bonus/retention_bonus.py | 3 + .../total_declaration_submitted.json | 21 + .../total_incentive_given(last_month).json | 22 + .../total_outgoing_salary(last_month).json | 22 + .../total_salary_structure.json | 21 + .../assign_salary_structure.json | 19 + .../create_employee/create_employee.json | 19 + .../create_income_tax_slab.json | 19 + .../create_payroll_period.json | 19 + .../create_salary_component.json | 19 + .../create_salary_slip.json | 19 + .../create_salary_structure.json | 19 + .../payroll_settings/payroll_settings.json | 19 + .../payroll_dashboard/payroll/payroll.json | 42 + hrms/payroll/print_format/__init__.py | 0 .../__init__.py | 0 .../salary_slip_based_on_timesheet.json | 18 + .../salary_slip_standard/__init__.py | 0 .../salary_slip_standard.json | 22 + .../salary_slip_with_year_to_date/__init__.py | 0 .../salary_slip_with_year_to_date.json | 25 + hrms/payroll/report/__init__.py | 0 .../report/bank_remittance/__init__.py | 0 .../report/bank_remittance/bank_remittance.js | 27 + .../bank_remittance/bank_remittance.json | 24 + .../report/bank_remittance/bank_remittance.py | 173 ++ .../report/income_tax_computation/__init__.py | 0 .../income_tax_computation.js | 47 + .../income_tax_computation.json | 36 + .../income_tax_computation.py | 516 +++++ .../test_income_tax_computation.py | 116 ++ .../report/income_tax_deductions/__init__.py | 0 .../income_tax_deductions.js | 7 + .../income_tax_deductions.json | 30 + .../income_tax_deductions.py | 136 ++ .../professional_tax_deductions/__init__.py | 0 .../professional_tax_deductions.js | 7 + .../professional_tax_deductions.json | 20 + .../professional_tax_deductions.py | 76 + .../provident_fund_deductions/__init__.py | 0 .../provident_fund_deductions.js | 7 + .../provident_fund_deductions.json | 20 + .../provident_fund_deductions.py | 174 ++ .../__init__.py | 0 .../salary_payments_based_on_payment_mode.js | 7 + ...salary_payments_based_on_payment_mode.json | 30 + .../salary_payments_based_on_payment_mode.py | 179 ++ .../salary_payments_via_ecs/__init__.py | 0 .../salary_payments_via_ecs.js | 16 + .../salary_payments_via_ecs.json | 27 + .../salary_payments_via_ecs.py | 140 ++ .../report/salary_register/__init__.py | 0 .../salary_register/salary_register.html | 40 + .../report/salary_register/salary_register.js | 55 + .../salary_register/salary_register.json | 27 + .../report/salary_register/salary_register.py | 229 +++ hrms/payroll/workspace/payroll/payroll.json | 91 + .../salary_payout/salary_payout.json | 426 ++++ .../tax_&_benefits/tax_&_benefits.json | 172 ++ hrms/public/.gitkeep | 0 hrms/public/js/bank_transaction.js | 14 + hrms/public/js/company.js | 41 + hrms/public/js/delivery_trip.js | 15 + hrms/public/js/department.js | 15 + hrms/public/js/employee.js | 35 + hrms/public/js/hrms.bundle.js | 1 + hrms/public/js/journal_entry.js | 34 + hrms/public/js/payment_entry.js | 84 + .../salary_slip_deductions_report_filters.js | 65 + .../employees_to_mark_attendance.html | 14 + hrms/public/js/timesheet.js | 21 + .../india/data/salary_components.json | 44 + hrms/regional/india/setup.py | 269 +++ hrms/regional/india/utils.py | 203 ++ hrms/regional/united_arab_emirates/setup.py | 59 + hrms/setup.py | 659 ++++++ hrms/subscription_utils.py | 177 ++ hrms/templates/__init__.py | 0 .../emails/anniversary_reminder.html | 25 + hrms/templates/emails/birthday_reminder.html | 25 + hrms/templates/emails/daily_work_summary.html | 55 + hrms/templates/emails/daily_work_summary.txt | 11 + hrms/templates/emails/holiday_reminder.html | 16 + hrms/templates/emails/training_event.html | 21 + hrms/templates/generators/job_opening.html | 33 + hrms/templates/includes/salary_slip_log.html | 19 + hrms/templates/pages/__init__.py | 0 hrms/tests/test_utils.py | 19 + hrms/utils.py | 60 + hrms/www/__init__.py | 0 license.txt | 675 ++++++ pyproject.toml | 29 + requirements.txt | 1 + setup.py | 5 + sider.yml | 3 + 1013 files changed, 73025 insertions(+) create mode 100644 .flake8 create mode 100644 .git-blame-ignore-revs create mode 100644 .github/CODEOWNERS create mode 100644 .github/helper/.flake8_strict create mode 100644 .github/helper/documentation.py create mode 100644 .github/helper/install.sh create mode 100644 .github/helper/site_config.json create mode 100644 .github/helper/translation.py create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/docs_checker.yml create mode 100644 .github/workflows/linters.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .semgrepignore create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 codecov.yml create mode 100644 hrms.png create mode 100644 hrms/__init__.py create mode 100644 hrms/config/__init__.py create mode 100644 hrms/config/desktop.py create mode 100644 hrms/config/docs.py create mode 100644 hrms/controllers/employee_boarding_controller.py create mode 100644 hrms/controllers/employee_reminders.py create mode 100644 hrms/controllers/tests/test_employee_reminders.py create mode 100644 hrms/hooks.py create mode 100644 hrms/hr/README.md create mode 100644 hrms/hr/__init__.py create mode 100644 hrms/hr/dashboard_chart/attendance_count/attendance_count.json create mode 100644 hrms/hr/dashboard_chart/claims_by_type/claims_by_type.json create mode 100644 hrms/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json create mode 100644 hrms/hr/dashboard_chart/department_wise_expense_claims/department_wise_expense_claims.json create mode 100644 hrms/hr/dashboard_chart/department_wise_openings/department_wise_openings.json create mode 100644 hrms/hr/dashboard_chart/department_wise_timesheet_hours/department_wise_timesheet_hours.json create mode 100644 hrms/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json create mode 100644 hrms/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json create mode 100644 hrms/hr/dashboard_chart/employee_advance_status/employee_advance_status.json create mode 100644 hrms/hr/dashboard_chart/employees_by_age/employees_by_age.json create mode 100644 hrms/hr/dashboard_chart/employees_by_branch/employees_by_branch.json create mode 100644 hrms/hr/dashboard_chart/employees_by_grade/employees_by_grade.json create mode 100644 hrms/hr/dashboard_chart/employees_by_type/employees_by_type.json create mode 100644 hrms/hr/dashboard_chart/expense_claims/expense_claims.json create mode 100644 hrms/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json create mode 100644 hrms/hr/dashboard_chart/grievance_type/grievance_type.json create mode 100644 hrms/hr/dashboard_chart/hiring_vs_attrition_count/hiring_vs_attrition_count.json create mode 100644 hrms/hr/dashboard_chart/interview_status/interview_status.json create mode 100644 hrms/hr/dashboard_chart/job_applicant_pipeline/job_applicant_pipeline.json create mode 100644 hrms/hr/dashboard_chart/job_applicant_source/job_applicant_source.json create mode 100644 hrms/hr/dashboard_chart/job_applicants_by_country/job_applicants_by_country.json create mode 100644 hrms/hr/dashboard_chart/job_application_frequency/job_application_frequency.json create mode 100644 hrms/hr/dashboard_chart/job_application_status/job_application_status.json create mode 100644 hrms/hr/dashboard_chart/job_offer_status/job_offer_status.json create mode 100644 hrms/hr/dashboard_chart/shift_assignment_breakup/shift_assignment_breakup.json create mode 100644 hrms/hr/dashboard_chart/timesheet_activity_breakup/timesheet_activity_breakup.json create mode 100644 hrms/hr/dashboard_chart/training_type/training_type.json create mode 100644 hrms/hr/dashboard_chart/y_o_y_promotions/y_o_y_promotions.json create mode 100644 hrms/hr/dashboard_chart/y_o_y_transfers/y_o_y_transfers.json create mode 100644 hrms/hr/dashboard_chart_source/__init__.py create mode 100644 hrms/hr/dashboard_chart_source/employees_by_age/__init__.py create mode 100644 hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.js create mode 100644 hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.json create mode 100644 hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.py create mode 100644 hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/__init__.py create mode 100644 hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.js create mode 100644 hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.json create mode 100644 hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.py create mode 100644 hrms/hr/doctype/__init__.py create mode 100644 hrms/hr/doctype/appointment_letter/__init__.py create mode 100644 hrms/hr/doctype/appointment_letter/appointment_letter.js create mode 100644 hrms/hr/doctype/appointment_letter/appointment_letter.json create mode 100644 hrms/hr/doctype/appointment_letter/appointment_letter.py create mode 100644 hrms/hr/doctype/appointment_letter/test_appointment_letter.py create mode 100644 hrms/hr/doctype/appointment_letter_content/__init__.py create mode 100644 hrms/hr/doctype/appointment_letter_content/appointment_letter_content.json create mode 100644 hrms/hr/doctype/appointment_letter_content/appointment_letter_content.py create mode 100644 hrms/hr/doctype/appointment_letter_template/__init__.py create mode 100644 hrms/hr/doctype/appointment_letter_template/appointment_letter_template.js create mode 100644 hrms/hr/doctype/appointment_letter_template/appointment_letter_template.json create mode 100644 hrms/hr/doctype/appointment_letter_template/appointment_letter_template.py create mode 100644 hrms/hr/doctype/appointment_letter_template/test_appointment_letter_template.py create mode 100644 hrms/hr/doctype/appraisal/README.md create mode 100644 hrms/hr/doctype/appraisal/__init__.py create mode 100644 hrms/hr/doctype/appraisal/appraisal.js create mode 100644 hrms/hr/doctype/appraisal/appraisal.json create mode 100644 hrms/hr/doctype/appraisal/appraisal.py create mode 100644 hrms/hr/doctype/appraisal/test_appraisal.py create mode 100644 hrms/hr/doctype/appraisal_goal/README.md create mode 100644 hrms/hr/doctype/appraisal_goal/__init__.py create mode 100644 hrms/hr/doctype/appraisal_goal/appraisal_goal.json create mode 100644 hrms/hr/doctype/appraisal_goal/appraisal_goal.py create mode 100644 hrms/hr/doctype/appraisal_template/README.md create mode 100644 hrms/hr/doctype/appraisal_template/__init__.py create mode 100644 hrms/hr/doctype/appraisal_template/appraisal_template.js create mode 100644 hrms/hr/doctype/appraisal_template/appraisal_template.json create mode 100644 hrms/hr/doctype/appraisal_template/appraisal_template.py create mode 100644 hrms/hr/doctype/appraisal_template/appraisal_template_dashboard.py create mode 100644 hrms/hr/doctype/appraisal_template/test_appraisal_template.py create mode 100644 hrms/hr/doctype/appraisal_template_goal/README.md create mode 100644 hrms/hr/doctype/appraisal_template_goal/__init__.py create mode 100644 hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.json create mode 100644 hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.py create mode 100644 hrms/hr/doctype/attendance/README.md create mode 100644 hrms/hr/doctype/attendance/__init__.py create mode 100644 hrms/hr/doctype/attendance/attendance.js create mode 100644 hrms/hr/doctype/attendance/attendance.json create mode 100644 hrms/hr/doctype/attendance/attendance.py create mode 100644 hrms/hr/doctype/attendance/attendance_calendar.js create mode 100644 hrms/hr/doctype/attendance/attendance_dashboard.py create mode 100644 hrms/hr/doctype/attendance/attendance_list.js create mode 100644 hrms/hr/doctype/attendance/test_attendance.py create mode 100644 hrms/hr/doctype/attendance/test_records.json create mode 100644 hrms/hr/doctype/attendance_request/__init__.py create mode 100644 hrms/hr/doctype/attendance_request/attendance_request.js create mode 100644 hrms/hr/doctype/attendance_request/attendance_request.json create mode 100644 hrms/hr/doctype/attendance_request/attendance_request.py create mode 100644 hrms/hr/doctype/attendance_request/attendance_request_dashboard.py create mode 100644 hrms/hr/doctype/attendance_request/test_attendance_request.py create mode 100644 hrms/hr/doctype/compensatory_leave_request/__init__.py create mode 100644 hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.js create mode 100644 hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.json create mode 100644 hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.py create mode 100644 hrms/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py create mode 100644 hrms/hr/doctype/daily_work_summary/__init__.py create mode 100644 hrms/hr/doctype/daily_work_summary/daily_work_summary.js create mode 100644 hrms/hr/doctype/daily_work_summary/daily_work_summary.json create mode 100644 hrms/hr/doctype/daily_work_summary/daily_work_summary.py create mode 100644 hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py create mode 100644 hrms/hr/doctype/daily_work_summary/test_data/test-reply.raw create mode 100644 hrms/hr/doctype/daily_work_summary_group/__init__.py create mode 100644 hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.js create mode 100644 hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.json create mode 100644 hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.py create mode 100644 hrms/hr/doctype/daily_work_summary_group_user/__init__.py create mode 100644 hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json create mode 100644 hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py create mode 100644 hrms/hr/doctype/department_approver/__init__.py create mode 100644 hrms/hr/doctype/department_approver/department_approver.json create mode 100644 hrms/hr/doctype/department_approver/department_approver.py create mode 100644 hrms/hr/doctype/designation_skill/__init__.py create mode 100644 hrms/hr/doctype/designation_skill/designation_skill.json create mode 100644 hrms/hr/doctype/designation_skill/designation_skill.py create mode 100644 hrms/hr/doctype/employee_advance/__init__.py create mode 100644 hrms/hr/doctype/employee_advance/employee_advance.js create mode 100644 hrms/hr/doctype/employee_advance/employee_advance.json create mode 100644 hrms/hr/doctype/employee_advance/employee_advance.py create mode 100644 hrms/hr/doctype/employee_advance/employee_advance_dashboard.py create mode 100644 hrms/hr/doctype/employee_advance/test_employee_advance.py create mode 100644 hrms/hr/doctype/employee_attendance_tool/__init__.py create mode 100644 hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.css create mode 100644 hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.js create mode 100644 hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.json create mode 100644 hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.py create mode 100644 hrms/hr/doctype/employee_boarding_activity/__init__.py create mode 100644 hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.json create mode 100644 hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.py create mode 100644 hrms/hr/doctype/employee_checkin/__init__.py create mode 100644 hrms/hr/doctype/employee_checkin/employee_checkin.js create mode 100644 hrms/hr/doctype/employee_checkin/employee_checkin.json create mode 100644 hrms/hr/doctype/employee_checkin/employee_checkin.py create mode 100644 hrms/hr/doctype/employee_checkin/test_employee_checkin.py create mode 100644 hrms/hr/doctype/employee_grade/__init__.py create mode 100644 hrms/hr/doctype/employee_grade/employee_grade.js create mode 100644 hrms/hr/doctype/employee_grade/employee_grade.json create mode 100644 hrms/hr/doctype/employee_grade/employee_grade.py create mode 100644 hrms/hr/doctype/employee_grade/employee_grade_dashboard.py create mode 100644 hrms/hr/doctype/employee_grade/test_employee_grade.py create mode 100644 hrms/hr/doctype/employee_grievance/__init__.py create mode 100644 hrms/hr/doctype/employee_grievance/employee_grievance.js create mode 100644 hrms/hr/doctype/employee_grievance/employee_grievance.json create mode 100644 hrms/hr/doctype/employee_grievance/employee_grievance.py create mode 100644 hrms/hr/doctype/employee_grievance/employee_grievance_list.js create mode 100644 hrms/hr/doctype/employee_grievance/test_employee_grievance.py create mode 100644 hrms/hr/doctype/employee_health_insurance/__init__.py create mode 100644 hrms/hr/doctype/employee_health_insurance/employee_health_insurance.js create mode 100644 hrms/hr/doctype/employee_health_insurance/employee_health_insurance.json create mode 100644 hrms/hr/doctype/employee_health_insurance/employee_health_insurance.py create mode 100644 hrms/hr/doctype/employee_health_insurance/test_employee_health_insurance.py create mode 100644 hrms/hr/doctype/employee_loan/employee_loan.js create mode 100644 hrms/hr/doctype/employee_onboarding/__init__.py create mode 100644 hrms/hr/doctype/employee_onboarding/employee_onboarding.js create mode 100644 hrms/hr/doctype/employee_onboarding/employee_onboarding.json create mode 100644 hrms/hr/doctype/employee_onboarding/employee_onboarding.py create mode 100644 hrms/hr/doctype/employee_onboarding/employee_onboarding_list.js create mode 100644 hrms/hr/doctype/employee_onboarding/test_employee_onboarding.py create mode 100644 hrms/hr/doctype/employee_onboarding_template/__init__.py create mode 100644 hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.js create mode 100644 hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.json create mode 100644 hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.py create mode 100644 hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py create mode 100644 hrms/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py create mode 100644 hrms/hr/doctype/employee_promotion/__init__.py create mode 100644 hrms/hr/doctype/employee_promotion/employee_promotion.js create mode 100644 hrms/hr/doctype/employee_promotion/employee_promotion.json create mode 100644 hrms/hr/doctype/employee_promotion/employee_promotion.py create mode 100644 hrms/hr/doctype/employee_promotion/test_employee_promotion.py create mode 100644 hrms/hr/doctype/employee_property_history/__init__.py create mode 100644 hrms/hr/doctype/employee_property_history/employee_property_history.json create mode 100644 hrms/hr/doctype/employee_property_history/employee_property_history.py create mode 100644 hrms/hr/doctype/employee_referral/__init__.py create mode 100644 hrms/hr/doctype/employee_referral/employee_referral.js create mode 100644 hrms/hr/doctype/employee_referral/employee_referral.json create mode 100644 hrms/hr/doctype/employee_referral/employee_referral.py create mode 100644 hrms/hr/doctype/employee_referral/employee_referral_dashboard.py create mode 100644 hrms/hr/doctype/employee_referral/employee_referral_list.js create mode 100644 hrms/hr/doctype/employee_referral/test_employee_referral.py create mode 100644 hrms/hr/doctype/employee_separation/__init__.py create mode 100644 hrms/hr/doctype/employee_separation/employee_separation.js create mode 100644 hrms/hr/doctype/employee_separation/employee_separation.json create mode 100644 hrms/hr/doctype/employee_separation/employee_separation.py create mode 100644 hrms/hr/doctype/employee_separation/employee_separation_list.js create mode 100644 hrms/hr/doctype/employee_separation/test_employee_separation.py create mode 100644 hrms/hr/doctype/employee_separation_template/__init__.py create mode 100644 hrms/hr/doctype/employee_separation_template/employee_separation_template.js create mode 100644 hrms/hr/doctype/employee_separation_template/employee_separation_template.json create mode 100644 hrms/hr/doctype/employee_separation_template/employee_separation_template.py create mode 100644 hrms/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py create mode 100644 hrms/hr/doctype/employee_separation_template/test_employee_separation_template.py create mode 100644 hrms/hr/doctype/employee_skill/__init__.py create mode 100644 hrms/hr/doctype/employee_skill/employee_skill.json create mode 100644 hrms/hr/doctype/employee_skill/employee_skill.py create mode 100644 hrms/hr/doctype/employee_skill_map/__init__.py create mode 100644 hrms/hr/doctype/employee_skill_map/employee_skill_map.js create mode 100644 hrms/hr/doctype/employee_skill_map/employee_skill_map.json create mode 100644 hrms/hr/doctype/employee_skill_map/employee_skill_map.py create mode 100644 hrms/hr/doctype/employee_training/__init__.py create mode 100644 hrms/hr/doctype/employee_training/employee_training.json create mode 100644 hrms/hr/doctype/employee_training/employee_training.py create mode 100644 hrms/hr/doctype/employee_transfer/__init__.py create mode 100644 hrms/hr/doctype/employee_transfer/employee_transfer.js create mode 100644 hrms/hr/doctype/employee_transfer/employee_transfer.json create mode 100644 hrms/hr/doctype/employee_transfer/employee_transfer.py create mode 100644 hrms/hr/doctype/employee_transfer/test_employee_transfer.py create mode 100644 hrms/hr/doctype/employment_type/README.md create mode 100644 hrms/hr/doctype/employment_type/__init__.py create mode 100644 hrms/hr/doctype/employment_type/employment_type.json create mode 100644 hrms/hr/doctype/employment_type/employment_type.py create mode 100644 hrms/hr/doctype/employment_type/test_employment_type.py create mode 100644 hrms/hr/doctype/employment_type/test_records.json create mode 100644 hrms/hr/doctype/exit_interview/__init__.py create mode 100644 hrms/hr/doctype/exit_interview/exit_interview.js create mode 100644 hrms/hr/doctype/exit_interview/exit_interview.json create mode 100644 hrms/hr/doctype/exit_interview/exit_interview.py create mode 100644 hrms/hr/doctype/exit_interview/exit_interview_list.js create mode 100644 hrms/hr/doctype/exit_interview/exit_questionnaire_notification_template.html create mode 100644 hrms/hr/doctype/exit_interview/test_exit_interview.py create mode 100644 hrms/hr/doctype/expected_skill_set/__init__.py create mode 100644 hrms/hr/doctype/expected_skill_set/expected_skill_set.json create mode 100644 hrms/hr/doctype/expected_skill_set/expected_skill_set.py create mode 100644 hrms/hr/doctype/expense_claim/README.md create mode 100644 hrms/hr/doctype/expense_claim/__init__.py create mode 100644 hrms/hr/doctype/expense_claim/expense_claim.js create mode 100644 hrms/hr/doctype/expense_claim/expense_claim.json create mode 100644 hrms/hr/doctype/expense_claim/expense_claim.py create mode 100644 hrms/hr/doctype/expense_claim/expense_claim_dashboard.py create mode 100644 hrms/hr/doctype/expense_claim/expense_claim_list.js create mode 100644 hrms/hr/doctype/expense_claim/test_expense_claim.py create mode 100644 hrms/hr/doctype/expense_claim_account/__init__.py create mode 100644 hrms/hr/doctype/expense_claim_account/expense_claim_account.json create mode 100644 hrms/hr/doctype/expense_claim_account/expense_claim_account.py create mode 100644 hrms/hr/doctype/expense_claim_advance/__init__.py create mode 100644 hrms/hr/doctype/expense_claim_advance/expense_claim_advance.json create mode 100644 hrms/hr/doctype/expense_claim_advance/expense_claim_advance.py create mode 100644 hrms/hr/doctype/expense_claim_detail/README.md create mode 100644 hrms/hr/doctype/expense_claim_detail/__init__.py create mode 100644 hrms/hr/doctype/expense_claim_detail/expense_claim_detail.json create mode 100644 hrms/hr/doctype/expense_claim_detail/expense_claim_detail.py create mode 100644 hrms/hr/doctype/expense_claim_type/README.md create mode 100644 hrms/hr/doctype/expense_claim_type/__init__.py create mode 100644 hrms/hr/doctype/expense_claim_type/expense_claim_type.js create mode 100644 hrms/hr/doctype/expense_claim_type/expense_claim_type.json create mode 100644 hrms/hr/doctype/expense_claim_type/expense_claim_type.py create mode 100644 hrms/hr/doctype/expense_claim_type/test_expense_claim_type.py create mode 100644 hrms/hr/doctype/expense_taxes_and_charges/__init__.py create mode 100644 hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json create mode 100644 hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py create mode 100644 hrms/hr/doctype/full_and_final_asset/__init__.py create mode 100644 hrms/hr/doctype/full_and_final_asset/full_and_final_asset.js create mode 100644 hrms/hr/doctype/full_and_final_asset/full_and_final_asset.json create mode 100644 hrms/hr/doctype/full_and_final_asset/full_and_final_asset.py create mode 100644 hrms/hr/doctype/full_and_final_asset/test_full_and_final_asset.py create mode 100644 hrms/hr/doctype/full_and_final_outstanding_statement/__init__.py create mode 100644 hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json create mode 100644 hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py create mode 100644 hrms/hr/doctype/full_and_final_statement/__init__.py create mode 100644 hrms/hr/doctype/full_and_final_statement/full_and_final_statement.js create mode 100644 hrms/hr/doctype/full_and_final_statement/full_and_final_statement.json create mode 100644 hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py create mode 100644 hrms/hr/doctype/full_and_final_statement/full_and_final_statement_list.js create mode 100644 hrms/hr/doctype/full_and_final_statement/test_full_and_final_statement.py create mode 100644 hrms/hr/doctype/grievance_type/__init__.py create mode 100644 hrms/hr/doctype/grievance_type/grievance_type.js create mode 100644 hrms/hr/doctype/grievance_type/grievance_type.json create mode 100644 hrms/hr/doctype/grievance_type/grievance_type.py create mode 100644 hrms/hr/doctype/grievance_type/test_grievance_type.py create mode 100644 hrms/hr/doctype/hr_settings/__init__.py create mode 100644 hrms/hr/doctype/hr_settings/hr_settings.js create mode 100644 hrms/hr/doctype/hr_settings/hr_settings.json create mode 100644 hrms/hr/doctype/hr_settings/hr_settings.py create mode 100644 hrms/hr/doctype/hr_settings/test_hr_settings.py create mode 100644 hrms/hr/doctype/identification_document_type/__init__.py create mode 100644 hrms/hr/doctype/identification_document_type/identification_document_type.js create mode 100644 hrms/hr/doctype/identification_document_type/identification_document_type.json create mode 100644 hrms/hr/doctype/identification_document_type/identification_document_type.py create mode 100644 hrms/hr/doctype/identification_document_type/test_identification_document_type.py create mode 100644 hrms/hr/doctype/interest/__init__.py create mode 100644 hrms/hr/doctype/interest/interest.js create mode 100644 hrms/hr/doctype/interest/interest.json create mode 100644 hrms/hr/doctype/interest/interest.py create mode 100644 hrms/hr/doctype/interest/test_interest.py create mode 100644 hrms/hr/doctype/interview/__init__.py create mode 100644 hrms/hr/doctype/interview/interview.js create mode 100644 hrms/hr/doctype/interview/interview.json create mode 100644 hrms/hr/doctype/interview/interview.py create mode 100644 hrms/hr/doctype/interview/interview_calendar.js create mode 100644 hrms/hr/doctype/interview/interview_feedback_reminder_template.html create mode 100644 hrms/hr/doctype/interview/interview_list.js create mode 100644 hrms/hr/doctype/interview/interview_reminder_notification_template.html create mode 100644 hrms/hr/doctype/interview/test_interview.py create mode 100644 hrms/hr/doctype/interview_detail/__init__.py create mode 100644 hrms/hr/doctype/interview_detail/interview_detail.js create mode 100644 hrms/hr/doctype/interview_detail/interview_detail.json create mode 100644 hrms/hr/doctype/interview_detail/interview_detail.py create mode 100644 hrms/hr/doctype/interview_detail/test_interview_detail.py create mode 100644 hrms/hr/doctype/interview_feedback/__init__.py create mode 100644 hrms/hr/doctype/interview_feedback/interview_feedback.js create mode 100644 hrms/hr/doctype/interview_feedback/interview_feedback.json create mode 100644 hrms/hr/doctype/interview_feedback/interview_feedback.py create mode 100644 hrms/hr/doctype/interview_feedback/test_interview_feedback.py create mode 100644 hrms/hr/doctype/interview_round/__init__.py create mode 100644 hrms/hr/doctype/interview_round/interview_round.js create mode 100644 hrms/hr/doctype/interview_round/interview_round.json create mode 100644 hrms/hr/doctype/interview_round/interview_round.py create mode 100644 hrms/hr/doctype/interview_round/test_interview_round.py create mode 100644 hrms/hr/doctype/interview_type/__init__.py create mode 100644 hrms/hr/doctype/interview_type/interview_type.js create mode 100644 hrms/hr/doctype/interview_type/interview_type.json create mode 100644 hrms/hr/doctype/interview_type/interview_type.py create mode 100644 hrms/hr/doctype/interview_type/test_interview_type.py create mode 100644 hrms/hr/doctype/interviewer/__init__.py create mode 100644 hrms/hr/doctype/interviewer/interviewer.json create mode 100644 hrms/hr/doctype/interviewer/interviewer.py create mode 100644 hrms/hr/doctype/job_applicant/README.md create mode 100644 hrms/hr/doctype/job_applicant/__init__.py create mode 100644 hrms/hr/doctype/job_applicant/job_applicant.js create mode 100644 hrms/hr/doctype/job_applicant/job_applicant.json create mode 100644 hrms/hr/doctype/job_applicant/job_applicant.py create mode 100644 hrms/hr/doctype/job_applicant/job_applicant_dashboard.html create mode 100644 hrms/hr/doctype/job_applicant/job_applicant_dashboard.py create mode 100644 hrms/hr/doctype/job_applicant/job_applicant_list.js create mode 100644 hrms/hr/doctype/job_applicant/test_job_applicant.py create mode 100644 hrms/hr/doctype/job_applicant_source/__init__.py create mode 100644 hrms/hr/doctype/job_applicant_source/job_applicant_source.js create mode 100644 hrms/hr/doctype/job_applicant_source/job_applicant_source.json create mode 100644 hrms/hr/doctype/job_applicant_source/job_applicant_source.py create mode 100644 hrms/hr/doctype/job_applicant_source/test_job_applicant_source.py create mode 100644 hrms/hr/doctype/job_offer/__init__.py create mode 100644 hrms/hr/doctype/job_offer/job_offer.js create mode 100644 hrms/hr/doctype/job_offer/job_offer.json create mode 100644 hrms/hr/doctype/job_offer/job_offer.py create mode 100644 hrms/hr/doctype/job_offer/job_offer_list.js create mode 100644 hrms/hr/doctype/job_offer/test_job_offer.py create mode 100644 hrms/hr/doctype/job_offer_term/__init__.py create mode 100644 hrms/hr/doctype/job_offer_term/job_offer_term.json create mode 100644 hrms/hr/doctype/job_offer_term/job_offer_term.py create mode 100644 hrms/hr/doctype/job_opening/README.md create mode 100644 hrms/hr/doctype/job_opening/__init__.py create mode 100644 hrms/hr/doctype/job_opening/job_opening.js create mode 100644 hrms/hr/doctype/job_opening/job_opening.json create mode 100644 hrms/hr/doctype/job_opening/job_opening.py create mode 100644 hrms/hr/doctype/job_opening/job_opening_dashboard.py create mode 100644 hrms/hr/doctype/job_opening/templates/job_opening_row.html create mode 100644 hrms/hr/doctype/job_opening/test_job_opening.py create mode 100644 hrms/hr/doctype/leave_allocation/README.md create mode 100644 hrms/hr/doctype/leave_allocation/__init__.py create mode 100644 hrms/hr/doctype/leave_allocation/leave_allocation.js create mode 100644 hrms/hr/doctype/leave_allocation/leave_allocation.json create mode 100644 hrms/hr/doctype/leave_allocation/leave_allocation.py create mode 100644 hrms/hr/doctype/leave_allocation/leave_allocation_dashboard.py create mode 100644 hrms/hr/doctype/leave_allocation/leave_allocation_list.js create mode 100644 hrms/hr/doctype/leave_allocation/test_leave_allocation.py create mode 100644 hrms/hr/doctype/leave_allocation/test_records.json create mode 100644 hrms/hr/doctype/leave_application/README.md create mode 100644 hrms/hr/doctype/leave_application/__init__.py create mode 100644 hrms/hr/doctype/leave_application/leave_application.js create mode 100644 hrms/hr/doctype/leave_application/leave_application.json create mode 100644 hrms/hr/doctype/leave_application/leave_application.py create mode 100644 hrms/hr/doctype/leave_application/leave_application_calendar.js create mode 100644 hrms/hr/doctype/leave_application/leave_application_dashboard.html create mode 100644 hrms/hr/doctype/leave_application/leave_application_dashboard.py create mode 100644 hrms/hr/doctype/leave_application/leave_application_email_template.html create mode 100644 hrms/hr/doctype/leave_application/leave_application_list.js create mode 100644 hrms/hr/doctype/leave_application/test_leave_application.py create mode 100644 hrms/hr/doctype/leave_application/test_records.json create mode 100644 hrms/hr/doctype/leave_block_list/README.md create mode 100644 hrms/hr/doctype/leave_block_list/__init__.py create mode 100644 hrms/hr/doctype/leave_block_list/leave_block_list.js create mode 100644 hrms/hr/doctype/leave_block_list/leave_block_list.json create mode 100644 hrms/hr/doctype/leave_block_list/leave_block_list.py create mode 100644 hrms/hr/doctype/leave_block_list/leave_block_list_dashboard.py create mode 100644 hrms/hr/doctype/leave_block_list/test_leave_block_list.py create mode 100644 hrms/hr/doctype/leave_block_list/test_records.json create mode 100644 hrms/hr/doctype/leave_block_list_allow/README.md create mode 100644 hrms/hr/doctype/leave_block_list_allow/__init__.py create mode 100644 hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.json create mode 100644 hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.py create mode 100644 hrms/hr/doctype/leave_block_list_date/README.md create mode 100644 hrms/hr/doctype/leave_block_list_date/__init__.py create mode 100644 hrms/hr/doctype/leave_block_list_date/leave_block_list_date.json create mode 100644 hrms/hr/doctype/leave_block_list_date/leave_block_list_date.py create mode 100644 hrms/hr/doctype/leave_control_panel/README.md create mode 100644 hrms/hr/doctype/leave_control_panel/__init__.py create mode 100644 hrms/hr/doctype/leave_control_panel/leave_control_panel.js create mode 100644 hrms/hr/doctype/leave_control_panel/leave_control_panel.json create mode 100644 hrms/hr/doctype/leave_control_panel/leave_control_panel.py create mode 100644 hrms/hr/doctype/leave_control_panel/test_leave_control_panel.py create mode 100644 hrms/hr/doctype/leave_encashment/__init__.py create mode 100644 hrms/hr/doctype/leave_encashment/leave_encashment.js create mode 100644 hrms/hr/doctype/leave_encashment/leave_encashment.json create mode 100644 hrms/hr/doctype/leave_encashment/leave_encashment.py create mode 100644 hrms/hr/doctype/leave_encashment/test_leave_encashment.py create mode 100644 hrms/hr/doctype/leave_ledger_entry/__init__.py create mode 100644 hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.js create mode 100644 hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.json create mode 100644 hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py create mode 100644 hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js create mode 100644 hrms/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py create mode 100644 hrms/hr/doctype/leave_period/__init__.py create mode 100644 hrms/hr/doctype/leave_period/leave_period.js create mode 100644 hrms/hr/doctype/leave_period/leave_period.json create mode 100644 hrms/hr/doctype/leave_period/leave_period.py create mode 100644 hrms/hr/doctype/leave_period/leave_period_dashboard.py create mode 100644 hrms/hr/doctype/leave_period/test_leave_period.py create mode 100644 hrms/hr/doctype/leave_policy/__init__.py create mode 100644 hrms/hr/doctype/leave_policy/leave_policy.js create mode 100644 hrms/hr/doctype/leave_policy/leave_policy.json create mode 100644 hrms/hr/doctype/leave_policy/leave_policy.py create mode 100644 hrms/hr/doctype/leave_policy/leave_policy_dashboard.py create mode 100644 hrms/hr/doctype/leave_policy/test_leave_policy.py create mode 100644 hrms/hr/doctype/leave_policy_assignment/__init__.py create mode 100644 hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.js create mode 100644 hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.json create mode 100644 hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py create mode 100644 hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py create mode 100644 hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js create mode 100644 hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py create mode 100644 hrms/hr/doctype/leave_policy_detail/__init__.py create mode 100644 hrms/hr/doctype/leave_policy_detail/leave_policy_detail.js create mode 100644 hrms/hr/doctype/leave_policy_detail/leave_policy_detail.json create mode 100644 hrms/hr/doctype/leave_policy_detail/leave_policy_detail.py create mode 100644 hrms/hr/doctype/leave_policy_detail/test_leave_policy_detail.py create mode 100644 hrms/hr/doctype/leave_type/README.md create mode 100644 hrms/hr/doctype/leave_type/__init__.py create mode 100644 hrms/hr/doctype/leave_type/leave_type.js create mode 100644 hrms/hr/doctype/leave_type/leave_type.json create mode 100644 hrms/hr/doctype/leave_type/leave_type.py create mode 100644 hrms/hr/doctype/leave_type/leave_type_dashboard.py create mode 100644 hrms/hr/doctype/leave_type/test_leave_type.py create mode 100644 hrms/hr/doctype/leave_type/test_records.json create mode 100644 hrms/hr/doctype/offer_term/__init__.py create mode 100644 hrms/hr/doctype/offer_term/offer_term.js create mode 100644 hrms/hr/doctype/offer_term/offer_term.json create mode 100644 hrms/hr/doctype/offer_term/offer_term.py create mode 100644 hrms/hr/doctype/offer_term/test_offer_term.py create mode 100644 hrms/hr/doctype/purpose_of_travel/__init__.py create mode 100644 hrms/hr/doctype/purpose_of_travel/purpose_of_travel.js create mode 100644 hrms/hr/doctype/purpose_of_travel/purpose_of_travel.json create mode 100644 hrms/hr/doctype/purpose_of_travel/purpose_of_travel.py create mode 100644 hrms/hr/doctype/purpose_of_travel/test_purpose_of_travel.py create mode 100644 hrms/hr/doctype/shift_assignment/__init__.py create mode 100644 hrms/hr/doctype/shift_assignment/shift_assignment.js create mode 100644 hrms/hr/doctype/shift_assignment/shift_assignment.json create mode 100644 hrms/hr/doctype/shift_assignment/shift_assignment.py create mode 100644 hrms/hr/doctype/shift_assignment/shift_assignment_calendar.js create mode 100644 hrms/hr/doctype/shift_assignment/test_shift_assignment.py create mode 100644 hrms/hr/doctype/shift_request/__init__.py create mode 100644 hrms/hr/doctype/shift_request/shift_request.js create mode 100644 hrms/hr/doctype/shift_request/shift_request.json create mode 100644 hrms/hr/doctype/shift_request/shift_request.py create mode 100644 hrms/hr/doctype/shift_request/shift_request_dashboard.py create mode 100644 hrms/hr/doctype/shift_request/test_shift_request.py create mode 100644 hrms/hr/doctype/shift_type/__init__.py create mode 100644 hrms/hr/doctype/shift_type/shift_type.js create mode 100644 hrms/hr/doctype/shift_type/shift_type.json create mode 100644 hrms/hr/doctype/shift_type/shift_type.py create mode 100644 hrms/hr/doctype/shift_type/shift_type_dashboard.py create mode 100644 hrms/hr/doctype/shift_type/test_records.json create mode 100644 hrms/hr/doctype/shift_type/test_shift_type.py create mode 100644 hrms/hr/doctype/skill/__init__.py create mode 100644 hrms/hr/doctype/skill/skill.js create mode 100644 hrms/hr/doctype/skill/skill.json create mode 100644 hrms/hr/doctype/skill/skill.py create mode 100644 hrms/hr/doctype/skill_assessment/__init__.py create mode 100644 hrms/hr/doctype/skill_assessment/skill_assessment.json create mode 100644 hrms/hr/doctype/skill_assessment/skill_assessment.py create mode 100644 hrms/hr/doctype/staffing_plan/__init__.py create mode 100644 hrms/hr/doctype/staffing_plan/staffing_plan.js create mode 100644 hrms/hr/doctype/staffing_plan/staffing_plan.json create mode 100644 hrms/hr/doctype/staffing_plan/staffing_plan.py create mode 100644 hrms/hr/doctype/staffing_plan/staffing_plan_dashboard.py create mode 100644 hrms/hr/doctype/staffing_plan/test_staffing_plan.py create mode 100644 hrms/hr/doctype/staffing_plan_detail/__init__.py create mode 100644 hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.json create mode 100644 hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.py create mode 100644 hrms/hr/doctype/training_event/__init__.py create mode 100644 hrms/hr/doctype/training_event/test_training_event.py create mode 100644 hrms/hr/doctype/training_event/training_event.js create mode 100644 hrms/hr/doctype/training_event/training_event.json create mode 100644 hrms/hr/doctype/training_event/training_event.py create mode 100644 hrms/hr/doctype/training_event/training_event_calendar.js create mode 100644 hrms/hr/doctype/training_event/training_event_dashboard.py create mode 100644 hrms/hr/doctype/training_event_employee/__init__.py create mode 100644 hrms/hr/doctype/training_event_employee/training_event_employee.json create mode 100644 hrms/hr/doctype/training_event_employee/training_event_employee.py create mode 100644 hrms/hr/doctype/training_feedback/__init__.py create mode 100644 hrms/hr/doctype/training_feedback/test_training_feedback.py create mode 100644 hrms/hr/doctype/training_feedback/training_feedback.js create mode 100644 hrms/hr/doctype/training_feedback/training_feedback.json create mode 100644 hrms/hr/doctype/training_feedback/training_feedback.py create mode 100644 hrms/hr/doctype/training_program/__init__.py create mode 100644 hrms/hr/doctype/training_program/test_training_program.py create mode 100644 hrms/hr/doctype/training_program/training_program.js create mode 100644 hrms/hr/doctype/training_program/training_program.json create mode 100644 hrms/hr/doctype/training_program/training_program.py create mode 100644 hrms/hr/doctype/training_program/training_program_dashboard.py create mode 100644 hrms/hr/doctype/training_result/__init__.py create mode 100644 hrms/hr/doctype/training_result/test_training_result.py create mode 100644 hrms/hr/doctype/training_result/training_result.js create mode 100644 hrms/hr/doctype/training_result/training_result.json create mode 100644 hrms/hr/doctype/training_result/training_result.py create mode 100644 hrms/hr/doctype/training_result_employee/__init__.py create mode 100644 hrms/hr/doctype/training_result_employee/training_result_employee.json create mode 100644 hrms/hr/doctype/training_result_employee/training_result_employee.py create mode 100644 hrms/hr/doctype/travel_itinerary/__init__.py create mode 100644 hrms/hr/doctype/travel_itinerary/travel_itinerary.json create mode 100644 hrms/hr/doctype/travel_itinerary/travel_itinerary.py create mode 100644 hrms/hr/doctype/travel_request/__init__.py create mode 100644 hrms/hr/doctype/travel_request/test_travel_request.py create mode 100644 hrms/hr/doctype/travel_request/travel_request.js create mode 100644 hrms/hr/doctype/travel_request/travel_request.json create mode 100644 hrms/hr/doctype/travel_request/travel_request.py create mode 100644 hrms/hr/doctype/travel_request_costing/__init__.py create mode 100644 hrms/hr/doctype/travel_request_costing/travel_request_costing.json create mode 100644 hrms/hr/doctype/travel_request_costing/travel_request_costing.py create mode 100644 hrms/hr/doctype/upload_attendance/README.md create mode 100644 hrms/hr/doctype/upload_attendance/__init__.py create mode 100644 hrms/hr/doctype/upload_attendance/test_upload_attendance.py create mode 100644 hrms/hr/doctype/upload_attendance/upload_attendance.js create mode 100644 hrms/hr/doctype/upload_attendance/upload_attendance.json create mode 100644 hrms/hr/doctype/upload_attendance/upload_attendance.py create mode 100644 hrms/hr/doctype/vehicle_log/__init__.py create mode 100644 hrms/hr/doctype/vehicle_log/test_vehicle_log.py create mode 100644 hrms/hr/doctype/vehicle_log/vehicle_log.js create mode 100644 hrms/hr/doctype/vehicle_log/vehicle_log.json create mode 100644 hrms/hr/doctype/vehicle_log/vehicle_log.py create mode 100644 hrms/hr/doctype/vehicle_service/__init__.py create mode 100644 hrms/hr/doctype/vehicle_service/vehicle_service.json create mode 100644 hrms/hr/doctype/vehicle_service/vehicle_service.py create mode 100644 hrms/hr/employee_property_update.js create mode 100644 hrms/hr/hr_dashboard/attendance/attendance.json create mode 100644 hrms/hr/hr_dashboard/employee_lifecycle/employee_lifecycle.json create mode 100644 hrms/hr/hr_dashboard/expense_claims/expense_claims.json create mode 100644 hrms/hr/hr_dashboard/human_resource/human_resource.json create mode 100644 hrms/hr/hr_dashboard/recruitment/recruitment.json create mode 100644 hrms/hr/module_onboarding/human_resource/human_resource.json create mode 100644 hrms/hr/notification/__init__.py create mode 100644 hrms/hr/notification/exit_interview_scheduled/__init__.py create mode 100644 hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json create mode 100644 hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md create mode 100644 hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py create mode 100644 hrms/hr/notification/training_feedback/__init__.py create mode 100644 hrms/hr/notification/training_feedback/training_feedback.html create mode 100644 hrms/hr/notification/training_feedback/training_feedback.json create mode 100644 hrms/hr/notification/training_feedback/training_feedback.md create mode 100644 hrms/hr/notification/training_feedback/training_feedback.py create mode 100644 hrms/hr/notification/training_scheduled/__init__.py create mode 100644 hrms/hr/notification/training_scheduled/training_scheduled.html create mode 100644 hrms/hr/notification/training_scheduled/training_scheduled.json create mode 100644 hrms/hr/notification/training_scheduled/training_scheduled.md create mode 100644 hrms/hr/notification/training_scheduled/training_scheduled.py create mode 100644 hrms/hr/number_card/accepted_job_applicants/accepted_job_applicants.json create mode 100644 hrms/hr/number_card/applicant_to_hire_percentage/applicant_to_hire_percentage.json create mode 100644 hrms/hr/number_card/approved_claims_(this_month)/approved_claims_(this_month).json create mode 100644 hrms/hr/number_card/early_exit_(this_month)/early_exit_(this_month).json create mode 100644 hrms/hr/number_card/employee_exits_(this_year)/employee_exits_(this_year).json create mode 100644 hrms/hr/number_card/employees_joining_(next_quarter)/employees_joining_(next_quarter).json create mode 100644 hrms/hr/number_card/employees_relieving_(next_quarter)/employees_relieving_(next_quarter).json create mode 100644 hrms/hr/number_card/expense_claims_(this_month)/expense_claims_(this_month).json create mode 100644 hrms/hr/number_card/job_offer_acceptance_rate/job_offer_acceptance_rate.json create mode 100644 hrms/hr/number_card/job_offers_(this_month)/job_offers_(this_month).json create mode 100644 hrms/hr/number_card/job_openings/job_openings.json create mode 100644 hrms/hr/number_card/late_entry_(this_month)/late_entry_(this_month).json create mode 100644 hrms/hr/number_card/new_hires_(this_year)/new_hires_(this_year).json create mode 100644 hrms/hr/number_card/onboardings_(this_month)/onboardings_(this_month).json create mode 100644 hrms/hr/number_card/promotions_(this_month)/promotions_(this_month).json create mode 100644 hrms/hr/number_card/rejected_claims_(this_month)/rejected_claims_(this_month).json create mode 100644 hrms/hr/number_card/rejected_job_applicants/rejected_job_applicants.json create mode 100644 hrms/hr/number_card/separations_(this_month)/separations_(this_month).json create mode 100644 hrms/hr/number_card/total_absent_(this_month)/total_absent_(this_month).json create mode 100644 hrms/hr/number_card/total_applicants_(this_month)/total_applicants_(this_month).json create mode 100644 hrms/hr/number_card/total_employees/total_employees.json create mode 100644 hrms/hr/number_card/total_present_(this_month)/total_present_(this_month).json create mode 100644 hrms/hr/number_card/trainings_(this_month)/trainings_(this_month).json create mode 100644 hrms/hr/number_card/transfers_(this_month)/transfers_(this_month).json create mode 100644 hrms/hr/onboarding_step/create_department/create_department.json create mode 100644 hrms/hr/onboarding_step/create_designation/create_designation.json create mode 100644 hrms/hr/onboarding_step/create_employee/create_employee.json create mode 100644 hrms/hr/onboarding_step/create_holiday_list/create_holiday_list.json create mode 100644 hrms/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json create mode 100644 hrms/hr/onboarding_step/create_leave_application/create_leave_application.json create mode 100644 hrms/hr/onboarding_step/create_leave_type/create_leave_type.json create mode 100644 hrms/hr/onboarding_step/data_import/data_import.json create mode 100644 hrms/hr/onboarding_step/hr_settings/hr_settings.json create mode 100644 hrms/hr/page/__init__.py create mode 100644 hrms/hr/page/organizational_chart/__init__.py create mode 100644 hrms/hr/page/organizational_chart/organizational_chart.js create mode 100644 hrms/hr/page/organizational_chart/organizational_chart.json create mode 100644 hrms/hr/page/organizational_chart/organizational_chart.py create mode 100644 hrms/hr/page/team_updates/__init__.py create mode 100644 hrms/hr/page/team_updates/team_update_row.html create mode 100644 hrms/hr/page/team_updates/team_updates.css create mode 100644 hrms/hr/page/team_updates/team_updates.js create mode 100644 hrms/hr/page/team_updates/team_updates.json create mode 100644 hrms/hr/page/team_updates/team_updates.py create mode 100644 hrms/hr/print_format/__init__.py create mode 100644 hrms/hr/print_format/job_offer/__init__.py create mode 100644 hrms/hr/print_format/job_offer/job_offer.json create mode 100644 hrms/hr/print_format/standard_appointment_letter/__init__.py create mode 100644 hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.html create mode 100644 hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.json create mode 100644 hrms/hr/report/__init__.py create mode 100644 hrms/hr/report/daily_work_summary_replies/__init__.py create mode 100644 hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.js create mode 100644 hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.json create mode 100644 hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.py create mode 100644 hrms/hr/report/employee_advance_summary/__init__.py create mode 100644 hrms/hr/report/employee_advance_summary/employee_advance_summary.js create mode 100644 hrms/hr/report/employee_advance_summary/employee_advance_summary.json create mode 100644 hrms/hr/report/employee_advance_summary/employee_advance_summary.py create mode 100644 hrms/hr/report/employee_analytics/__init__.py create mode 100644 hrms/hr/report/employee_analytics/employee_analytics.js create mode 100644 hrms/hr/report/employee_analytics/employee_analytics.json create mode 100644 hrms/hr/report/employee_analytics/employee_analytics.py create mode 100644 hrms/hr/report/employee_birthday/__init__.py create mode 100644 hrms/hr/report/employee_birthday/employee_birthday.js create mode 100644 hrms/hr/report/employee_birthday/employee_birthday.json create mode 100644 hrms/hr/report/employee_birthday/employee_birthday.py create mode 100644 hrms/hr/report/employee_exits/__init__.py create mode 100644 hrms/hr/report/employee_exits/employee_exits.js create mode 100644 hrms/hr/report/employee_exits/employee_exits.json create mode 100644 hrms/hr/report/employee_exits/employee_exits.py create mode 100644 hrms/hr/report/employee_exits/test_employee_exits.py create mode 100644 hrms/hr/report/employee_hours_utilization_based_on_timesheet/__init__.py create mode 100644 hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.js create mode 100644 hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.json create mode 100644 hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py create mode 100644 hrms/hr/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py create mode 100644 hrms/hr/report/employee_information/__init__.py create mode 100644 hrms/hr/report/employee_information/employee_information.json create mode 100644 hrms/hr/report/employee_leave_balance/__init__.py create mode 100644 hrms/hr/report/employee_leave_balance/employee_leave_balance.js create mode 100644 hrms/hr/report/employee_leave_balance/employee_leave_balance.json create mode 100644 hrms/hr/report/employee_leave_balance/employee_leave_balance.py create mode 100644 hrms/hr/report/employee_leave_balance/test_employee_leave_balance.py create mode 100644 hrms/hr/report/employee_leave_balance_summary/__init__.py create mode 100644 hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js create mode 100644 hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json create mode 100644 hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py create mode 100644 hrms/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py create mode 100644 hrms/hr/report/employees_working_on_a_holiday/__init__.py create mode 100644 hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js create mode 100644 hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json create mode 100644 hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py create mode 100644 hrms/hr/report/monthly_attendance_sheet/__init__.py create mode 100644 hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js create mode 100644 hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json create mode 100644 hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py create mode 100644 hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py create mode 100644 hrms/hr/report/project_profitability/__init__.py create mode 100644 hrms/hr/report/project_profitability/project_profitability.js create mode 100644 hrms/hr/report/project_profitability/project_profitability.json create mode 100644 hrms/hr/report/project_profitability/project_profitability.py create mode 100644 hrms/hr/report/project_profitability/test_project_profitability.py create mode 100644 hrms/hr/report/recruitment_analytics/__init__.py create mode 100644 hrms/hr/report/recruitment_analytics/recruitment_analytics.js create mode 100644 hrms/hr/report/recruitment_analytics/recruitment_analytics.json create mode 100644 hrms/hr/report/recruitment_analytics/recruitment_analytics.py create mode 100644 hrms/hr/report/unpaid_expense_claim/__init__.py create mode 100644 hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.js create mode 100644 hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.json create mode 100644 hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.py create mode 100644 hrms/hr/report/vehicle_expenses/__init__.py create mode 100644 hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py create mode 100644 hrms/hr/report/vehicle_expenses/vehicle_expenses.js create mode 100644 hrms/hr/report/vehicle_expenses/vehicle_expenses.json create mode 100644 hrms/hr/report/vehicle_expenses/vehicle_expenses.py create mode 100644 hrms/hr/utils.py create mode 100644 hrms/hr/web_form/__init__.py create mode 100644 hrms/hr/web_form/job_application/__init__.py create mode 100644 hrms/hr/web_form/job_application/job_application.js create mode 100644 hrms/hr/web_form/job_application/job_application.json create mode 100644 hrms/hr/web_form/job_application/job_application.py create mode 100644 hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json create mode 100644 hrms/hr/workspace/expense_claims/expense_claims.json create mode 100644 hrms/hr/workspace/hr/hr.json create mode 100644 hrms/hr/workspace/leaves/leaves.json create mode 100644 hrms/hr/workspace/performance/performance.json create mode 100644 hrms/hr/workspace/recruitment/recruitment.json create mode 100644 hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json create mode 100644 hrms/hrms.png create mode 100644 hrms/modules.txt create mode 100644 hrms/overrides/company.py create mode 100644 hrms/overrides/dashboard_overrides.py create mode 100644 hrms/overrides/employee_master.py create mode 100644 hrms/overrides/employee_payment_entry.py create mode 100644 hrms/overrides/employee_project.py create mode 100644 hrms/overrides/employee_timesheet.py create mode 100644 hrms/patches.txt create mode 100644 hrms/patches/post_install/add_expense_claim_default_account.py create mode 100644 hrms/patches/post_install/create_country_fixtures.py create mode 100644 hrms/patches/post_install/delete_employee_transfer_property_doctype.py create mode 100644 hrms/patches/post_install/drop_column_max_days_allowed.py create mode 100644 hrms/patches/post_install/generate_leave_ledger_entries.py create mode 100644 hrms/patches/post_install/migrate_daily_work_summary_settings_to_daily_work_summary_group.py create mode 100644 hrms/patches/post_install/move_doctype_reports_and_notification_from_hr_to_payroll.py create mode 100644 hrms/patches/post_install/move_due_advance_amount_to_pending_amount.py create mode 100644 hrms/patches/post_install/move_leave_approvers_from_employee.py create mode 100644 hrms/patches/post_install/move_payroll_setting_separately_from_hr_settings.py create mode 100644 hrms/patches/post_install/move_tax_slabs_from_payroll_period_to_income_tax_slab.py create mode 100644 hrms/patches/post_install/remove_denied_leaves_from_leave_ledger.py create mode 100644 hrms/patches/post_install/remove_duplicate_leave_ledger_entries.py create mode 100644 hrms/patches/post_install/rename_additional_salary_component_additional_salary.py create mode 100644 hrms/patches/post_install/rename_depends_on_lwp.py create mode 100644 hrms/patches/post_install/rename_field_max_days_allowed.py create mode 100644 hrms/patches/post_install/rename_offer_letter_to_job_offer.py create mode 100644 hrms/patches/post_install/rename_stop_to_send_birthday_reminders.py create mode 100644 hrms/patches/post_install/set_company_in_leave_ledger_entry.py create mode 100644 hrms/patches/post_install/set_department_for_doctypes.py create mode 100644 hrms/patches/post_install/set_employee_preferred_emails.py create mode 100644 hrms/patches/post_install/set_job_offer_applicant_email.py create mode 100644 hrms/patches/post_install/set_payroll_cost_centers.py create mode 100644 hrms/patches/post_install/set_payroll_entry_status.py create mode 100644 hrms/patches/post_install/set_salary_details_submittable.py create mode 100644 hrms/patches/post_install/set_training_event_attendance.py create mode 100644 hrms/patches/post_install/update_employee_advance_status.py create mode 100644 hrms/patches/post_install/update_expense_claim_status_for_paid_advances.py create mode 100644 hrms/patches/post_install/update_reason_for_resignation_in_employee.py create mode 100644 hrms/patches/post_install/update_start_end_date_for_old_shift_assignment.py create mode 100644 hrms/patches/post_install/updates_for_multi_currency_payroll.py create mode 100644 hrms/patches/v1_0/rearrange_employee_fields.py create mode 100644 hrms/payroll/__init__.py create mode 100644 hrms/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json create mode 100644 hrms/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json create mode 100644 hrms/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json create mode 100644 hrms/payroll/data/salary_components.json create mode 100644 hrms/payroll/doctype/__init__.py create mode 100644 hrms/payroll/doctype/additional_salary/__init__.py create mode 100644 hrms/payroll/doctype/additional_salary/additional_salary.js create mode 100644 hrms/payroll/doctype/additional_salary/additional_salary.json create mode 100644 hrms/payroll/doctype/additional_salary/additional_salary.py create mode 100644 hrms/payroll/doctype/additional_salary/test_additional_salary.py create mode 100644 hrms/payroll/doctype/employee_benefit_application/__init__.py create mode 100644 hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.js create mode 100644 hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.json create mode 100644 hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.py create mode 100644 hrms/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py create mode 100644 hrms/payroll/doctype/employee_benefit_application_detail/__init__.py create mode 100644 hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json create mode 100644 hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py create mode 100644 hrms/payroll/doctype/employee_benefit_claim/__init__.py create mode 100644 hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js create mode 100644 hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json create mode 100644 hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py create mode 100644 hrms/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py create mode 100644 hrms/payroll/doctype/employee_cost_center/__init__.py create mode 100644 hrms/payroll/doctype/employee_cost_center/employee_cost_center.json create mode 100644 hrms/payroll/doctype/employee_cost_center/employee_cost_center.py create mode 100644 hrms/payroll/doctype/employee_incentive/__init__.py create mode 100644 hrms/payroll/doctype/employee_incentive/employee_incentive.js create mode 100644 hrms/payroll/doctype/employee_incentive/employee_incentive.json create mode 100644 hrms/payroll/doctype/employee_incentive/employee_incentive.py create mode 100644 hrms/payroll/doctype/employee_incentive/test_employee_incentive.py create mode 100644 hrms/payroll/doctype/employee_other_income/__init__.py create mode 100644 hrms/payroll/doctype/employee_other_income/employee_other_income.js create mode 100644 hrms/payroll/doctype/employee_other_income/employee_other_income.json create mode 100644 hrms/payroll/doctype/employee_other_income/employee_other_income.py create mode 100644 hrms/payroll/doctype/employee_other_income/test_employee_other_income.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_category/__init__.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js create mode 100644 hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json create mode 100644 hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration/__init__.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json create mode 100644 hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json create mode 100644 hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_sub_category/__init__.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js create mode 100644 hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json create mode 100644 hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py create mode 100644 hrms/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py create mode 100644 hrms/payroll/doctype/gratuity/__init__.py create mode 100644 hrms/payroll/doctype/gratuity/gratuity.js create mode 100644 hrms/payroll/doctype/gratuity/gratuity.json create mode 100644 hrms/payroll/doctype/gratuity/gratuity.py create mode 100644 hrms/payroll/doctype/gratuity/gratuity_dashboard.py create mode 100644 hrms/payroll/doctype/gratuity/gratuity_list.js create mode 100644 hrms/payroll/doctype/gratuity/test_gratuity.py create mode 100644 hrms/payroll/doctype/gratuity_applicable_component/__init__.py create mode 100644 hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json create mode 100644 hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py create mode 100644 hrms/payroll/doctype/gratuity_rule/__init__.py create mode 100644 hrms/payroll/doctype/gratuity_rule/gratuity_rule.js create mode 100644 hrms/payroll/doctype/gratuity_rule/gratuity_rule.json create mode 100644 hrms/payroll/doctype/gratuity_rule/gratuity_rule.py create mode 100644 hrms/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py create mode 100644 hrms/payroll/doctype/gratuity_rule/test_gratuity_rule.py create mode 100644 hrms/payroll/doctype/gratuity_rule_slab/__init__.py create mode 100644 hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json create mode 100644 hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py create mode 100644 hrms/payroll/doctype/income_tax_slab/__init__.py create mode 100644 hrms/payroll/doctype/income_tax_slab/income_tax_slab.js create mode 100644 hrms/payroll/doctype/income_tax_slab/income_tax_slab.json create mode 100644 hrms/payroll/doctype/income_tax_slab/income_tax_slab.py create mode 100644 hrms/payroll/doctype/income_tax_slab/test_income_tax_slab.py create mode 100644 hrms/payroll/doctype/income_tax_slab_other_charges/__init__.py create mode 100644 hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json create mode 100644 hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py create mode 100644 hrms/payroll/doctype/payroll_employee_detail/__init__.py create mode 100644 hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json create mode 100644 hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py create mode 100644 hrms/payroll/doctype/payroll_entry/__init__.py create mode 100644 hrms/payroll/doctype/payroll_entry/payroll_entry.js create mode 100644 hrms/payroll/doctype/payroll_entry/payroll_entry.json create mode 100644 hrms/payroll/doctype/payroll_entry/payroll_entry.py create mode 100644 hrms/payroll/doctype/payroll_entry/payroll_entry_dashboard.py create mode 100644 hrms/payroll/doctype/payroll_entry/payroll_entry_list.js create mode 100644 hrms/payroll/doctype/payroll_entry/test_payroll_entry.py create mode 100644 hrms/payroll/doctype/payroll_period/__init__.py create mode 100644 hrms/payroll/doctype/payroll_period/payroll_period.js create mode 100644 hrms/payroll/doctype/payroll_period/payroll_period.json create mode 100644 hrms/payroll/doctype/payroll_period/payroll_period.py create mode 100644 hrms/payroll/doctype/payroll_period/payroll_period_dashboard.py create mode 100644 hrms/payroll/doctype/payroll_period/test_payroll_period.py create mode 100644 hrms/payroll/doctype/payroll_period_date/__init__.py create mode 100644 hrms/payroll/doctype/payroll_period_date/payroll_period_date.json create mode 100644 hrms/payroll/doctype/payroll_period_date/payroll_period_date.py create mode 100644 hrms/payroll/doctype/payroll_settings/__init__.py create mode 100644 hrms/payroll/doctype/payroll_settings/payroll_settings.js create mode 100644 hrms/payroll/doctype/payroll_settings/payroll_settings.json create mode 100644 hrms/payroll/doctype/payroll_settings/payroll_settings.py create mode 100644 hrms/payroll/doctype/payroll_settings/test_payroll_settings.py create mode 100644 hrms/payroll/doctype/retention_bonus/__init__.py create mode 100644 hrms/payroll/doctype/retention_bonus/retention_bonus.js create mode 100644 hrms/payroll/doctype/retention_bonus/retention_bonus.json create mode 100644 hrms/payroll/doctype/retention_bonus/retention_bonus.py create mode 100644 hrms/payroll/doctype/retention_bonus/test_retention_bonus.py create mode 100644 hrms/payroll/doctype/salary_component/README.md create mode 100644 hrms/payroll/doctype/salary_component/__init__.py create mode 100644 hrms/payroll/doctype/salary_component/salary_component.js create mode 100644 hrms/payroll/doctype/salary_component/salary_component.json create mode 100644 hrms/payroll/doctype/salary_component/salary_component.py create mode 100644 hrms/payroll/doctype/salary_component/test_records.json create mode 100644 hrms/payroll/doctype/salary_component/test_salary_component.py create mode 100644 hrms/payroll/doctype/salary_component_account/__init__.py create mode 100644 hrms/payroll/doctype/salary_component_account/salary_component_account.json create mode 100644 hrms/payroll/doctype/salary_component_account/salary_component_account.py create mode 100644 hrms/payroll/doctype/salary_detail/__init__.py create mode 100644 hrms/payroll/doctype/salary_detail/salary_detail.json create mode 100644 hrms/payroll/doctype/salary_detail/salary_detail.py create mode 100644 hrms/payroll/doctype/salary_slip/README.md create mode 100644 hrms/payroll/doctype/salary_slip/__init__.py create mode 100644 hrms/payroll/doctype/salary_slip/salary_slip.js create mode 100644 hrms/payroll/doctype/salary_slip/salary_slip.json create mode 100644 hrms/payroll/doctype/salary_slip/salary_slip.py create mode 100644 hrms/payroll/doctype/salary_slip/salary_slip_list.js create mode 100644 hrms/payroll/doctype/salary_slip/test_salary_slip.py create mode 100644 hrms/payroll/doctype/salary_slip_leave/__init__.py create mode 100644 hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.json create mode 100644 hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.py create mode 100644 hrms/payroll/doctype/salary_slip_loan/__init__.py create mode 100644 hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.json create mode 100644 hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.py create mode 100644 hrms/payroll/doctype/salary_slip_timesheet/__init__.py create mode 100644 hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json create mode 100644 hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py create mode 100644 hrms/payroll/doctype/salary_structure/README.md create mode 100644 hrms/payroll/doctype/salary_structure/__init__.py create mode 100644 hrms/payroll/doctype/salary_structure/condition_and_formula_help.html create mode 100644 hrms/payroll/doctype/salary_structure/salary_structure.js create mode 100644 hrms/payroll/doctype/salary_structure/salary_structure.json create mode 100644 hrms/payroll/doctype/salary_structure/salary_structure.py create mode 100644 hrms/payroll/doctype/salary_structure/salary_structure_dashboard.py create mode 100644 hrms/payroll/doctype/salary_structure/test_salary_structure.py create mode 100644 hrms/payroll/doctype/salary_structure_assignment/__init__.py create mode 100644 hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js create mode 100644 hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json create mode 100644 hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py create mode 100644 hrms/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py create mode 100644 hrms/payroll/doctype/taxable_salary_slab/__init__.py create mode 100644 hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json create mode 100644 hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py create mode 100644 hrms/payroll/module_onboarding/payroll/payroll.json create mode 100644 hrms/payroll/notification/as create mode 100644 hrms/payroll/notification/retention_bonus/__init__.py create mode 100644 hrms/payroll/notification/retention_bonus/retention_bonus.json create mode 100644 hrms/payroll/notification/retention_bonus/retention_bonus.md create mode 100644 hrms/payroll/notification/retention_bonus/retention_bonus.py create mode 100644 hrms/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json create mode 100644 hrms/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json create mode 100644 hrms/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json create mode 100644 hrms/payroll/number_card/total_salary_structure/total_salary_structure.json create mode 100644 hrms/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json create mode 100644 hrms/payroll/onboarding_step/create_employee/create_employee.json create mode 100644 hrms/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json create mode 100644 hrms/payroll/onboarding_step/create_payroll_period/create_payroll_period.json create mode 100644 hrms/payroll/onboarding_step/create_salary_component/create_salary_component.json create mode 100644 hrms/payroll/onboarding_step/create_salary_slip/create_salary_slip.json create mode 100644 hrms/payroll/onboarding_step/create_salary_structure/create_salary_structure.json create mode 100644 hrms/payroll/onboarding_step/payroll_settings/payroll_settings.json create mode 100644 hrms/payroll/payroll_dashboard/payroll/payroll.json create mode 100644 hrms/payroll/print_format/__init__.py create mode 100644 hrms/payroll/print_format/salary_slip_based_on_timesheet/__init__.py create mode 100644 hrms/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json create mode 100644 hrms/payroll/print_format/salary_slip_standard/__init__.py create mode 100644 hrms/payroll/print_format/salary_slip_standard/salary_slip_standard.json create mode 100644 hrms/payroll/print_format/salary_slip_with_year_to_date/__init__.py create mode 100644 hrms/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json create mode 100644 hrms/payroll/report/__init__.py create mode 100644 hrms/payroll/report/bank_remittance/__init__.py create mode 100644 hrms/payroll/report/bank_remittance/bank_remittance.js create mode 100644 hrms/payroll/report/bank_remittance/bank_remittance.json create mode 100644 hrms/payroll/report/bank_remittance/bank_remittance.py create mode 100644 hrms/payroll/report/income_tax_computation/__init__.py create mode 100644 hrms/payroll/report/income_tax_computation/income_tax_computation.js create mode 100644 hrms/payroll/report/income_tax_computation/income_tax_computation.json create mode 100644 hrms/payroll/report/income_tax_computation/income_tax_computation.py create mode 100644 hrms/payroll/report/income_tax_computation/test_income_tax_computation.py create mode 100644 hrms/payroll/report/income_tax_deductions/__init__.py create mode 100644 hrms/payroll/report/income_tax_deductions/income_tax_deductions.js create mode 100644 hrms/payroll/report/income_tax_deductions/income_tax_deductions.json create mode 100644 hrms/payroll/report/income_tax_deductions/income_tax_deductions.py create mode 100644 hrms/payroll/report/professional_tax_deductions/__init__.py create mode 100644 hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.js create mode 100644 hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.json create mode 100644 hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.py create mode 100644 hrms/payroll/report/provident_fund_deductions/__init__.py create mode 100644 hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.js create mode 100644 hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.json create mode 100644 hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py create mode 100644 hrms/payroll/report/salary_payments_based_on_payment_mode/__init__.py create mode 100644 hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js create mode 100644 hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json create mode 100644 hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py create mode 100644 hrms/payroll/report/salary_payments_via_ecs/__init__.py create mode 100644 hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js create mode 100644 hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json create mode 100644 hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py create mode 100644 hrms/payroll/report/salary_register/__init__.py create mode 100644 hrms/payroll/report/salary_register/salary_register.html create mode 100644 hrms/payroll/report/salary_register/salary_register.js create mode 100644 hrms/payroll/report/salary_register/salary_register.json create mode 100644 hrms/payroll/report/salary_register/salary_register.py create mode 100644 hrms/payroll/workspace/payroll/payroll.json create mode 100644 hrms/payroll/workspace/salary_payout/salary_payout.json create mode 100644 hrms/payroll/workspace/tax_&_benefits/tax_&_benefits.json create mode 100644 hrms/public/.gitkeep create mode 100644 hrms/public/js/bank_transaction.js create mode 100644 hrms/public/js/company.js create mode 100644 hrms/public/js/delivery_trip.js create mode 100644 hrms/public/js/department.js create mode 100644 hrms/public/js/employee.js create mode 100644 hrms/public/js/hrms.bundle.js create mode 100644 hrms/public/js/journal_entry.js create mode 100644 hrms/public/js/payment_entry.js create mode 100644 hrms/public/js/salary_slip_deductions_report_filters.js create mode 100644 hrms/public/js/templates/employees_to_mark_attendance.html create mode 100644 hrms/public/js/timesheet.js create mode 100644 hrms/regional/india/data/salary_components.json create mode 100644 hrms/regional/india/setup.py create mode 100644 hrms/regional/india/utils.py create mode 100644 hrms/regional/united_arab_emirates/setup.py create mode 100644 hrms/setup.py create mode 100644 hrms/subscription_utils.py create mode 100644 hrms/templates/__init__.py create mode 100644 hrms/templates/emails/anniversary_reminder.html create mode 100644 hrms/templates/emails/birthday_reminder.html create mode 100644 hrms/templates/emails/daily_work_summary.html create mode 100644 hrms/templates/emails/daily_work_summary.txt create mode 100644 hrms/templates/emails/holiday_reminder.html create mode 100644 hrms/templates/emails/training_event.html create mode 100644 hrms/templates/generators/job_opening.html create mode 100644 hrms/templates/includes/salary_slip_log.html create mode 100644 hrms/templates/pages/__init__.py create mode 100644 hrms/tests/test_utils.py create mode 100644 hrms/utils.py create mode 100644 hrms/www/__init__.py create mode 100644 license.txt create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 sider.yml diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..1828084 --- /dev/null +++ b/.flake8 @@ -0,0 +1,37 @@ +[flake8] +ignore = + E121, + E126, + E127, + E128, + E203, + E225, + E226, + E231, + E241, + E251, + E261, + E265, + E302, + E303, + E305, + E402, + E501, + E741, + W291, + W292, + W293, + W391, + W503, + W504, + F403, + B007, + B950, + W191, + E124, # closing bracket, irritating while writing QB code + E131, # continuation line unaligned for hanging indent + E123, # closing bracket does not match indentation of opening bracket's line + E101, # ensured by use of black + +max-line-length = 200 +exclude=.github/helper/semgrep_rules \ No newline at end of file diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..0d1699c --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,15 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# sort and cleanup imports +4872c156974291f0c4c88f26033fef0b900ca995 + +# old black formatting commit (from erpnext) +76c895a6c659356151433715a1efe9337e348c11 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..094b52a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,7 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence. + +* @ruchamahabal \ No newline at end of file diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict new file mode 100644 index 0000000..64539b2 --- /dev/null +++ b/.github/helper/.flake8_strict @@ -0,0 +1,73 @@ +[flake8] +ignore = + B007, + B009, + B010, + B950, + E101, + E111, + E114, + E116, + E117, + E121, + E122, + E123, + E124, + E125, + E126, + E127, + E128, + E131, + E201, + E202, + E203, + E211, + E221, + E222, + E223, + E224, + E225, + E226, + E228, + E231, + E241, + E242, + E251, + E261, + E262, + E265, + E266, + E271, + E272, + E273, + E274, + E301, + E302, + E303, + E305, + E306, + E402, + E501, + E502, + E701, + E702, + E703, + E741, + F403, + W191, + W291, + W292, + W293, + W391, + W503, + W504, + E711, + E129, + F841, + E713, + E712, + B023 + + +max-line-length = 200 +exclude=.github/helper/semgrep_rules,test_*.py \ No newline at end of file diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py new file mode 100644 index 0000000..378983e --- /dev/null +++ b/.github/helper/documentation.py @@ -0,0 +1,54 @@ +import sys +import requests +from urllib.parse import urlparse + + +docs_repos = [ + "frappe_docs", + "erpnext_documentation", + "erpnext_com", + "frappe_io", +] + + +def uri_validator(x): + result = urlparse(x) + return all([result.scheme, result.netloc, result.path]) + +def docs_link_exists(body): + for line in body.splitlines(): + for word in line.split(): + if word.startswith('http') and uri_validator(word): + parsed_url = urlparse(word) + if parsed_url.netloc == "github.com": + parts = parsed_url.path.split('/') + if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos: + return True + elif parsed_url.netloc == "docs.erpnext.com": + return True + + +if __name__ == "__main__": + pr = sys.argv[1] + response = requests.get("https://api.github.com/repos/frappe/erpnext/pulls/{}".format(pr)) + + if response.ok: + payload = response.json() + title = (payload.get("title") or "").lower().strip() + head_sha = (payload.get("head") or {}).get("sha") + body = (payload.get("body") or "").lower() + + if (title.startswith("feat") + and head_sha + and "no-docs" not in body + and "backport" not in body + ): + if docs_link_exists(body): + print("Documentation Link Found. You're Awesome! 🎉") + + else: + print("Documentation Link Not Found! ⚠️") + sys.exit(1) + + else: + print("Skipping documentation checks... 🏃") diff --git a/.github/helper/install.sh b/.github/helper/install.sh new file mode 100644 index 0000000..8dd1154 --- /dev/null +++ b/.github/helper/install.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -e + +cd ~ || exit + +sudo apt-get -y install redis-server libcups2-dev -qq + +pip install frappe-bench + +git clone https://github.com/frappe/frappe --branch develop --depth 1 +bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench + +mkdir ~/frappe-bench/sites/test_site +cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/ + +mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'" +mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" + +mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'" +mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe" +mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'" + +mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'" +mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES" + +install_whktml() { + wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz + tar -xf /tmp/wkhtmltox.tar.xz -C /tmp + sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf + sudo chmod o+x /usr/local/bin/wkhtmltopdf +} +install_whktml & + +cd ~/frappe-bench || exit + +sed -i 's/watch:/# watch:/g' Procfile +sed -i 's/schedule:/# schedule:/g' Procfile +sed -i 's/socketio:/# socketio:/g' Procfile +sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile + +bench get-app payments +bench get-app https://github.com/frappe/erpnext --branch develop +bench setup requirements --dev + +bench start &> bench_run_logs.txt & +CI=Yes bench build --app frappe & +bench --site test_site reinstall --yes + +bench get-app hrms "${GITHUB_WORKSPACE}" +bench --site test_site install-app hrms +bench setup requirements --dev \ No newline at end of file diff --git a/.github/helper/site_config.json b/.github/helper/site_config.json new file mode 100644 index 0000000..52caf50 --- /dev/null +++ b/.github/helper/site_config.json @@ -0,0 +1,16 @@ +{ + "db_host": "127.0.0.1", + "db_port": 3306, + "db_name": "test_frappe", + "db_password": "test_frappe", + "auto_email_id": "test@example.com", + "mail_server": "smtp.example.com", + "mail_login": "test@example.com", + "mail_password": "test", + "admin_password": "admin", + "root_login": "root", + "root_password": "travis", + "host_name": "http://test_site:8000", + "install_apps": ["erpnext"], + "throttle_user_limit": 100 +} \ No newline at end of file diff --git a/.github/helper/translation.py b/.github/helper/translation.py new file mode 100644 index 0000000..07004f4 --- /dev/null +++ b/.github/helper/translation.py @@ -0,0 +1,60 @@ +import re +import sys + +errors_encounter = 0 +pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") +words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]") +start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}") +f_string_pattern = re.compile(r"_\(f[\"']") +starts_with_f_pattern = re.compile(r"_\(f") + +# skip first argument +files = sys.argv[1:] +files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))] + +for _file in files_to_scan: + with open(_file, 'r') as f: + print(f'Checking: {_file}') + file_lines = f.readlines() + for line_number, line in enumerate(file_lines, 1): + if 'frappe-lint: disable-translate' in line: + continue + + start_matches = start_pattern.search(line) + if start_matches: + starts_with_f = starts_with_f_pattern.search(line) + + if starts_with_f: + has_f_string = f_string_pattern.search(line) + if has_f_string: + errors_encounter += 1 + print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}') + continue + else: + continue + + match = pattern.search(line) + error_found = False + + if not match and line.endswith((',\n', '[\n')): + # concat remaining text to validate multiline pattern + line = "".join(file_lines[line_number - 1:]) + line = line[start_matches.start() + 1:] + match = pattern.match(line) + + if not match: + error_found = True + print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}') + + if not error_found and not words_pattern.search(line): + error_found = True + print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}') + + if error_found: + errors_encounter += 1 + +if errors_encounter > 0: + print('\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.') + sys.exit(1) +else: + print('\nGood To Go!') \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2a3a971 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,102 @@ +name: CI + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + schedule: + # Run everday at midnight UTC / 5:30 IST + - cron: "0 0 * * *" + +concurrency: + group: develop-${{ github.event.number }} + cancel-in-progress: true + +jobs: + tests: + runs-on: ubuntu-latest + timeout-minutes: 60 + + strategy: + fail-fast: false + + name: Server + + services: + mysql: + image: mariadb:10.3 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: YES + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Add to Hosts + run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install + run: | + bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + + + - name: Run Tests + run: cd ~/frappe-bench/ && bench --site test_site run-tests --app hrms --coverage + env: + TYPE: server + + - name: Upload coverage data + uses: codecov/codecov-action@v2 + with: + fail_ci_if_error: true + files: /home/runner/frappe-bench/sites/coverage.xml + verbose: true \ No newline at end of file diff --git a/.github/workflows/docs_checker.yml b/.github/workflows/docs_checker.yml new file mode 100644 index 0000000..26c44ea --- /dev/null +++ b/.github/workflows/docs_checker.yml @@ -0,0 +1,25 @@ +name: 'Documentation Required' +on: + pull_request: + types: [ opened, synchronize, reopened, edited ] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: 'Setup Environment' + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: 'Clone repo' + uses: actions/checkout@v2 + + - name: Validate Docs + env: + PR_NUMBER: ${{ github.event.number }} + run: | + pip install requests --quiet + python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER \ No newline at end of file diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..41ddcc6 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,29 @@ +name: Linters + +on: + pull_request: { } + +jobs: + + linters: + name: linters + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Python 3.10 + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Install and Run Pre-commit + uses: pre-commit/action@v2.0.3 + + - name: Download Semgrep rules + run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules + + - name: Download semgrep + run: pip install semgrep==0.97.0 + + - name: Run Semgrep rules + run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4b6dfb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +*.pyc +*.egg-info +*.swp +tags +hrms/docs/current +node_modules/ +dist/ +__pycache__/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..69585a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +exclude: 'node_modules|.git' +default_stages: [commit] +fail_fast: false + + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: trailing-whitespace + files: "hrms.*" + exclude: ".*json$|.*txt$|.*csv|.*md" + - id: check-yaml + - id: no-commit-to-branch + args: ['--branch', 'develop'] + - id: check-merge-conflict + - id: check-ast + + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + additional_dependencies: [ + 'flake8-bugbear', + ] + args: ['--config', '.github/helper/.flake8_strict'] + exclude: ".*setup.py$" + + - repo: https://github.com/adityahase/black + rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119 + hooks: + - id: black + additional_dependencies: ['click==8.0.4'] + + - repo: https://github.com/timothycrosley/isort + rev: 5.9.1 + hooks: + - id: isort + exclude: ".*setup.py$" + +ci: + autoupdate_schedule: weekly + skip: [] + submodules: false \ No newline at end of file diff --git a/.semgrepignore b/.semgrepignore new file mode 100644 index 0000000..5f06719 --- /dev/null +++ b/.semgrepignore @@ -0,0 +1 @@ +hrms/patches/post_install/ diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..15080df --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,18 @@ +include MANIFEST.in +include requirements.txt +include *.json +include *.md +include *.py +include *.txt +recursive-include hrms *.css +recursive-include hrms *.csv +recursive-include hrms *.html +recursive-include hrms *.ico +recursive-include hrms *.js +recursive-include hrms *.json +recursive-include hrms *.md +recursive-include hrms *.png +recursive-include hrms *.py +recursive-include hrms *.svg +recursive-include hrms *.txt +recursive-exclude hrms *.pyc \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ccea16 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# Frappe HR + +Open Source, modern, and easy-to-use HR and Payroll Software for all organizations. + +[![CI](https://github.com/frappe/hrms/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/frappe/hrms/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/frappe/hrms/branch/develop/graph/badge.svg?token=0TwvyUg3I5)](https://codecov.io/gh/frappe/hrms) + +## Introduction + +Frappe HR has everything you need to drive excellence within the company. It's a complete HRMS solution with over 13 different modules right from Employee Management, Onboarding, Leaves, to Payroll, Taxation, and more! + +![HRMS](hrms.png) + +## Key Features + +- Employee Management +- Employee Lifecycle +- Leave and Attendance +- Shift Management +- Expense Claims and Advances +- Hiring +- Performance Management +- Fleet Management +- Training +- Payroll +- Taxation +- Compensation +- Analytics + +## Installation + +1. [Install bench](https://github.com/frappe/bench). +2. [Install ERPNext](https://github.com/frappe/bench#installation). +3. Once ERPNext is installed, add the hrms app to your bench by running + + ```sh + $ bench get-app hrms + ``` +4. After that, you can install the hrms app on the required site by running + ```sh + $ bench --site sitename install-app hrms + ``` + +## Documentation + +Complete documentation for Frappe HR is available at https://docs.erpnext.com/docs/user/manual/en/human-resources + +## License + +GNU GPL V3. (See [license.txt](license.txt) for more information). + +The HR code is licensed as GNU General Public License (v3) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..7dfcec9 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,24 @@ +codecov: + require_ci_to_pass: yes + +coverage: + status: + project: + default: + target: auto + threshold: 0.5% + + patch: + default: + target: 85% + threshold: 0% + base: auto + branches: + - develop + if_ci_failed: ignore + only_pulls: true + +comment: + layout: "diff, files" + require_changes: true + diff --git a/hrms.png b/hrms.png new file mode 100644 index 0000000000000000000000000000000000000000..5564fe66a33ae1de9738c7cf8c7e0ea0cda21e22 GIT binary patch literal 1180791 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�ny~;|a@GZUFZ$HGov1xyM7tA)LYV9%?c5#1y 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%0AM3%@(K>2rys1@k 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

4o3h@wtR(21Jg@pI@Bfu%+nP8cL=`AVqa| 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

5J2@X?M2qbpNFW6=1Dg0ZZ4gs;q zQ|BJ;JUxc8TW{?5cSHW8qv1v1e?swpYB8L~bb=YXRz?bj_~2!R!7%tAfjZLg7l-|b zbQkA8UuivbET1*PvVQR_Y*&~~UcERll`xV7H@2so`pns{zm@Ey z<@<<6CC_;+-qfWQuJAWLz`OC@XTFO7drlMKYSQ;`=0c@jRpMOy_%O5+%(PTqE17db z%#tsOR^>^Rjk+s)aYETLT&1Hw%o_FRK`kY$Oc5yGZKS`G`AQ5a=FkrqApVa(t^@gp z+5h_e$&f$q7djGOGp@op0%H;`V}wAzubI`sx>j6=qSg-CNdRinfV%$MDm)&ga zF)Pn9Uv@MHU&C%-K&lCV+6tFoa4s=7EckhsThNIB-qHQmW`tpOsE0EBe|=P5_!s5@ z!K1Gg^h*hTyFD^$qGQU^^PtK07wBF1PSyy!wSQjn6mHmuYq>qp8=lkOa3af_w~^>k zS>C^{#eVti>uW!oTEB>cYm$0KS5;1JejC=?aXSOg>iZcwR&DEDCTG#>4JM+r9)#w1IEm zxJFqrqE9YaK##f|fpsYtq3_gDMWX{eLwp@GzXqab`w`AJzV8$N7WTX~9eLE4@GapuY=Tf#AM_P=aY~ z!<&W7-h|4pM*X7}@`iqX^z9I^_0ly$JZ~#cZq4{_PcZEnY5DCo?aM+KbY7})`Mt+N z$U=)MaA2i1aKOD=qq@afUi&3IfymZ-tv@}*LFj(u4se4^`VA@j;mlRC#%Oyp3(F$tH&Nq zdGzwXoMGhCu6-myl7ipIija=Jlw=cO+#F3HzVn)5YI zFcfS!er-v=`K59Kw)<3T7F^{8E9hBnG|aWr&uTfRa0@;_Zs^qF>RwG1eo##O zfJ;N<9N?)XLbMmt&I}=Jr$@3HwKSk}Y7+AYd%gVqWolr~8lo^Vho0CV=dY07ecC)n z7h%>{d$1X?{X_C;tNvvjHUR?IjZSt4J^SEUU;KS}e`581el7US%_Z^gbxH7CLBwks zI%3ov=x=Ts4mxycN+V#H5s;bS$(}bdq(A2fpOD#b=rR1ADd7-|;$aemncHBe#=(n67)LDAjo&tuqnm1H)?lPW zGN*+ztl2AVN3uIGgA!;s`r+}d0~MxyiLm^&k&cChidX=OaqQ&G2tzcwAkjIktj-}X zD2<6gg2ZmBya|Vsd}Quw{$95EmIO1rcRF6KHfpeQQwY z#8sT2U z$CKB*Js)k(g)<4NLtkQ_YrjKn(V^Zx&wlBE*Eg2o+)u=N>ylx!^n!>BfR63Iw40JK z@3n=cLK+%eNPb^F#y?$dbHdFjda=5EDzQ{F9$cL0geZk9SL0Wu}`tWuE5BndBu#j1SaXvXzK%Q5X2REA{8AKF--XMt1AG+^d!fBObYwxzyiXe}n} zRieAd^}I4aa)*`UpQEOv6dQVx4#v_UnFqSn^8@41-W-4Gh7}r$6Q91`Lz1-l4?z`@ zM~3$(d48Rkr&WpbBRt|N+YaI~^rh{y7ivbGpNEG7a-8tkWImZyzEXOngmiNPXmrl` z8PFN7r)9pR;4$#qh35o(jvM^+=G@;)1~&;x{<{VJxQ_0#JWhWvcK_hOND6?3vl>lw zLUc1Fam``)zArtyV zL$s(8$KdB^+c?ao;DpUi)YPKIVOzv~(>Hi0Ti4_6tLybc$aplLT(M7mupiwB@K~j& zw)>DKsOV*=X!90CX&6oLsi70FZBPpydVRx(`S`ajx2J@cz+66a>@v8*rox&cX_v;g zMvR&rpoiawOs|llMR)gitzkK1FP%^X>a@16OnjGEgc{pcQAYlI`mh zmL!DEeR`hiUPqu*RdY%?R^CX{)vQCoqU>Ws{F@}wo#;n^ZJDd8g(SIYlwe?tql^Is zp^Y6zsxA73m9%+GHZBD$80>P`BW;H zWF*Lte9QfI>y;W4=nu=u@obDfP{(RUk_&_u0z za1y;u0y@(Xo_t(;HmWu?t$nMNu|g>ubwNy09xMSvhq!*O!ZvFk-2bHcfy`|;pcRdBQ$~`uurQ63MY9G3_Y?|{Xo_lQj#h|`mjA3) z;09-4o-??LuF1XJsXv3HUTAL$WV!38I>Wy)YBZK&SS9KP=i zAU#U>KC=H=e~$J@Hv4O+Od1@bB%4d(Nj#SUnt)_>Md*}a$h`2PEmzv~7oMz;!$GWs z>i^3EDAe6vX8K7)mQ!#8_b?I^{gb!~TE!b?x1O5*d))+^Fp&F?#(0D#V()mw9h1SK z4qKwNFJA9SZqW9xpYYZV?|D!o80a(pSGR6^M8`yuo`$|w+j?R??Lj0bNIChQRsg?g zTS_0kaIkJI3#CIUtX`^9K>bhIVq5w0w{KFEBcB7EYDYLOnj>F$#IaH=q@Xf%R&@~Z zIZWF`mtSZnpW---1GjB{CVgt>!bPZU*!+@jo0C0}uRZFcRX{dzq_bKp_vFitTO}UG zVjn;oNHbE~H!^N43gZZ73f|mmt4k}I|Ih=b_F`zL%Sl(+V-+U6l}41L!6x+V+cXq`unfn^8=;Ak}V6-#6IIdQ9yS5pP<} z3yni`d)w7Yzk`Yz7DuE0qgv)NAV?#JNL+4`;trxUa3xnYMSqQf8q^7IAW?gl+@I*` zIyKy=IA?rJPDL<^L=NTqab{~pi_Lw4yC$Llo;Uhiv+O+8Fg2SQ`%=*oME)lmeBuaq zH0($OAEUiF)dW4l>pj~s!8#QHGIex!=y_95AP32a{=9{SMcih0uJKMmh!)x-D=3S<+xnTHFAH zR9URCIARTy>4g3;C9q%?r6Jay=9-xEcy6aTa++py%&4LUZ9%z@dAci`z8&@vzNkWM zr`Mljh+wY;Jb5r({M{cH@H=B5yY+AIJoz+uPsvXZf+dDYi?ploMBN!qMNPf(*6Te^WO%)<>pLo_anyVtkQd`nuJ2G3in5OmYXw#j$o>7*6=`sws~ z5E^nXvur%;!Zxk23EA)UxfU|xRLkWwOwoI6T(P%)md@*q;9Gq`gX5c>-L@IJ zYc2R~)6J`s=Co>i?X>!=7k`*|qIASMFfGNhB#ENNa_?iKbHX(H00GC9Pb-_bGo{WG zBI8!CpER4Q%DrlW2)*j?VM*Sca`kJ7uxzH zhh7ZUFLyn%X>;&o0(fi>-AV@c)ez8Ixx+4JH-2$I2cA6kPNrISJp} zzkOk)g9k|aSdR@>K=P}Nh?)QUf{RJU&*I%Ico6gdr;_R7Jup zIM42~I3M=pWZ8Amb{lrYPQ~9{>EkY$w4Krw>h!!>J8FM2zrNyts`zFLlXf#{GZ&vY zIVY*-6<>tW)F54VM~_FjBG1Y5^`Nluc~AtP4O6B#In@mHg>s)PmdDj0c)3})`P2~f z^d3wa;W*jDD23{QjwlOsJ6Y{?;z1YSAIgmwMXD^9U%;U_=i%n23wDKrESlzPT)dTU zE}Hn5^rFR?cs89IhE=aBzi%(1s z@a@2-IY_>DRHOauypNoBt&Nn0*O;Gk_n}%)a8@C-|SuAwQtDuKzo8+VP zwlP7ZT@hcZa*-Zbo0|UzrUxx%4?T0Z5|w#p>}c0Oq&LGTosB#?mINBJDa7!>ix}=@ zXxu#SpQCeb%Egxs;!@%a_)(#if6fIb*61ga=sY=ly)~!>jy`dpU&~O_zhQMv?9>W) z*`lLUk=E;(%8t_+N)V4J9G;Rf7|hNyGv#%P17Eo zb02WiLh}yTO9LXar|!)S9q)rwV7`?N`Ffx2{uZSRCxW(HRHg}zaOP`^p;t8yFVAF$ppoAW475E* z$v0RCJ<0eZjWqW~Ey|*)$40H6t2^^)XosJkBe*Y%znFau_4MK+k>;_*6vKe=vnoR` z<@I9Z8`9@!kIPGqFMmGM$JQu~StS|`H=bWJ{5!k<0QUJ2=X*EU4b5y11QbY3%3CyL zaFhcFPLus8EWOFDO6TWH zqc&t``juw6M;16m&%{A`oZv9KulTR#_W#pAa@?{-Tgf9n^OP|h)kEG|UID^?1n0;$2!N;;xe!`nXW*)nZut@%Hj8l~Al}2L&Ekqf ze~E}CFqWbGxB&nHG(MRwYj7{DEq)*KM31+!_5=E`ipTaR$L-K*qN(=VKi?0$KTQd{ z1Xu_mM|JX42csunY)qs6nU8d=5BHps>L{L2g`X%caQXU)hN&75chHV1QXN$C5}A<0 zrx5Y5^^dm*c9>5A_z!K&UAa$LF?#+GrUSTWzdYc-VZ6tR9 z`Hbv}0xn0$P~}Cv1z4eq(@I-}Gi?+}64ToyWG7Ns@Nc+G#>nX5TQgSk5D2g*(!J(v zD+~}+`k?C)A9556J%&8TozO4-;M)kp90LwgZf1_s)$WwaZ zBSU|a+Dc*qu;z3AXP?y~S`NHl4J|#2t)Fo7CQb0CzC^MRd`RI&cX+Jm;2kUktsJ9- z`q+fq5?u1>@lM7XP&utPL+OpW6~?MQTOA#1f76MNp7YBPCXV4Xs)J>bzmcRVhUOxm3iiN{T_0AUF<6{!6rMGVvXpy{HU@T}p|SzyHK$e8kvc(&xbrE3D~>o5#PIh~+ru95yrW@S4JL z?AQa7d3yT_6-M!|DHW2<7QP+?P)C`?N5`hD^#tg1SQar>-lM$-I+Ydf1{n#B0l z@cQANLh%*d>+%$CtI~^D{z!E?-gjYCxPPg2@RGwFP_Rca?>x6R$|OLKqg{-woQe+L zT(Yyt1nUQ{WU;nyeL6z7lhZlN@HxP1r1|K=?Jn*I`5Ze+T=@8u;2$~LANoGL>`(zK zDu7M+i#EU~_(7iaPh1@SQF=PAAWsw188+qiTp7?S$qmnY^lYY;J zEGuKnZ-fu4LIlCP)4fD;vTMS0{(>C=iVPRN0PB%7xi4O60b$*-O!=b2Ki8gV+^B#j9`joLRATEp=#{T>x}0@K2Pp@M-kNbO2>=@hqLNzR_N86&L8W{fX)@o|bTo**6+&v?4OuTdcy?MGFe07s`CtMOHt zpoAA8%081D3co^hv%DPu?O1J1RE~YD6**uxN45dd*^696pGrpDE?=bTob+W8P(2=C z_2FxOhHFRl$(hIE?hpkR_j!NA->ap_0H5FDbE%rCLLpRt^kbiOM;a4=9oVtD!B1{G z{x=v#Gww*fhviFCMolN3L`my6sMOxGGCidXandEY)9yy5m;IYPtpH=jR3-0A=U$zl zU-JAjQUn}#(J~T#TeZ<)(_wtsDg9Sz>C2X*no_t02L1&}{SXvgFTy z1#K83sGI&@iY1y1#J#J`Fdd4LVWxTs#0ajsh9YEDh?qI|cQilyzcjaal({u3!bLMx zga60R82r+e_CM7u%aiP_-|#e&nKKXs<=SQmbnGIxATu&cFtJFMG;k?0-6i!LdKI<$zTH45`KGa}h$3GPj1v~KK2mnhJ5 zMLnWde?Ax}SQr?=yTH_{PZU5$BG1X)5$zBkVi;&YJvmvJx{CelWwp{%w|o~C&Jxe( z)6ly-gdY{O++|!*i^&4_T+s8y#ZAQo@Q!>sRR{|FSb>a3PEUnh^QdM_QcHemFxl&a z664L>7CVBID&W4-ZjDDH-hj6rJkfoB?elhFZLm?{b^j|rs}PUoxpPv4A_Rtz=tAQI+2V-v2@)$H+jh*5TWOC*!QH?%yyFk^$G0a3i@GctzCmuL zEMcP*7Qj6S9kIVWQ!dysm9Hl{JC1bT?_H7p;nLo1yPNb%jx-^Ga$z4HHih_`jn$|J zqu;UtMAo7s!Lgmpj`_qf_F}`qs1M6vF^VWtBm)!;E?4H_R}W?QxI6Vl!z*Z8G7te+ zhO1qF<5tlXe}mfPZDDy@>6h9bI^Ah?AxjX)ql!@~YAODC!M3v8RW<)3Ejq19?a(6; zTS-ik%9!%rafXW{Zl5fDl}ogDn^l;FFXiZ5KgL_?I9A=x?26y0m-G)= zw}#^`Be3}+L-=}M$JEE4;-#g>s89<6^u=a#X2kBoA{nUj4h?{X>wep9Bh`P z${zFFG%=X2Lfw=-3#2@h^yR(w{VOW>XEFh&NEsaers!SB^HJnnZ7s+{a^o8*7*q{Y zp9QUdgpw7~H=?`c3DSx^u*{3&&F4d;Fyaxe&O1kUX9p>PB=y2N12a02pKkrJoHYO8 zU;vo_%rPAk|NZg?L6WveaCETY+r<(HjikOVcS1g+o9<|kf!G+x1g5=ZW|#JCpE#!J z^g^=4IKX7c@evgeWl=8%Tq~BFmI|kLrAjM>1J!WjVF~G2)Bo9$4t77%E@EEjE`f+U z;wwbABpGJt?BEq^I&p+5#QG)}B3XRA?0+i}&U$JmzFQJF?U{yhoc3IHWpg)>Y?Z?; zQvO8RKF`YwpfR|w*rC~vbtjTz+*@2$IN-i)VO<sD zDSce`LA5D?RV|Z)LD^mVLEjS70C*rF!u}xk-)Cd(^(uIgXZwYCHRg4JD^tb#n*GOi zzXRqZsW*?^f>5h(`e6?uqeuD@?i^X#9~r7zv>ANSEC|ZLqeehAqxEojQKluXies69 zaqRH$YcPihSj{v2DXb@@JkIAW%S+VzFsodA6xoU+@+^aN1QS%~9{${Bi$l3=2xq z09Sk&AVAF-g0$mQq9F;8ti=6{KlRtII<^P|-TKj$R2^R!?f{QC5R~IImez2pzH`JI zKewx|-t;SRf+y;jhL$Y~jHvjC(r3hr+ici!W+^7AUrHf^LClg&I3wnKfx4@5=DO zq*k*iBk+XV<7*RqJ#;n(;0JWp06OC+P635xlZ*llIpEiDl6-}CG2$OicMkqwP-5+c z44K6XZob$Qe%dc&Q<_WVW7zpH;Blj8N9dcpy|bNthfP~V#Q*0|>9Z#=w$`?&f>7+M z`QMK$ZDKm@M4GKBJ6V{U{-wE{G)s0?MO4|-AW5=Hj;%9&4)Ys(v|UTZm;FZ#SuGeW z^SX!b*IH@2mhJOFf^ehD>Ql$?zS4WDekrqP%|~)wV9=pcNLJnzOMM)Nk3JF6|7@2| zFT0=M95tw(G=LHA;}zb#DS2;-3R;Kn3 z64{{E9+h1J4Xqk1fkYAi9beZMJq9DQSdSB~Uk1Lz{Slenpn8SNOoATwFJ}dZPvuzh z#BtXzPx|-JZ;R)&*Ucxzzj@+ft``+;S31`oN;w&bP0qf`=>_8$=;ix*y8ZPsT> zj&DSyEqwli) z8r64m@Hpj#8t%u1FS|I1{V0L*m0zuTYh|4sg)n9!2xvdaoPYM@t2ow+xx9K|n3JtlgB|MUU|p_dqcErpfjEi(jWKr?Zavr%)k+bIYmys;VM*BzeCj z4adqc`VNvMQZ>?>r6-?waA}f$BtBSZTO5H$*J~cjL}rb|Z$!eq6;f%98@K|=#*MaU zv-!)Om`dbCiHQ-(Yxt36(@b7L(4S1tQ)`{^C(fRa$wzv!aXbr2@g(f465E7MHGn0y zBEErK>#OC{Je(cJcPu0O-bONxAF){amGXB!mLkd&MO6D=0Qo65F2K#n8p+>g{sz3} z+S9Y6AmAkylV_wE(LSFn8?HzpVI++n(0<*orK;*8VD)MLnjewdaB005v|@{Dm)Pq6 zB}(oZl21q&Rx5h$M>8k>;ZfiW>zIG<^7oYdjdbj9ipsUVd2N{a04>CP|3&q>hxtcp zs-4e)fucX@nl=n;G2p&B$XI1Wbk~}1xw3e))_d1sO}c{7fFgf{{%hz6(`LaA&o`+o z#K*#FO*j+{_xwdAtHI-LNc(lX%zhlpV;sI5I}g@hR5igMgk$HUI}B@OFWKAwx2thR z{{uWUF=YJV#D+y4LJGVPw3bGYSjcXsG7W7s;z4ADhkFWbHEbF>!MK>xHdAlc=LZ2% z@F2_?=-bmCZ31{-fv4-ICD21dsaFfgCO!RS?9^icwnBP?y0kv{%~PJbQy)Euf{+?h z5tvJh2JDI35dygtuT<{IHxRP3APa2gSylzP6=(2paDg++GETN>D2~Y7wgDI z$o#i|o70&rZan|Vmw-+SpZ~6ltezZf96Ds9VX6!jw`^K5l>;Schs-vW%tV}wLC|@# z@c8;`d7XLAd+8#Fh|kCe`xe%giqtz*HioQ$`P@YkNBGSD#ETUph7-lt{IX~~s2v@q zys~u6YG^dmTi6Uwi;G{r+ZB$qN)8GP+pVq z3`6u`&IyWs<$TUEhS678N4-RYJI=1QqYScf#6(IARcyNPEU_eZ$Tv>ESJcl}vT@P5(CHZ=bJxD)l+C=EB*776M_nMuKncltsiH~sFWtC{-xac%cqK#kSDGg1_^(?W zry9bVY?f-EV(L8h7{FT*))RJ~dAi;zlYXhEGs5t5au}&U?Od;6CtpU8)`tF{yh)boKI7ZM?*wyXsWmw%dGX?RQTkm^4Rd& zkvpUkTS|j^{)0gLoqI^AZ_*@|d3$RW&`^CN1cJ!I<6CE61#} zp=!F35!BE4O-d1vo$At%ex1VedSpk8SH9iyKc<2hQVL=q>=wLEy_FIrMiGXu;^NKl zaCv416v?z)$br*5tw_GS&cEBHf|JtY8~Rj^JXg2F?f_E48K_}JxO|LuL`s@$ak)9D z5IA3_cFcpAF=r{)A1z1w(e<$5->}D@uT+jTr89P-;KpK1wS;H}EBFsldFp{9)`)5F zDlfiz)#z7xeISw@wHvto)GBX*PO5QJq1~zj{ypSGJwzhaUS=UH&n}F0b>C z3<_MPA4y{$f$iz9!#D#XD(Diu4dyaU492QvyMsbQpREI~^OA=z(CQ*nXC-X3*s+Zi zOlMP9#Yh+LU%t@RVO75aI$AwvUIcz09C)>cLhQeNc{%QkPw$Ka47OS@U?rgVvJc_h z!I0h*h1cyJO-*ybQ*gweopLT}R8YTp}Mp<`G{=17KkWY(4OOOa2{z@0e7F zu`?^}KKBZJn8rm7CU{Nf*hJB?baormy`JXO zh2kVbBP16DVTmQkr{azcecRDl2iPtU7_mji${yf&jZbK=LY_+UV34E@MxwYiw-_Lv zA!QCxLeMC+az&9r(*dU#IbD82AV6M`&(I3|)d94;@9PRyJ-07yq639^|LT&{go*Gx z7#+~Nz#boGKIx8<^A>TXitE*8N0=2unCYb)UxK`7(bX$LkCKipecpb?J_qwR6AThX z2WXFy{MtM`jFt$v9<71s5_mn1n!GMnsO`|WPgQpo4!+!>*z`h8BD2BVA!aeJm=8B7RhAoPpMQf*x==UU+$~jXt;OQtO58k}z z(0RRk^uQ2usy|Uc|N2{<-~xUMXV1}70V{DSaz!@`1v5;@FpG%clIi(K@ecYxqp=q*-K8Dh#RG*F@m_3!k|mHQHVmcH4DWkU*2dy~YGZ z_~fpiLBkx557TNNvZDU_L&=^oJKlS^*sMbMYg*>1vHho?(LJxcfoC<|T-QNUTGtWW z=|4Y(^r*fF4CZKRql--`=}uX^qgBqZt%1r*rVEyj7Z6j9*=Muw=Q+>0h%MhcnCZJS zdnlpiYy)?&IY?%F<4_W~cfcKd31_FPbt?6Z!8yMz3#_eeI9?vFEXyeDKHy2cpv$+B zQr}mBh{pw%7J!GROl3zI9`*d5aYI|f^$ zc!=85A#e-#nLTN51ayhcQ z*)y=g#H~E<0euT$POSaTn#pc*-9ajx#dg+D*+Rw5%yE|?MZZ{^SrPf3^_c?8Z5WUI z*U#8^C-wd?w>}q)RJy^)W^9oTy4Om)g%+ck^3`a2;me)G-}3ZFLeS}Bv? zaXN%j2i&D;{=5l5({OBp!S9!fW~UQ3X7rltz|w^^8elSo4U;``QNIpG6&cy#e9tqY z3uEx%hE+yTNSlAX$m^aP(rHH37YthRNL!lm+@UXd_hl0`F|y7Ga?M4gxH?NS6~K2AjSKrw=f%v zU(YDgALB4~zfUwK*u8^y@S1q;W1s(`jWR3m!mYE#&erCq>z2CkE}{&?k#VWr_F?~z z8B>cK!vi~s@DlHK$08~3_gl-0+DpT`f^y{Sk^bRDg1*sFOVRwE;|B)jn`a!S`@cxc zbz?fZ1ybwLihqu-;B99A+|@op_1EDm)$pqHx*i|z&;5_SOMEV4b>~nRVa_9zB<*sQ zEXlw`?yzHVt`gqE_NCzu%~g<1llulQUoo_}sG1fjYm60PKF^W?(izEkoPqP5|Z)z&X-(NK!S$`x`giY_OQ ziFKLuvz}h2MA^aIhqon@(v*0s?z%A*X1FA`s_2OAF*#c%JFnj3mHmVsZkG-0@l06o z5p&{w&X4{c9?WU3)D&=i@QUm;K6kG!2TuE(Cwcp;Lo0LXt)a1pK%;D}2l3pEz0!G% zW4zk6&!wm4EB@Hpl__)I*Lp_pYUm22qLx zeOAax6~(Oaw*8hi~#r*XPnU+Xf*Lp!V$TT30OPk1&6Y*WqO7HDpkp%~LJ ztMDiC$4^!$vg;h7W-+c2W9an5+vOuhdZNl?ttFyjVoGo3)0>dAD}H_w4}C^ICbx}~ zMB9YgV<2rIic(qcA~VoAiFXeW{W1uGKF|32d*%CoE#FNAR3}jk=GQP1KV}-I8#{|8 zoDT-szvmhsVsk)9e4(%TEfB{xs{YXu>l-PrX9}S}S?JXOA_j^9A@|ZlhvLvWY6;QN zPACWT_N;w3@%i4N&5U{2zWG$qnVX`xe>V~aLc~%&`X-OQ~lR_ER<}K zK>dfRr?Uuhu--jj}G$Iy`g9bc78f{veJV}YSCb~O7?Smjy4%}WOq znyNW;>ss9d!a&L1FExC5se(dpq_KhQ&p$M$_g34*4UW%fxg-f*Wj{Xs&%62{*rj+y z4=58&{^jL3@9Bl(y5J&v=Gb8&xDfWJ{*0!VfXRU8NK){O)dEP}^7<7K9ofK{)HY?iGcPs32vb4r;$= zWEdrDcbSxr`OE0bb2*#cGhEr5&Dy~;BP6(2Lj;?S zJbeR*Z&5s%5(n-vFE1!XQmC(yoyiUnSw$|6x`?2;Wq#G0idn)r4FWcbz$ z0eHAbD?#IXX|Ev|dje7SMITVV5eN7I-Yh`V>SOyq3C3e2LBt@4TGhw=rHw-9X}t+` zr@jEEak3^9?x_4wnY{ztK9nIz+dK{_*2&m`uqBN5waiS-jkbI?01!+2Hu)t&But5v@7y~873L{ByI%>*QTIaY8lkznmneUMW>jG#;xeApBRO!=E$HDi_qq{w=P|<*b`kj4Q2;Z4 ze7>@@1|Yo`d2`pwo7&V^prK(QTfC?|qFGReCwc$Mg(CZqray%tkDvD9RJzhw+5(`m zZiDraaX~a+BF$QED&or8Rc6g=n$N4ApTm|1>Xik_aF7d^pIeeI9a*#O2}*Ge+vg~- zu^O_^{ZX=C@PhGKOQpO1EsyG_cNJ|E@f<&lJ}ltvKKt18G+s$D5Ua**ykU(uRcPGE zByedc(a4_!C3Ocausy*>?QE$swKSc0iVyTKnn00 zhcowp^^GX>H9uFcenHQF8<$XUX*X#0ZsHM|M~i)7Y8(nnaIky&{s0y-gSUZxQjCir zobL3H_hhhG?-A}bikbl*JvoHJs{gG?f#j%1sQG%&@zeV&yO295?CE_O$mZATWA5Wb zF^D8-5=Up_ZdK~juV=@E2>UMAMv)@-chamh`p#lXnFH!IH}orvO4E+(xVepM#g%V- z@YpMIyJ+)L3H;WJpww?{oy&;t#6h|_XQo#v7;kpp?9)ElI0t;cLL1?SY2fo~`InHN z0fBX!Omdj30bm{4MC_Jqmcz?u^~YM+F3VdWE?8?mA;c|FHJEMP7rO)PyVDWkpa5=W4dOe3>FKG>PbjOG{I)!kRAAELJwiQ$!uSI&@CS}N?_QEA2UR1R9Q*{i zKbe{M;&7*EZx=>(1l0kvNZE{n$DvqlxWh%4w|1tNLL2X6d z-ze_x?oiy_3GUDqcWHs*UNpEDw-$F=oFc*9ix(*FZY>@l$(!eS-~XLEbLW1!pYDf~ z$z;|(=OmML_TK9!t5LcY0F6kRzmU5fs>AZnVBt&p>J0i>0szt3xE>R@wuBAbRFZGo zyU0HO&G3m^%BF{$sD@vI!3NJ=eJ`x`Nmk`WkjvSH)m5j?-VQDFl!t|kG1b%jrz@!C z^TiuEjlL)FHsF`Ird8-a-#a-6i{p3P1MG_~g&e!mt`pLG<2$)Zpw?a9n;BJhTOgG` z-k_cODo5H;ewM)7k&*R zp|`fGti4+$xA?E$PKS4JfbPeAH#$LswDd!s9~y1dMLoDWwrCa2H@@j5!-#u%it_3p z=zgRyCW0?_@4>`zov^jY(zL|;x?^AV{bpv2kKz74%1H;m>?zm0}G!u9JBLDf7 zFb;l1sjzHy!au+Ly}KN>1vn8?<*_P~+k-DN8Kv&Q??jb;I-T9BGQbZ4sw$GWQo)0N+6>BFmM=@lH6_Ug<+pu)PeZrUe2QK8d-=BpB;+D{d20J{dJg z!!XiH_~Xs>@bvtSY(ISbM*hD{mwGDaNL?FN!MCo)Qyz?! zsU#nbh2}(5s>CofQD@w>id1!z$73Bm{jwkY{37hD0Z4=tooCdTue817J-;_aADN;)lU*qQ0x2|2KQN*B2}O_>r<<25f;bgi>-Wr& zkY&{u5N<|MN=G0N@#T+C7znNzP0rv!QQx8YXyc|qdle%ttS?>Zr%1M%_=)71%4NDX zWB4{U3KnswK9np-m>BK;VhVWT;oKfLTV=a+bq-$fhLVn7bqo308GPonk~lC(ts7Zw z`aNSu3w(`2ORu$3Q`vV}FG7Av3^z%7m$g=`n@O_!$L!BE(g3&d>BIMIP+6+Qk9T5tW5Ru^M6>F zo_0Nx4)|6G{A)Abf4g6Gx|Fwadh{TT*|f{?%RT`V4$rY*(19uNChy11s`{niXJa7;-BP%;_LiJ4&~y-I+)x2PXs}6)175*;X};$pj6!$+}{M>P?BTm-2OiNmYIk`V8MS=~m70FNRl99(39HSw=fzMr#LF8aO|Wp|wD{WR9TEiP zWG06i0Ih}5xiW{?E8c)y{%lezQ(Hlv6Yt1jV3<~{ROH>kgx$A-s##gEIAj_KGYJg7 z6W_G0*rD=g5?k>%ZkZE7opf0HL+7ff2hv54jZG!;Pz*;44ED{acdK7`Lk}dH^d4JR zm#LLWV)VMOrrg(F8(bK{vW+35muw{T8Ea|>#&N~x>Ge9h4m}vmq`U#$$uG*S(Jri5 zbdEAblMy3flk9OmDxO*_TY=+SRjB%%OR{j}F>KMolMMwtrkacKaqObbu?2S#7_5I6 z*ikB$4qianHn+FtVZxX6H2krO7^ud%Ve<3gIL)>jrWYQeTo~xyab(_WWM&N#t|xB+ ziIhJDZsTnLTGsid;S?aW8XGEnA%(-eXSRa<^bh{3?OZL2GO;b2+ctDbMbDm9_4lm^ zMChm#YH#C1PffYr=>OpxJF>QZ7FXFXnM@hRPzhCv{EQ%)Of49}f-}LfDPgFUy(kPY zY{A);{wdvDVS7~{?#-&R&2`2ZYdSvnMe8bE&=_-mQY~qg$mdg#>B(G#N<*R)^+wO@ zX%SStQyI^)M^ZVoe!xynjo%6gVS*g|ob|91(wQxyuY$bvq$=*qjZP}-`U@wurz~Z8 zi~yc)UWq7ewUVZP?%LVEePknwz5*qdk%`yVaM6``LuWK?{(k(Htw14p874(B_Du?r zcS!L4M!+vNwKq2EEP~o)v|LRp;J3UZ%C(MV4j4!I!kLv9*ot12bGG31oy#Fjst{z%8+s%K1 zAmSEfvbpErK1Npe%c?-=X#9=G zwXlHOJs6hVjbb`&Bm+b&E$sVnof*W%^xrhRe=JH3zvmdZ`m@1)52hX)VPoNuXIVbJ zHoIA%t4BGzH6mEMywi&r=COP|ht~ALT-4&d2yKUQ{OR@ZGuDh{fM?*E&n}aiYMQqO zvsPi%$L@qFUhc}}vcSF7#5fl<%o7)+`&e<{JZ0VQ^A5c)_02dO3*!pjV9V2ONlyBG zuNbsjE$^^K;)gd@Zl6M#G%6=&MN^2hn<`v8LBTG(^r&rf!u2;D`nkQD5GhNam47)m zw3+uM%nr>KRg+_VCwL#kgb+b`IQ~?nVGh0Y2Hl~|a_q(W8(vm!JPTx@d_MC{!-K=6 zWwsV?k@@9EL0!EX?c~R&&+s-4Z~_e90`O%DCKd8@;q?5s4ByF>i&icudo-2VvM_)D zBl9knJrW^fg>x?&XygR9$zGrf|L{S?J`oS=!b2ZGYvFWZJyNnwU4r3{OJg$%l$WWd z&U7fo=^dgVH)f;2jdfZAJ%UE}!iN)OY88HYD3>TF@MiO8Yq@C&EmHdGr9ZxKUX49H zhF$azA2o&RL(6_&*`^g^6If)OCduy&6WK=d@*HLG94RSgS|LJhWrx;c+|>HfZ6}bT zp|oy>GnPH-4UGq$*aEiV6y>t}hBL)Fb1(!Z<)Zr2{1=?`K@#(vi;pgn<%gLfHdpVS z1vV9iO-rV&7F5xZF#LoTFoXz^R|=Tk^>+@hjh-hut0CE`QN!2nsR|}4zd~>@t?~53 zITm@)L;#f|4om__yAv!Zqo`ncfQk!#m4?--3;HCm);lg<-DW<#YtA)1%SnNSfLb=u zSiUwt>G{;!9i3>DpoWfst2ToX$7?X@s6}WYJavIS`GkI$GN+m4lnD)PGs-j;!c*Iy zFeeyksA1R{AATS}bN7_A0KE!*{+A+)2^hV;k<v}yKRj!>Tao~n-a`>s-gh7qK|W+6p~2@k@6hvC zAsMVOx)N0T4$bw4^mT$a>y-+J)u-E;M6V@ke{#Ld=9>Ss5Bir2QR9{r&5?J65qVHXJ`7-zXW+hg>X-hkE4HZkd$um zxV;W&Onm5)x(hE4Rm& z+VC^pR;3X!3V>qR4!)1;DV>3d`G4Q4QV?Xu4yGxyczLNz)F02T=Y67WIM!um1=xDv zYLCB5D~bDk`^NgS`z$?a$>lTg&h}#s@Dmj<_6Z&OC7A40>0n5%I1N8`b##2SF zF@$sgXgCaK?izpSP_Y5M2`TzELYRjp={Q5U>;h*b6g^!T|B=>?TN}qiTQJsj z83&Vsh}1{9NrvLci$J7>&m-j?X@c&LMA#F8C*U6R5$i-XL@eWIrd&T^NRd$sbNySX zUR9x?7~5ULTMJD(Z1Zo9r?#WPM<8WND~qaIG53X2_(8iYjRsO$ohQY*QZ6R`iyV%a z-^ICbiLV@zig98@+i6HznnZ%Vg21Gd)A5+k?2;{e@r~{v950f3XV5;jBT!ra&dOH% zV11=wacafAabai2)2`Hvn1Vc{&Q&j&jBx=p7XVRYd-5s_mpkSB*Sbd_XPrU%EP>bU zYDw4N>e+NG&wQYttV`BR>XV_fmPUcdRWj=kcEK(evMl>f`hr|IQVnj$Y>!P{LK)AH zq)OYezig^<$9fte{9#cBgv!gs-+_(&=BF@?+kS*f4Ta3cPPaW?f$^gzhMYqV`RSSI zIT=We5PPlDZu5DkmW!y)<)l{`9C&l5{(P4~U$MWx-yf2^G0X7tWKW5UzcWEUhNhU; zhNU3asL=-(NpmG#QGdJbHmNK6%sP^oy7!QM(m9I8wLQtQI9yKEq>NE&q`6L@3O$Dp^A&@|AHACG}J*AacUv| z84?1`UM!UbrbXZHSM&mh@xEWvE3@2$>)TR0eDY3*!yh*u9P8Ep0YdP_#zrkUnBH#7 zeS0@_YadeSf+qC-dKX-?=57aBl8+yFtO8Ev_GrFF?qCTIB}5yY7SU5|&Eo}?TpU>S zqOe21Z%iXZA1$sBW&J8~Y{TmRK7P<_UVk0Kj5arquVJ1yetCcg&p;`Z(V~>-xZx+e zsDRHXxB1_`=8n*)*+mc`wdwi6&H1EAp<9r7ji});Sq&&FaJ4VWamNa*%Sg3hURY5C zxp3IE-KzpVz=6s1KzA5}3~Xn}C%Pt$*ASbxDZS)ALRRDN{7gOPrw!p9L1Zq9&T0%) z2_@`?ctd6nd@rCsU$+zb3X3-eYJh7a7Q$&sN>(DrK|HGxqC7zn>pqqOOK;#apeY-FRe< zu-kZ~BD_zv`-;RgmGI!|kerw)K}Jc&{*Y4w%AtvX*O546YWc*=|0#K4E|aGAx%v0g zsG>09qfkeHf2x=n&&=%%Z*B_r#Jt`du54E(Cg%Bg2XT!>w#6i&b~6gIqtt81K6DW} z)TL#3gb)tuR9UnB8`M=o;M<5uwRu~s9jDEvrbIcHgzZAgI(4#Du4+YOhBoX^IYeiu z=l6uecowBe8l~t==K-iqQ@8=Vh~ zzBv^c1x)%#>a47+fCf80K%`FB-7STH+^N?+#bGwdP1Yx3D3S9!H8J0cAo;Bl zCv*>q3Q9=P`kJ+DBkczqgS0h=mw?qY)X>wOUk!NW znZr0vgq^m|!0Xm&PF)fJ;ctxUIt#Cj9dAAypv1zFzApM+53?{nBxSMDjBX+%6?i^njMRFHxqK9QI%6WQDZoJ&FLKshJGm{&Tb`;)jyF zSc%E=?@?%3+bw+RyH8-DINC?{Ka8nrpRFPFO@jh9!6kY*hda%-%?jkab=_g#YomF- zVyEi1IX0B>Wl^GFJ$Lj~(PI#HvVMg_rJ4)_5fQ_Q8Ac6@ho`8y>S(J~6SAMz80r;; zo5CybCb$z%d2TyYPl2%pX?emO9WilCly)>%T!yJQK6V1(;yd}7J&_QIkH_b*th6DJ zyL)2BIU5I39^FNvn}6JW;)my>k@LHe2CGIqkeVmeyvS{sQW!X%q2{h-GMp95U!_(y zDw)B4F8Om99tw^%A}|3poYkF>nDx!vwfi3+x5f@nuZ%41-uhiTGNPToutrc)2z9{x zT2zSh_o0z-6r3}8oYQ&A)nC(1VKV#azXd+f3bG--`S88LiqruhGc>uK*}6XZJf87J zcTiG;fRo`=EA{f6i#r?P9DFDuC?1Va3UVrN7yb|^02d$798 z-7KkDMbo`MeYeW4+{n5FW!`=6gX#=J#k}UR%!}9lG|;ePc|{6CYvlyu zAEFzZ1~BOzyiuyz^mAoMU%xSciV}=hle|v$ay@p!sJ?%HaqrpAq_cKG^O zb4E_v$WAc^PkyfmP3!V%xLGMSJap%APrz$zxlt+}*Hts%`rqdwn&237xHu&)O+rDv&4kNI$Ud>H=wwUt&+fSBPS5| zlL2)3rt$yR;Wg@gtL+LJQwI-y(@oYpO$d|!q2*zNLfz!H=@tpCwbNm|d|FZJ z55tQ!d6xD=X2V`D_^)_SzftolQf&Y>o60Li=B|?_WWOPPyOdx-9e2NVr?%hX&9xUp3y$r<+Jf=}#+v zLE_8mCQJ~o6FL1MGR;4mQWu!jh1DzP2-E7gEYgp*76Pfz<{htU`177?E#H!Vf88e< z3x0niX_7g^S;UH~b=9yaf|B6g3*`)fJ_2(JWkSA%-i*m%`ZGuoAnDBLEHY-uO}E&VjG#SZa*=UzPb^|ZEYq}vH6>!l_YR{>ZYs=y2KE%_EBZdL zIBfvVa?ARSPJ39@5gLm>50}OSVb7TfIApDLs+bIz8+(#()<-)j`m>Y-t0Fcir%=)# z`viJr^Um@OB@8;YpwC<5k91{|Hhvz!$R;pxObvBle{pBvOL|A1j_{>oRVE>{R`ge6 zgn*TeQzY2R*2X5`J`wM;TV$G{BAW5tT-_vw*O=(CtRw;Au)VC60?xb)_Wt50%$MDR z18K-(5kxxV<&wJAPM)(E7n96XY>4HsVmv+z5|qTQH+2mNXLL z^6ptAl4pu=<-S+ca6BVDud)soshKhFDbf67+~qwO=M*^pO|zQv-d8yyh-59(gK_jd zj(+BAvCBEro9A}gyfyyY9Jz|Vh!1&u^l0>?&F{EVLG0{yOy>=@k`^c^8Rxs!NSa#-}x-B`Z zrk5a`^))Zm>Sss;b}&_3Bt79nU!CpCt?4onz)~j1ln4hCP_o+;ktR&jKiw`r%=d~k zYu|Z;E3@rBpL4QOhMFH1+GV;gl(AX4x9gpm^*+$E|7}%a-Crs;{6}7lZlv%7o|(sh z9m7MuVhqf^nzVAVp@!(%Bd>(CElpQ} z{h^r!cBlwTSFf@?0spYx4v;=gFBZN2D=OEOg=9)@-rcRDpVHk7;2wlqaGPp*V%k#s zr}BQ>59kE-4iu}ru`su&tV$^=-k?)GhP#=haxkzK-))Wkh+G!9YBeqn!Vk=eN?p}k zP4CAdiX^QfpnSfo`GC>jI*wr~F((si$RWvo=j$N21VQ2DxWX9X-QnFuHVH2-xkqx* zmoK&6O2TbZ{nf*qfuKpjNMbGMz=X{MC2b)KTN^W?0M8=HX!1xHpgyEGJe^ttu^I@=(#LWnMo6pq4D z2TZjZ*E}`N++6AeUoZJW3!yo%OQ1FoVgwTjf$S*<-k%u+-cPTbX3L_d;`#6iHp;CK zx{&?)Fq*GBQ@>zj6@omPV;#jB3l$Z|WpGtvv+gCpLK_NgA>@_oZ^sv~3(LY$CtAgg zH&kn`{oMVzL)MJQpj1qqay3xJxY63mhw^7aD5hvSSDOj?iv9{$ee`fP-f)Vn66V5y?mz3t-l8-LWzb`o!ag z9H_Da@guZy0tDGd%Uv|cFlVUj$QVa8=-+X>stJG7OjihHp<30c{d!L3U@QwnvO3C} zrC;A01L=91%=R*GArQ+5pb64ObmdBx=1qM)Bo#;$`SRhFIkJI%0sJT>3qy5A_AbM3 zKm%gChUMB!Zy&z47ETd8K5&iEhnn$`Cy_l(^2l&!*|oxwaJNZeD)OTC0QDOL4haJ& zQhZoRR!)RYN21XT&cTx@v+-o+3)-@45Vcd=`+t1aOScVaX`t4dg~3IXAFoT0oanMb zIhXkVu#{)lRqd&_KQFf(pQ=zDp56LG9^L|B;}P$Q`rMNIhYLtNof)bqS+*w-pmfh0oW+3cvK>{)~wp8kt&;Ksl8`yvL zKcg7Z=_zsL#TfkH(<{W+4|Xs4O#j7C?*?B#U-6TCJS_{Ryy1YXqCV}UFUguWw~AW+ ziA{=)_;7-vaY+9lA__*~i*HYwo`9xGB0^0mWL$de5HN%$0N_``^ z6GZ%irvMDSa%|;b1`P3-9;{kyG&t z?izZF$F?BtJ`-|t0!4m)VR_$kO`q2BG5P80KlD>VG8L9SHPepHPW7%A(}6G@h9cxr z#IFjHZ#x=9a2yFw@1{+jUyb?(r>9IhqpKUE;T(N9O{S z@vYnX6)!S@0%=KU{{V6C|5CeS5E0>{EQnB9XW1h>RH`MW;>J9y7{Bx%4)s}ID2_{Va=mQprWL|(WPf*e(c~G zWtQ#g>N-7pC9xf4AE&ZgcA5FM^=>9&krJeP%y=DQf2(7nKsWShGOB5z>PgqcfAfzx zKabf-=KwwTE7Puco9uYmXHqBMj_ONW^Ar?g2&XmjJq!ohwFG^j zWj~&L?*D#_e^R^aSj;I-==!j{j{ZV*6bStdugLZNZ>S5LQA9qict8)zf=7qL6S2P~ zrfi^gN2vv#Z`X2+q~~J1W#`8KAwgQ*Lu&p*)q>NL?{@|Wbic$}9roBl2KBf5%nLqE9dY+(d}@_5 zh6&VvffhvUyIg5A*g{8uzvKwYnf?y{S^A_XIDREYluf{(%3s1bxkqjV;k4K^uPi9& zxw}{u(u_8UheOX=e+3pYj_ISi(A7!XN}DaXNK3s!P{HBn_osQn!v>@0M(%3UO0j0O z25RG}KJ$v|a${IggHi)XD5e(_#%&D6HH3@J)wmu!f{JN!C$42KMN!mvD@SCH^zFv7 zbInnb|F(+p!*_{BB(S_gYd1&sqBOin4|0rE^j2v@L_nF!J~VGd03En^Yg2O}(L%om zqFjGnF-GynV%kyIK@=AzByKJAujbLmoIZQx8bUIkhb*aRuPS?n6iJ{#r)BgZ-(+9@ z(N_Rra!}pVeSA4>E7&ua$7sSBgykxclB@m1n!_f34~BO$qWpYQ(-1N2862FCM=$en zBVMEf6BQzRWk@F{+5csf_ck4+n3RC6xFRM_DrkZr(&y%k2{JtkEVS$k$Z~636Bwh4 zJV>L2&wvLg6Nt9uQpCsWs8x+vh=n2CZ`J1bWz0UNRt(8a>2J|5vg(28LY)c*eI}$L zWtSzM?k=j*`Y4@4<$AU4#_?_YTo&qdGv@uTQ1+0rHrhK4(meW!_>ES^J((K%m&51E z3@|@@2&*BzCKAM>r%hjpto14=lmU(gdy+?f$fUFD>*>%a+av_XH~Swn)q^(Yr{m6l zWZ@ovHy}MGMlSn+saW5WvuMHSKtvO_SQfPUczmNFngjnUrHjz`r;2vJJMWQ!4d(@PK`p`c<`mBS<^6s3 z&?bU5+VhWH;7^^up2VD*$TFt_Dp0->3b&gyRK7m9>9?u>ay^t}TY?U-M-pL>4vVIL zyGQ_zRx+TN6aH#$i8n$3BTV~h@aCN7njYd4i1l-wxUm?3o$Wd7-VrbfM3`iY{!7@? zF7OqP_3AJm%j5)xfe9QCwt@F5ySu1e-Bc9_@OX>9&q zaaVl=1)Z?w|6j)em0wau{2{=c=Qs23-<&~%$e+-wKaxVEt1-w0l@!8DN<)vjR;kF| z@WJ@M{E_Iu&AJ3L5v)y*my7$${W5>V=lmPRz+7;SHQ6-}Oe&?!v-3Jip8uCEeGPT_ z)i=O`WkRqiB8us8=#irWw@>R1jW30wjrik_G?8KH`R#WCjF^$R-}vZN)};1!cWUx^ z#{TuiHD!xSLjXOtHJYLPT%0CahcB#LxIC#O}GqBaUt~nEQ;5{w#Hlp zom{846TBKx)FhEfj6Fl*+lZxJY=nxMMD^GQxEsAVo?it2_(Av=5!V$s2T=A#M9RN% zJbw5qQSJqwq=lSv)Ik=7$^5DWmV@vWhFcm?<6RgZ}RV3b)x^_YwjLkf>6Q+8add<_k(t49q)ym^npx7S| z3&S~@BHm#ekMvDixzrc}&Y;f4)yK0jS{@#?Ddr0+X@O8Il}ObPC<*-c!qJK+%{T3b zGs6B;Qb$LDlbF!XZyk_{@sjVEWAA+7loFJ(0eHPV;a6r5TIDd74v+Q!nb_fcGB|mq z%i5a@4vsy1N94YT*g+;ydC%3X-|fNc?&De=E06fInGq^!8LbFQFxc#$7K~Qw9^f%w z5F)GTMVt!k)V@y)vM5yzRP0*ic5&p~#MJ3yC!1Czov5?8z?GPzRqxe8sg4uFFRjd9 zfAhYmO@W@4M=1(v5ye>ybvqtPIW_9ha#hT`(LnPt*>ULS5)uXp$&3x@dK78l5`pa? z!Y$iDDYYzxOV!lvl+VqP@a3f(UUM|CJw+c)4Q<`2#Xs&avX+c~;b9Ld8FDkP`aQPU zoC{yd*zU7;fXZg1@i&>w7co&6TWOCu6?rWLN^lrovO z*yj2^=%6>N*LEN5l0IN?BSn$on8)sXQaVSo8%LI6zpvV8XJEi#pjDeR2E_eQn2;Tj z48QGbr&9{=NO1KK07gw^dUI=5r&^t%*0P>SB?|_Y@<#qw=bTc8a`7s^8mSGs|B^mwizKXr#f zuNTj6Gkow*`xlTmKC5M>ex8r~EI{#=eowx=$*aU5Iu*_t?R3|_V)^J802myxjmIWj z_H&+7H_D)18=*okPle8;E`KF*u0>6v{A{J;wus6?Jg22O*>Dlt;S@4NUf9tbPnw~8 zWI5N+jHRFmX3%{UUE4p!O|w|mBhoz(xE`?1wX{8i%dnY39#&OT<9xcj3fmaN@{784 z#*7E_V#s%V60vh-T~D$PCj`hW#l^20T?8F)-q|}0208?>n_?rr*(~R0I~=uBrA*CV ztg7S3FK4BQdy^zucSOQ)Rd}8zXG?z}e^g9-Ks?1v6w6}DkVhoWLXAop`Kdm3TXR_} zV2&s0%Fxi5%X4p{{!wQo3W~>oWtIg(4>sZOIBGRpMST73Ir!(Ot#==zCcYqi0%TBUefvm% zjA#8<*AuS0-4Z^pdxuh?{-4VRLTlkb*en~;{cA{AjH;G7BOfb3Lk5Tt3EX=-yB++k zb~clVU0Z|L;&|oO|BLdL)nQkm92^_Chdd7Ga1y3|KjyRkI~(%;{}~&7{OkG0@ikTA z_3^83QNqJD05%L&{eEBn58ob-pWnYgI)nQY-CIt!aK?T;g+Ce*|-~zh3AP@rPA>X7mCe9qi z0xnDZ3SXacnl|0!LD?9nr;$+h#n_F&yUtkcxUp_AgLs^*YZq!0g=Ce(WJ*j?GPi;i zkq}fTq2=ihHxqq%35r7j(S_4^q~xLVxLqi`or?$yOJOkfX))jZ1O5tcN?YVnE96XE z&xi!fTs0=kof)qCwvU)KGs*NaJRVS*=5l!h<`* zk%wO>tg|nn2bHz>-@%(+NJM!ceGk7IzypO%59~+YmB~GGL+;>e`H-i1?~SC3$~P$< zVgT?P0ueeJu&po^;N~Q1DpxitOV&Gb8=-z$)>wfvoSopxf&t0VlQ9m*A%stuPaalk9u%_8i8zNyEyrnjBd7RoyFCk|CtzP=sOROHL7E)Vk)$?oJ?6z0q z@Bg6F*~FzAKV63X2{Xo--WJKxmBG2&!f+j{u%ZU=V*@Wc9<>N{sL9UEz*O#R0phOp5spd`(qI!CtQ$8dIOB{*y&^IJq(AB zcB$HsR8c>?C#Z?qesvuU{K2z6p+YP%(zhnI2>q7ra#?WNSB-HSMVCAeewny6n%8t9 z2E_M{T4N&(*b3}6JhAfrTHphMy)&0HTE0ZUPUdaa25ttIf1u+aY7tPpCwVkTTHHeA z6~<$)+A}0)E{TiTCFqWELT0R<_@okj zA|pbP;%(8e0i~m4SPz(3L0Q|J*;1(@ET~#@7n*y22JgqWpmZ9SQK;}qXs)te%+NSy zPCMGUyVa{36d@lEM4M1*r*U%rlaBjqYSNvmi2)2Wq$hxfs-+_g6S7vpkVnVW~3Tm4{q%q4DrFzQ!j!IYb( z!3Z_BC2NSXUn;qs8KcmyMzm;@R0jWscQTl zM(}VY zmejP9U4q3{(};FCfdMHamPlzxdK++l^?$4=ZVlOkm_;>Y_5aUGIJQTRjw7~Gl@1C0>s}CC)e3fAQgB8v^*D)4%qC{b;4%+Vd*5GxzD$h9 zUeLfmU5&G~8Lh_{kJL^Xj-Rm4v5fW$7zHrPx+bV0FZBk-W<}OGldP_kyvJu1#X5_O zgtSPlDSY1?o1ReK>5Hr(s95bWBc)!Z8gz;Wl93$-!$(8vKZWuH!*0-s4VDa$T}@NjDl<4=af3lbr0r~o>e-+5UOPlyD}@~f_(`%`m`irEa3n@ZaB zxcv$8C!i2?NBs4@J%VDe12lxW9KcJ zuLXN_qxu%iIedK(w3}I;;Y4KakbDKKHHGCMbuMz#6@J|nW(%qg>TNp(du+;>q;PXx zh7KGLT_&)qCNK7=qN*FMdlDRvzvF5e8B*&0NlZP(ZWNbt9Pj%SMM8vcQbvf$Y!fny zNnj*vG;W?PHz_4H&XRYm%lqltJYC_bsqn4AGehnzC~rISV|O`r67o--18P#R^e96c zA#^t%IAb37mBZVt2v{k=#Rh>(>->6#mzH~jR)xTi5x@WLLF>xl3PKvzs{*g=-?xul4(e!5Au&ocjGXY; z$1eB_xz>Nvc|X;-i1vS6EMAqqz(`!_Di2Lex8-_YxThWKW>e9c;avg4oeBjm>pB1a zXR?LCXXoqrm)Kdfs#~dfIT5VRjacf~6tP~W%3eLl>2(LtXYj{!fjE^IOj3FftXUEK zn(9bN`sH<>DLg2uPF|XVV}tojjI>nSi>JL+_)Yt@jBI8=&hsN*zd?Ep3f?)v^s9li zs@(1pFw=m?DzWBWj@Q%Lm1eIGeCOf2iuB6 z46NdQ!&B&VBDoX#$b-x<3J=f4MKYkNJJKvoR}RV&66YH&fD%(SrmC>~89H$5LFkp@ z8=K-u=ozb-AWMeb63RcM(NBiu6^}?ua%%&LC~TE3cE+ibkRQP)9pkx&B!*(ra*cXL z)?n~BR%6iHO(?EV3h{hmc%yJ2Z?iqmgk~of{n~_@X3szT1LYm&U|88o0&;@q{clsr z#y&iXB>msI420%3^Sc)}(jgQ1kBxi(7?=6*ifGC&du*sQQv1(_ty}W^7|Kr!%d7Ip zx!LbdjB0Q=w=FR1q*y{v^oFMOzTO+19^B!E6jlk8 zr}K=XDvpXp1ULPZv28I3V@Rmh1cfrX(8#*0T=zS! z5TryeRNf3Sz`nU}`5`F_ACo%dd2Vn5fk};?3wPmNAVMqd$IVoccm0X7!wE6AIHRoR z;(hVWcG_n?Xi~;EVQh?PNLoip{*rd|L+<*}7Ev$%kg3kAFia)FQEvS~h)i)enK(ndB`s9}bMjCWa}7ysOd;3L z7AQp~o33@irC~DMNct2=KO-0JD=DDdJNX)}<7hkB2RD9-%0{l=P+ZjSNV6+?STt9N z63YMS^$EwV>^+J%Vx;Sg+2IXrWCbcDi>QOxYw|@ThSIU|SE9wg**Um~IK2JAV9UdK zb>ilJ)5{n3;jf^qFD!m^Xsq8fhA9Q^=_KrxUNX`68&YLYLg*CXyz1n=HTb`ykY1o;t>tT((MsujUkKlGm47C14%?ev=W8E<(ZnAl>o{OU2~+@`Dc)MA z(Wk7m{8vo#+rOpttsUPyKr#&Z=U;USo}3~66~IvvV%}T7&;Z_&aGwhSaw6^7_I`v- z?yQ^O>n7QGi%%xjxifmtk4^UT``$s=o_nW1g$HpbXe#)qHd}BWNjQfv%P4@)PoA$L zeJGIvWVH2+T-8a-KSl_cMp(p8lEl<#6l9gUCA_Q&S_{GAMkA+fh38gK6F`7(Vuvn0mW)S`wf}y1h=%pMV*{dy zEOj}tA@L}IQ2n*Lezsi&07kZE+GzaV?n}E~siLrnzRn6svif0n!lO}xPU-eOeqn_J zJzP8Sx9%%_R__`o*eAOHSz)n?wGE+K{dU091L$65i+R@DX>_lCVM z^G(3KDLbc%XOqF;Bl2RIixR9yr=GdkjgY~@dNDwhv6Y@I>#1+gd<6z$%2vUmzfs0vO= zA%IQk72|~QYXtMmztT;dA}#rZ%d+f*ccdOo+d7PIhrx;pW*%YVq2>=x$kQK!ER&gI z%Y2{XNST&OzG;plT~5;o<#mcu!D<~kAs@en^37Tad2q@1hs-R%NK$+gT40n-kjsv5^AXSpXmcD#wK`T^Y`& zK0K+OEiyFSO3!$HMg95=osUyxrn!eO>#WGbJ)bfaTY7;cC~%`FxM>D)DaP=6T0Yz* zV{#sj$bLh23F1nBx7`;8x7QHyf`4Lb_4{%5bMfy-UF+)W?R?zn4)_xK9`dsnIEtx{ zvUjPb1A}hKiCLMYDnnac#TxuHs=xKVLWWlQN<4i(nzPH{jvh!Sy)OD%8Us*c-gZv7 zX_$@Ci0rce3u%g{fi4tuQf9NKXK-v?xptMTB|P|M5ol;8WdBzwfroxZ^Ls?nZE^B7 zuc^l6L;o)W)P@gx*sN`2jE&j+J(1GkZ%xTb($Vy6uhmmQpvceRte7P8o$pek7zE_G zD1|nG#SqvXT%vmIfj9wh#)Py8QI#}qY3|nvWTVb7*j$wU8Qnu#CcR>iak)-n?^1lC z+e0Wvxq08Kxjgr2B)z4PVt~piT=DR>SPBOlLD4+pgsh@L1m)~bGE(?sJ&`z@X^*uu zBjN|BmRSv=%#rX3UNb9VC2#!h%^s(#Lb0`hB9R~)l1g98qD>F&RAzuqqn4*H$;UIQ56n$wb_=z>_P_%SpH!eX*US?&1PQ9hNJ!T zUC{B5!U!WhxxQBY7{gs!eYyQry-Hj znLKI_V}@ifOCA+(y*1&x;sv>02u(3?TTN~m-Qex);xf2lS|XuS!dxSLz^c1Cfq0*7 zY9R5AvXkQV=}Dt@2Gz|S-8~4VxFwPh zCfiQGaVS3{%$N5C%`_aCVTixg)o#mf(4P9Re94Zu@$h@xQdQXeApJlMr&!3c7Zy&; zqTr9s8NYo;ZfXnaDz0Ek8u?G{nOG)0-6$a?lO@acz^w=6Ld>2FUhC`W*U{$eUKg|i zNF2*kF3uybBhcE%G(JUEWRxt-w|~Rka_W?hwu74X6C=}aCH8t_qAcKf#QD@pbb09v zwfJ8xAk76%r7Q@o53pU>huYo^^xtte2VH5|8|bZ4T>zGcVq`|4GG9%2j4~sM=xe3f!DZk>|>nVP-tSP;E%jjA)-oFbn)BPMqlQgJ&pm zb6hBup>+_dAI7{r6*5=Q7m05diP&S2RQ|TR+n32YTE$|g1o0qbzif7qFO{_vL-4KL z-;rQ+boBIE1Vnwwv><%N52xLjz6VtlC9YrMQ`dx=U@DM&<1M%zQ11kYGWj^AW8!h0 zH}N3M8xRSL&i?^}`&^qafC1Q6UTb%hrI{-X{FEb3S9?16$03U$aUR^78qM~;uvByj z8;nrH98D3WFW%$n;*RuFFB&9ww%<6=`mWt~@>HQf)=7T)Ak#q8S^-9TV9)&;b4J!8 zk9Ol3RT~vQN5M}2I=Pf0M(*U_ASbQOb~1UZ$ln`$aXUTf>g0_*6tzrTMHvst)Fb+I z6m8c-j+WKu7VQ-jj>ns9IS#G9iOPtn+pF?3fXuw7mUlo1$sC@F^uhrQJnSbnDjDjK z4=;e#T||@cyKz?q&dSZCDpNtpmifD4`ouOqv<>Sw5f+}CehQKds1`DPTlAl*ZliJIEOo0-xXt?NZ7(@ELEUuR3oY1j6{Jb z#eoh{Txa+@DOb=>esl8SNslRVS8{Q5b9-~=Xyu9Qs^s;`OC`krPjm$%H{z7r zq_dMAKgqsN+_!?)%Y%QYBfqi(EnJH`W=1m1(&>R(fQ;m|M^i`Cf;DHUy@7-otTemHyzhb3Xo7EW98^0C>hg~s&{H< zT*k{{NUVe!28z8GH3GDH(kL#-65EBT!U)$ z=;Lq3hxyn;NL=$wk-;(MnQFq=NRIMiMD5{y3zFyygDBIj`?>{Iama}wNOycm?uQ@n z>J*GE8MR3hKb7!Kr%wry+>WFQSavI#WfswX`bMgT_24n0*4l@eo)z`?kkO|*v+U1?(Jl237#Y0!u&9`$T62ui z3((oc6|pE4m@Q3g=+YqkZthrMrDB$_{Z39&Z1V5-gCe$b@Qh%r8$v1fs?)?4>flA;sSHlzfz zsO5ocIg)KyNQNiy@zt=N$)j(aZuuA>s`k7?<_f~ywm>LB`WDhOpV7?t#%i<&3Xf=&-`8Xo&*+=l ziRr9hlV0onAyc*eL+Hb|Ku>?1Dq=O(wbC{xk3NedRZp-?==$Z~hai z0!42MPwhuWIT+9WKp|C4UT@VRu7Ku=BqtG0u0Vk2tDp8jf&M)seuc@k|Mu;HdMLWe z^&4qf*5-S8+cen5cwMr0Mc##lnMWb{rCNZAf_6)Eq8$u=mB@RpUM7)@*?bpR349!Y z>9Yz>!Ib4lWG+^59S#(xZf2rqjgasjrzMtMDwitW%4>jMok`2eZ2*XWU~_4r zpnx@05ebVsP}-zv;#$#yedH4pg}Lej1!+pb2aBbGPmxwX*JN0eEPCO{BvuH4x}7HL zoWbF@eQX8xZSX2=xMN{uB(ov%ChD2|w)!!bYUcYN_mi$MSHG`_PpJ$GyMm^J{q{f-mPHo>9t3mD6SUTw^hYg5X z>0Fd(LzKP@mua&5-wwIT;dOy52IIgzThUc-Ux`0BaWs`y0(hDO)RY(-HNb#xux)`{ zWp*b_FHn135S`vl4>7Q>lpnz*@2!Eo-%>XnfZ{Y^JT^^ z`AFWrqpjn=Yrs9Z_`lb1DdI@K3t>cL3vIW+UqY{0&-5G#2og={4WksRMwlIp2G8%L zK`CtmYO<@L?W~|nvNg>rw$5L{mnE8?+l$@?)6nQeY5|@S&h5=Y&uPhShH6q`pgh2f za!e?3j_XcyA)~=$Od+%2I;C#n%HE`#PT6vHa8b}Z{K+u4K1gL!ayJ;g0oYaRgY4s3 zgWD)}lUJ`c(z()@?FR^bKr3S9J4*Ji_~$SDFwZhNEM|kjlHYlme1Y3fRQ`cfSgTHP zhMS@hvVbPSAlmKox_bOt*B8Mxyt51>x%LK5(UhitE0vhNPs-Z;(D;%B<<6Ph<}!}0 zD<~QvL|kwa7MxEF`vOmQNNa8BK_=DY8ur~WeJf6aT@dV&iW7cEi9)IrQH`l$8dd8@ zF$d{;;84vrpcO0!&R?NTIHf#rzFmfi>xXovvRN$3eW^Gp-R7&6*Q6V!b}7fX?rYVy z^7?9V%%tILNiEP_TqAsFL3i}SHccmyRZc>x&1DI8i1@I5&u3$Y@Tzpt{h0Og|78I* zaUOM=Qs8Z5xj^6}LjP)OU?Yc=vO$=L#KVM(m!4s*8)P2FkLywFip%{c6&x!b!E!6= zL6=nT+A!oiocO|1j5tpH{{BvYVLZgu?GH0QY=BI@aEucoOdzyD{U>FlO}pu24-NX9 za2qmJ7-NmS{qIce;v=FN{@d9;$I;2FDuy;oZ7unne(Cn7s`tkOUAG{g0`00t^rdQN z7Ly}0bOFRFiyj++D{WD+htnQ~sxdM$a%Ud_$B4m-CE_?uA1o~{sB>#g4S^s}+v35F zR$eu=LVd+)mTC5Pc9A_NM*4{YGT};dEpKz$U~lGAf1S=qM4TUoVPO z_h0-;(R(T>?+k)M;QV&f34Ieg52bE0LLl0-HJ^E83uc69no)C*PYNy530|stvx&1* zCJcqaRd!xpS(!4KSOg)=UZDiv_Oh&KMw1aX0=dLUB3@+fu`)bVgr$}YpX6PUQ=pT(j`mpsi8Jge zj2X2xn;nZ|9QF9?3iko+zJq_F;-@Utx4Jl+r$Ir`#7A~XIpUGX>j!%DM8);TJ@psD zmOH!7OSM+|WQF7}9Y2c`nrpk!x_C;;=(aVP0~*zt+UyGsR)CCPq}Ca@3p+cavzfa_ zWKEoYzUV?y`=8@s>G=B1&ZOH-Ue^~ycL&y(R%opfV&8x||DBKj52TgoI`cftifW|d zfA4);P|3|Yl+y)(z*OYq=Sg0sNPk;d`SH5zJoDA+XVw4PvIT2WJ&f0RQlKNb65w>x zCP?hn@-(mfR${*Q7XBqea5vtx^VjO~8vm_lx;d=L35#h;Ca>2SF*NBFq}pOAJ8R0| zeE8*D`r&khMHbUG_1Z|+yol*L>b({+A-c2w4GT_zOUs|cLTachpc`N+i{o7ezIaa< z=I|!lS)iRj$9B6IQvMcp!Yk#^YWto_g5Y2v1pjkA4BMIvJ83H1jB0G04jS&P?zcV@ zC_DnqWD+qN0*U51{s4Q1P;D&Fr~dng*JHzC5zxJeGHX496Ce+FS6vp4p|gwbkIxHP zhVh}8R9Cu<6U0x+%yr1~lF2wR!^F@pzi1VAYS#l1i%`PgG5!_chOyYm)1`~DczwQ6 zh12L2FWNRU&x_hXrF)P=hVUIkq1h>T5{OftiV>*Rj%a(n4C5P?RK0t7-yK+Yw(~0B zr)iy@I~W!(-ZwQ%-hxCxSt1Hxv(m$O8Q2c&$Vwu__q`264i5!9-pslgCIg25R6U^9 zO1DyUe}svE+N>e>4xaAdc37dSHKxkn1$`f607IVqryt<4o%7z8$%o|~kT1WVdou84 z=M!-aWq~j_%J8>ehVXk)+`{}Vb~sV>{`uE5T_=VW z4}B56GJ>qY0GVJGT3yg;Cf*_UT{QL>jBo~>ET+K>y*>5oV+BI#Rf|qhNXp(ARnW1H zLlQPbTQ`81qe@a!lOy>W+ouztL3+$7` zx<&>4UhsoAnMP@xL%}B5OcWl%VRbOjwL29;s`IO2dRl)E_|Wt(cM2iEn@LH0QS@)x2R5vTEd5kU}e{xwQQbU&@crFm+Hd(tMuMf~9o zpSoKzwbM+5E{${gRi<_t3ryhwr1Y*~B8UqDz-~g%+95b=4@66a|0r~N|9OD*{^Ka8 z)n&e}tNdk{{QTA%Y@b?wop+g(BI*meF=z$??|1bPA+1aP4~Ti~>;GD@g1oeu@K;BR zk7Ryu4yom{J5Od{tJ52>e+Arnef&J{@6g@pUBkJg2PZZFoWG)EZx7MEy=0Z~$gu+s zRe*_Jvk}&;>q217asFM3p~hSQhc*z)VE3!TCzQIMaco3>JQI;tN;bft_Kp)6G~oR! zg~afBGwD-LUT#O|e;htQtAg40(Z;;zl1b?4 zLkgUD$m%+8?)89ZfI5f$q*x)C8TdC~zDo0&nMCEZ4nSfl;oms6s@9blLLti z-Td_fi!n5wl%}oCny681HG)kJcSiTXv732EPkeAKvouzd^d*6gQnl|8^&gz+>M|HO zFMMgOu{()xShgCEFFaxwHiFPR6o0e?8D|wVGVcbB!+U|+4_QUrOs^`2PwL3D0C=73 zl@|eCj7}kccSHpblgGa4;WKxgTC~c`Xcu_CCCx7E7i~YR13u7C&mLD#@d_vU_h~D7 zn{u9VFU!FD+u4yoO5VUx5o3FjYSXF3B6OvN8%F`(oAtPbqWyhWcb8mYzAs|Y9SROg zz4Cx3G7#72ZQkeR!L298`x5 zBQ3f;C)CJTz4nLvD}0sH?)Fy^2i3ZfWpsCSD6DGZ&67k(W@l{*jL|K+{{Dcp;f5gF zimbmv8FSy*ern=f7GCx*ZWWPznliQeWqw7d_hlbZI9SRs4LG7 z{uRA|CGbbrWQWX+UOtt*fMsw$$U%Sl-0drc)8_9?oN;>4?6NGzZ0i3!Pm=ln8|a(H z26vMh=>=&3QO^#K%D{dX^%m=JqO`Z;x!#1=o|B#QTJ^`StsJ_#>JM}o!Cw>VBjfMA zq#Xna)ex=G18L*<^U1KE_#Q4U8CcXbNfa!|hj2R~z{#cTtbOOLSB3AyBBv%8r!!=q zEP%ah!}%%<9?NL8x3@PA-_OHUx>kYH-QD=tT5_d}kn=eD;ON1e99Mu#mfz>_aXWIy zV2w0>8pWrpPt0)Ct=-#E7Apm-=wJU}$CyOJDr_MpR6(KNIQG=dR`p@r;Xy)M;8v`# z(0`+A5Bb#5O^ZzrrHWf$XNQ*>Ap*opryClLGw_H)oQXHR)68+#0-xzFq==US7u_;P6-|34JS+0IKmOvrsa{6aNYWR!$ z=sn=4qCvPoAe!SdxlTLR_hvo0pCvG^>c?5fWj?V2l$L9e3A{j7j^= zHjTb>@vOll6xsH{WZCwmj;kUBPzAAzAyA7trSMqYUmeT|ZF63~D{ptUS!!X)8Mo+p zlsc5_19B>T-gA3r`#A<=>soxOG}GSW&4YNx8kSbgx3x*>B_yrf#3CVzShgaP``d(h ztx_NnaJ8q{w*EYztk`-k=hp}vA^cUhG|N;7@$a2Z7#yXdOLHw7ZeAv&=$%rH^pIPA>!_{Rt%u^?*Xa~fw51$}l# zhVvwm^K#)j?SdlqBon2M)%T&8x%M9v@8Qw;-ty5M6G)Oo8^!MTm=xldnPuqCdX(a! z2=x2dMXkQ?JfjjTT0?3?WMm%CltN>wsDW`z3E|ls0?=a6Hu%m#YVg<{^9eR7t$Mx* zA&9_o85TFmQc+i{uTLW7t0gEI_m2Ugj8pG>LI?*)dyEY@PNHPE#$!dXjYE4YjAs3m zSw&IWE|?Vqhw9Xva6fyBWJqi5hHRz;rLA9!Ocp9Bc}nK_%hZQ|1%z)3xqhnO!*1ua z0Dbm;$_>f4giPPY;1hrTTy$d2H)TP8BKj_iF(6=KnoUM+u#Q(XIm!SR>=Zlh`u!1q zjhDqgeV+j0&PXH%(L{DYggoH@op|A6@>nrj&H8g(GZH~o7qIwQ7Y~al(+Of3miV^w zn*o zHp6w2Ukz3U28OvNE^O+$H%6W>T924gkyPTJpN!@FCw$JkwY$~|H)_z4QK~d+F=qSy z&i%@EElgt+zsHJwoQyt7;+A+RNSsQBvdAKAYtNjFyam7Q%KIgsoyo;9;m^5aa!FEF z6iMRui_6~|%TmcNN>bE0{UBQg^am=DO(GcZASUeJO|q1+$U<_si4`*;hi9wNd+;R$ zph-NG>nIzxq3%kXVKj<=ql);FhbI}-VzcVM7)a9krXuib8?*K#O)tJ8!#Zp5mjnqT zg${KVqYK^u9^&4~GSRtqG#L728 zvq{aGRxFrgdbYZF56DK@8jrrt_fT?UqYP+dR8t=sl^fPIuHuYs0gtt*p?1~onvuKE zF|J2@1on7AxR>Oc0@a}Eg#n@H>Kxt*h-gVpM`Q-)GDs)!!)b+EWuIUz@Z>*@+uk7rT3Q%WiF;PCK$O|k5B zRZ8?)nG=l=Co3d%9rpr5JDvG{DtR4ybrc1LWcqK+l;u{n<8Z@)XIOsC#sN~YmrpHH z!Cve!|Q5)sD9))$pH)X z&9RyPVY4=wW&$wFO74=6$e@x|0L+Z1z_cq63p@+*>-xf@$#uIcDB}DV*r@x+n|>w2 zB6kWTIy5`|E&psd^mQ9BNG$hEHY(ONbIp7D?&0LHPL$;eiaGxkpsKCTsX7t3_(}UO z`AQ_`T&@$up}g?9iXZg=Y-k5_L=;w|TtPZ-wux80w>{3Yogi-;%sT-E(7~AiZV%b% z&P2~l(_&q25dl1I9gCObUHe70%nkK7c`fH(Z(U=SJ{0Nn*Wk5|KsERFJhKi?444<7 zRxe7vSs>B-b*KF!#W{e6UTnPTZ`av+N_*s4rK>3-1Tq>1n;CaOrQvgsgi9%6hRH_l z$NHgmnkYp~NpQoq06Fi2SY^2`nI^&9_f2~yWZSKJOp_55O%`$n(NYN2@)dien}*qym;{qW)#_u*f*^!k|T1!*l;)kB_+yI!xs4} zDl^V!d1elbs>MYo1;vArqSyhNj(d3@PkBVMzr|KYL}RX)x0X_pCQJY<&e_iE)$`V2 zBx8?3u9=)Do;W$ABU6eE23!4-Xm?OM1mTxJrhvaaU@<^G-*DS~DnYv_UpYO#-drMR z$uOXNu33*f+jg^IxJKgPK?1G+Fm$Uld<$wCHd}R7-4B*SRI7V-BObQ6J}tcR^YfwO zyMWnre+`ppAOW$CeOQei{JhD$H(aWY`r(qdok^v&Uj5MgjtG>sMZf(Yryv8b*IC=m zQbisierxIkDb2b@?kdd|jwt>f1)Oe^dx;y9;f2`?NM_ zJp}$KI3JC~3x#BqkYiXQ#2m#>orpMZl!S9X%@E}(z|)J0E*cb(ZmVR(j7v4n!LyY6 zjQBkN;H@QaC~7?Al&0pW^c_xd-C)MMm&@~Wetqf>myfNqnp}Z z$GCPH%OCsl3Y~dW`Bt~|uy}8kb31^ztDoklp(9@`SZO#;5M+co7#4i5QugnMrfjj` z-(S~(k>Pp>%YTvY&60Q1o~s{OtIhwij?z{io&l13*NzPzt@4Mvib7i+)NVjJq4M21-Sz8l@c> zQgA8+&<}hv34ux;zafF2(qFNJxtkTv9nho#6d|aEKo(Gm(A)|x_Y_uI#F^iV3EBZ;9+c5UOIfZyNvAIpnizY=p)dUa^UL9_?Z+x~g?F+}nSHCe%##Uic&VUJdt#How|y3zVnbS$ z$bGyC7eqCpDCotc#rBo8NSlP|_PX?D3IrTRnbf^hW3&>X6(Yx3Hg0|kfi}CgEQfY9 z1?2suHh-xQu~9{&E2PNlpJN0D&esq&Vh6GP=7UOedFaKI&YtQ93 z*t>~F-trFxOP~wE*x(jRKpak(K&BB;8kVm<){<8Ld=Mw^BSC@*Bh(MHnzGRr$A}mj zjrU_YktAHht5E(*L;XSNgm35#i-+mx*6&qcjNvGztVH-5hWv37RSFNL1By%??Kx3rOAC>N~D#@p>y2A5pp3nO-1#YN#3uT z>SSZP(!aObvH3KsnylR?-K%3Z9yL-_{x(3;la{8GoZw;Y8TcHgjk9jIYS25+()~~V z{U~IoK^e4qeCd++UTOEoI;mzve&k?{>mY+q&*y|eFRUc?Q2Vd{Af#LgQ6mx)*3oxZ zwV*A(u@D7l8yegV3hLkLl7u?B5HdO@7#(7E*a@QA{HGA=l+3>5xl^n&dP&XB&+`Mi z!4oRthfXH`H}Ix9ExS9j)BxdKiVAF&B@dkzD|P#j2RogT+utdf?HG!6)tYA{jOdmyYDJKl9G37X0%;X5Cq>Q#!EBn72V z47SKvr`_NqVbVh;AIz%Gv1|rrpHNQsqJ;dGcav4p1#oyFhBX7F*T+Jr2We}1Pk~=m z5zZ^0RMiBR@Yks+V0?@kW~C~>8t4LG412)oJt57)M98D>lk9%_s!S(Y45Q)=birw! zY$d1d9MdY$FXoR2-z;FJ;;cU-8WWHeLX~v^3vZ0zpppM4XvY6fP?`T5)am~#=xl!n zaEtz}>@ykAH+#UDQi78NjH_HCn%u?4!&{H1iIkD8WUHVr(REvK%HZ_C%Ly0K3yoyB z5C+AF?K(&DBL=#K

4dk{w(dDsJ=h^m=N{)n7XwhOQcPwZJ6qQcIPWx@NgMW4X_WEr%Fq5998FolrP<)k=i~!$t*`t^wdKI&iG#J?DBYSIBe)J;MYI zwyP^Kzg8Fl{h9xis_TI^ZR25xxR$$=++cSGdKI0LacXrVw>5&JIWSN^IK<*<5qSX4@_e16{JnT>MQ=|7Vx1VuK8+Mr_eH+mlB`sN3}ZB zzbna3`;Nks(aKRe7S)5lX0Flh&bAV=Iz1+9XgVDeE44ay0`6Ch6bPsi-9k_MHg%0v zEFD{TM2oF9=bx9&JHpeNwLk6CK0g@7h_p7L$!?5@f=)R2c|u>rl=_F|s@Nrn50WPe z6=#h*Oz_0z6!171R?>5a7i8iL0Q=1DgE3hbM|!R6E0@s{WLF9s&V-AKp!yRwg&n+K ztrm`a2RK`0b9k77cv5UeABT66_UaSbx7udW<`brof|FtN@YTAzUpWFF-tUbz(u4FN z0t!&a2WUr39N~jZzu5SH5>tg~R{UyOw5zuA$Ny$Ck7nC|c-2^5pg;D-7r}A^Ti&Ix zB!*>!<(!Nf0TcV|+vjKoVsNa^Ohi(C;}2^s1(zIjG_l|MdSgk|KgLX?bx@LcQW?oX zFj!N9E86vafy;5H7i6K+c#O8}O$g0N#YZ3-~h$eu#-PF|Z(JOn%N56xj=mwXZLLNK?28Ct@q^ttNh_`|a;PR172hEG*kH4uu`o%!%{^=Nl##e}$97I7?S;9rj^`={d0ty8l z>73V;_97I6E20`0B*jPCkED;1*Rmghij(4_8+&D>nC^T07mIISzc0Lu zC0&WU4}4u(`1~dWC_b)x_(;j^UAWhBFI%$fcPQ^cF1w}qQq(y2@Ih^~aG$0y(SuIV z(Tq^yTa?21-w)y*J`!?87Vc%dfpZ3difypH1urZ<3pWHiZlR^e=wgYvBKkB>zI~3i z`c`X~L^b-SuM#iqN6H_N3CVMH@*qOsfQD=G+N*D5_X2YFP15Oi;FZ`1bVbZy-QUx2 z4~1U8Czgt;x0Al02)~l=$|M*$j5DCY4iAGe@gQ7%c!OW+3C>RBE=0LQTAsM28xdOn zJ`4T0MbtZwYP8n=ergq2cYvDift4bmQc?D@5Iz^7=XGu7bbE0^zjCIHtoPnxR*UGQ zA%(Qs6j*!KG;6gZ)VgARL`v8Z*dTE;DeL$83CW*frIMjFDbk(buEbG?kLXWZ)IZF@ zCKxZa+O0$h#0+aLXKJq#5a@>%lBN-a)wWEsbS#a~j#d+y1PQ0e1!IQ*?+VOozmpZ_ z(wId^>3ctjQ#oQGP@yJBNMc~1>tXVOEGHVwvf;GZ`g7 zGm;i(NF#!_L7xGif4`DKsRpp|5}1d7t=+}{i9?S}F(AgG^alTy9X`Ju(}H%U37AZ2 zeca15JIutsxVdd<^}6Q~*ek}qIN6VDs;cW=pmC4c@aXjla7zLPpbJh*|MGQTH^<+c zw~1}lYqcG2k%o2dqdJIAq5CX5eGi{@?)KV;+w>c^7dS0WqG*fWEJfA3ILdOo4}6T? zj}_c-k5Sx^L>+)D4)$&#Ws@LeU1bo`h7X9{<&6l20u0DeQ6{!eB0n{r&4omvVrt;v zF%=p~LG9({2F62Ih~2S7;oM{Vf=YnhfKDGviDF8GDA60Y64y=t`iXK$8*W>uFM7Az zB-<-AC#5nOdxRqix3N)LQflt4p*V->&e2fs?x7*y7}?;U5PB zIL<1eV>rzjjdX#nXOhPZ2^{nQNRE&?$K2E{oi8jY(TS2)n3<63kA|M%w%C)Z75xk0 z__YT=d!Eh$YTZ;MT<1z!eP-Uufbu*h=S`#tGLR);@aezquXNSBz3m{1oIx>mhHp!I zd~Ro}8&#y}Li`F@9-sB6fj1~_A%O%l(-j03ydFO0o#Gzf?Yx1qvxcXy;FLZS zUT@$5uRr|P-#^<&BLwISFFwBJRSyh@5nQf zsl_T3-OR3q-G=fb`Jn6hqLIi!+ac}q<(-F*(;D4(EXbDdN{r|4pLwP3ozKe~H=COI z^cI%nmm;61wU+Zb7iti@44K0ysS8eU6n!`mw$)pgqkvjN!>t@quX)nr^O&M6e&mgp zz(qax)t%}3P59+n1JNWwmmSnIb+?evO14AiT^GQyZ-uyF#z6ABn54=(LrJDvL2?>n z5{;XYHYBXaB<_QOwt!V3?{(#)sIV13%2`pgKy!oSZlurZgu*2&G7Kf`u5@r?sT1m5 z&NZly$M({`RKlD8{$9<`D@FMzsXOzcw%}9O8<)sgasEpALq9A#Lk0P);5YfmvcYDHimNPH@yepn9N0+ zMCW(nQo?&uY;y7!0&VlB4^v64NHxgmTun2I7Jdv$A?E}oQ=DVFXa~n{hZH~}5Mfvs zFE~g@TbW3Sn+nC~rFFn*ATddeO~vYa5gfKbniWKJ!*!_ki%Xlrffst?W@^ut@A6CA za}0Iegd}=V66d7nyGM}qsoH7d@C64OIG+x9PAMwGE4x2{U?Ay;Cvc6AwnA~+R;BOQ%Y}HxrUE8JPfO#MGXIpe!oeSFHoqO zFcRTVt(X?^07+T2N3;UNPBxuydhjs4@Q=z3Rnb---3%k@uqQ~P_pIXOql|Q69%)A- zr%cO`_wh|=Bnxw6;8Do@)8%8?@LJ*`=?ws14T_ByRola~TrTjpy)KKb#A*n+pV^&R zqm;ty^e9fl9HD*)_16IOEjP%&(7r0%v(|`Me?IEWc@^FI$g~%@!d?4DA+GA2{7pCo z?jxLZ{HJB_#j~AujIb_#e%l84x*;m}=OD+-W|MQ~^k?9B+0w{~Unf13rHK~Zw*i;vOqqkrSyjH#>>Wi$Qo2Fm~M z#*js<`+ecrFpPW($a1gOu~UXU=gx z{tkP%Wae?ui)0XwRM*5GCj~8@@@r^z=#IL8b8Wus?`{st?)uMi;M~+2wMe9BejI;` z2dKM9lAhodxjJ^$7wrVP-SQ0yp>v`Z7)OWQcTidPa+v1 zOf)<^#>FD1$!usBRwFRbc`oF>{dP`aw95e{^-RFRoh;4eKPr55`=re7?bvs)+O=Mub1vIxam6zM6E2`wIN5EV*Y5se+C+;7ukl%!X1QxaV~vDX)wP{onznWVf?ve+<^_x- z9)*cULKM(_t0TA|Mj)^G@nqg|zR$tFil-8DyE;RHtD61m;f}t1h`ayqk37prRAfXQQ86dt{e^1bk>1uj#_oyT za$dwgZ>%57O49S}8l;%)A93R>|KK5oK@y(>v1~hycou1YSy;ozJP$Ow)jrR%CVQ2h zS0CdF;=1pjywy+FNg9C24iheP{5KIsd{hbpDZIAEpNH8{kx#>Gn<5OpbUe#>H?zUU z5!oS8RDf5>bxZM**R&db;>IRlw$2)e66N!_Ik5CK85>MdBZ3uft8iZD56;Yh_=usO zVXmO@tN*^*jnR3jP=a1x`ncMFO`veVq+k5n_KhXvJ2=)fKiawu z@4oPcO)JUc0xr+V!P!l2M%l0mPV~kuw6F_X)?oX0wNr5Vz7)U5O)7PQh7c1J!6yMs zjQ7dUGVPs#-^&|-u#-G45aBOhaZ5f6Y)bS_&PT&GRS|c%=6M)0NRfLCdOBQUoTv;W z)$tE>DYJ0Z#5wd!31hMIlaj+TzfaQStzGcC(MH5Xhre{X=NJLCL zRM=;ql}*sL%&4`oG?koE@65I&va5+CZ$P(8uNTy0Kdsy4{!jO;X!@ZdBm}voSGQIS zDx*>O>@+}9l{BxBX9eH8eu*uv1l0~J85S%h7n8$wAi(#`d(n4THYD#({0_&&|9uR& zyp8j07kX)aS96cz__7j3*$Y`AX-}o^U9KgWz)&2wXFk~eW&Oln_3iz9MLlg21?Tgi z2L0JMr7$=vg|D`uZ_d`Zqv%%^nhvb7c@8%P`GaWcTEK9^VbEkvZk%Hq^ME?3EuB}g<$p@0fuh(9OP|2>CxNN9L(-uKm!cUE)4qu|E}>x;s@+^ z7%84{QCgANz{nuVCT8)nC3c7Oe0}Oyg*$xnN{m=?K5L*k_IC*LUCC-uy6N6#;DwO+ zzs&Q2gcU;>a;2RA9mP$0%&IK^JBO3q=ISEIX!9-pS5!M@#QU2K6f9o!WajoFF>%O7 z2K9}%BIKyaT-=qdHUmo^zs*#L^9CM(<@78gC^~3b6IgNUPr38izv?u#9jEVxv68}W z3!lfNj-ct4Z)xYYpJ{&u&tmn@?{qaM-F!M!qLx;mW9 zZEZ|ZR(m!f+h5*jtN2a96V3x%PTUA&zC5v50Hjq~-$TDP3-Txgmv|(z>3k*M#wFDe zaVceG+(y`9IGk%#oTnevc;SD@4?CCc-NLasF|ZG6#WT;pu&8F*5s*IkLP~uOP)2&= z+&OEhQeR&1vPhjAuN%HZ<~UDu*Ub2%%>kX>cH)STS_FE0lv1YMEJ#dYg`GE2l>D4rte%>Amrq$I9YBGZi;-#cXTukGu?E6EvZ}?N(SKD| z3coSeJV2+A_jO&JFUzs-546E#I5?-mq9c%hjCR8QnaTdP=&bKf8DWut4^!JCTBY40 z^Qg`*0BhA5s?LI2963=h9gI6qnqH?BIok?t{)x?#*jruoz>@!>@Mt(lsA5{z8>^UK zp&~*jjc-d*2Dw`z_L{O$%#?H5?QdJ=gJeE(;yIO6{04o12tuP52+}p(-@0Y1EgpU^c~G8WKg}yX zWo%aVrd5!8#{}j7A^0sGoEy4gaSnreXh^bRxF!zY?B_{!8pRvfXok#?bPv@BVOTDC z8tjU&xpTJa;o&ABZ}e!W6-e#MjPgrc1QX8Ms>F)Qj@&d}1rOsF2C*Zc;VzzAJEnXc ziOG=IYs$&}V19JXSBY;KO~iz`R%+z3ags$sRVWY^eoL^geC^L8o}S{y*I53EH}EMQ zvLSLm-)^&YyOOT8WGu}FVR;iwb*W3SLvtP?)+9cdDI$u3WlUxLx`Xtk(aL7E)(M$# zv(?^-+6(;_8ynkwN%ee-IbzGo)tFtpmJ_bQkHUj~o9@h9Cm$a6g>nVi+i6twYc>2r zn7;g2Oo+aI`8fxgN*TZag)5i4I_3v#iIA|kr6SHIfWjiV;YdY1z_8FyXF1()ND+C|K%i~o zNioY7C0p{36*oDl|1rjuq_>ZwjX0pQerg`pSZcq$oz0<1T|J(|!tU4ag{O-RWA!9c zy3dAENUwswn;}FQ!eEsx+#S_4pctPL36G>4p9r^E-JZAJN({7V0$1;q)h}nlDH4~B zv6Y_X5jcfHm9Y*-h1^*tM*-WN{7PBJb>yTD#b<%HFgjC#xY_9wm#_^xvmM)~68{)l zk9)5?;k0KcO&}$xlF3jE(-DtpY~qfFPouB3?G4k6zES#GuJ|Z`md)DSk}v0Hoj7qo z_y2JAmSItKU)->ibf+`}NP~nl4Bag$;7AEbcQbTKih?2yg3?IW(5bX^NO$K96Yt#r z=X$T_`}2LBefBvsXYaMwFBTzJ!^D=ma_gO|1-&)e&fVhmzg2=*eX0!2*Jm`2p_s0R zlT-idZfz4)OIZdrC$fw=aNE?s#B0nJqke+ynKcIho8H?1iiBk&FI#Nqx0raZEH*Fh zlCVGe0SdrbRg%Q`j;VyIQ}(C$?0ODKp<6q|wb@I$iU6;HtA+us1D0y9vC7QVFzqMU z7t~v{`JDOd9~U4*Kc~FftB}-TRGDzZGJ^C4l-0gmkwV!owczd9qVyD}nAPsBP|9zg zK@mG&MwD!14C{*R>+=>7D#LuhXY$PWN@K{B=O$j=NmE*m+6e_RgwVu`sb1?Uo(!Ef z&llLffA~qUkX|=XdTS<$PKHa!Sa749d{4H&(mQaif$#E-2mJ)GYyh?LTgP)v z6RYxDu8$Oo0M!r@VlMbbJ*>Ry0+9e_9mW_`nc*4x!Ad0^_s2T!;`pLY0V|QJwMq%* z*XYq_K%fQsqU1hE%jv79YR4K=YkEuzsQ(n8 zq`&!?rO$%TiCeJ9eXogHSHQ@g6baGovMz)8mbCrE z(6Bn2B$0x>xs6=wemZqjcopzuscG06baQjpPM9vwZ!n@YG$1)I_H)gt#S14=82`4M zw32Bpo&0?|70ioGnC+O1$mo}FJb>kxj7&fU?j&XqnjB-;*MxKFwwlv;5Q5bZ2U}El z6zCh`hfW}W@1GWx7rcu$u}ZwTBPUgQ+b~tA6*;P28u_QFa2Gj;nSY(WgP?NF}DE9_sQdXi2V&7)7moHtVI@mKJ9 z%2gRwF;)KI1PZ+QG5(!*_*24bd|}0>-1fpsYA?iA>=8=pZFS^DMMOdD6u7>=Qf2Oc zfVaxq3{^Vzh~m_UB}_X#7ApN9>Gr?GycA5D95G*y$`N=tbZ>*OMAE8)8tc~CTxdG{ z&;0vR`74akf7`EWuE|#arWBC%ol#YvSM729{B8{WMwYR4{Z4k*RgcMZy$*J}2iJ0^ z^td7ua(Y(@cRusy1sE|AbJ--mwx7eULB^8HaV&d1F_^5qS%RCz7FqOW#!*!2;_kq3 zp9jprgue9!(movX^A|ih;Cd}AweXoKCZu)BFc%i_ul>KOfL(@Z)56z`QT&H1Io~1X%&!rPv4& zVPb)A>EGpI0XZ-pwmhbWrGI`t%A0!z!8M}#8XT?hAcjv!P|oC{mGG8@HrsV++V3?u zq717N&+s)^G)VKI0D@bu{qJLn4< zMOc;mlm`}H+-Z70NfUdTm7_YdSB_-|UEl7=lhPb%>?~$}H|zjzG5m>POnd528^y$K zF^W65OZ^9Np+#wR-~UEtDOoy9+kT&JJe0apAahnGNgvO^Ay>w;eEZ?6IgzuNCgJ=F z>$r~1AC=$nK?y{Cs>T=_75Z0Pqm~h6h2lo^N6G{?w%4*QU58lQ8~9tJ(2MAK?wX?@<`*@q8-WINOu!lfjP{fi3ESh3>jY zZntvhCXQ48Jd<%JN`-}b@4B-jd^C@=7}nEP6_{X7xvaFpZAB2)q^91kDYf3f%D&#= zTCQ{SYuFL7{hD|Vwrwir`y<#}>Q&_XuTxXsi7-^C%VA-&@Ov<56mKSWOGAG>gF?se05YJ0fyIibqNPuTwe|8SM|;;?I}(|lL{3V!w! zBe@ZYhuhrx8|IwFz+|T)3wV9_7*A%Wy#XmMZT)X#&?t_RsmH~2M} z5Srj9$Yiy81k>KhD+gy@JVg}DMZHP$O>N|qRmX1Ysyxm_Up;G z5Lz(m9s3zQ*JE^d)Hzi9c$*9t34ga7y606tm3&KZfOb)4efea78rb#wEaaS0w??t~ zeBD8v1)Ry`{XourTK{5NZ6z%>qjyu0A!f>;^6%-0pSm57-KWzbxxqVu6<2nSRAS5K z*Fu8g&z}!_T^qEpvyvMdxVJdA&EL@FGPg)&JD=wd4halv=43@4WTNel9nT8n84k{yS*J`7| zhEXpGKlSEJ%qm#yPc=^M2?p4I!PKT}zyz3y_@v8SJWwRazcBgg(>lZ>DtPq7K~wN+ zx(;t+JtlscDvIH{HEpxxGK>1t)3CcB6{+NrLS&ZZnYGI6B7ppR%O0gbNzGvfHAV+@ z+r;b={+|Wlmi3Hk;1#?$Q8YNFw9K3k3WAz>VNR2gm^JnRcX+!Ie@r04K`zxa1bm z!NU_Erai%*7~kgDBXzd>X$)3T8fL7W=;0UJqV-A}jseHfA1_eATSsc85+9@*+ZK1Z zn7@_lKLoIn86-Zawj~d9fWORfK>7A^OL5JwpaYs&EVNaj%l`|zuWThO5(Z~=1nOL6 z7k%ETO_xX{soS~wFDUz`)yWkl>BQ`#$!hFW4sgs`_BnLiK>NyUBv*yQH8}A9_X5B* z_YT_P7)#SO5%h;9;_e?Jc+ZraezWWfGJk|SRB%H1c68A#mfK{#D0YX-cxgM}1JTzv z;nV?aVtZ-cv}u}1k-1Db_0@B9`)I3t$;7K|qfg1!;aj~y!H}d5GWu)v|2@RB_nJt8 zppWRT&w~2jRG?lz#s&p}m;YqDK!etJDbSMDLAy){Q4-uNdBHylb0X z5a^k|Ub`h2GXyt0+Y%lH#l6miD6Mly_hgP!av6qO{q(LM*O!&lKR5HDRkA~J2VCat z2q~ZD1HklGGP)=#hg7Wy2k<6~q%wfkorkV1z4(=uiwDmF>`#Y2rDn6X!~(&L`mA~n zdIf9}=aEGGuH_LR4DkD&#F`?X-?e=}pf{;~vStXho_N$h4z- z_zTr4=IMgRd4d`7)k;)>yTZn>X#Ul3`(Qd6LMJ??ZM~^bWK`HhrHeAZcOj9ZI;NZD zZXR#;8y3$UqEM1B<*-jFBy=H_Zr-z;k7h_^vG%QZTkOPFy=NTr$XZONGZnP7xtE99 z2kZ+c_{Cq=TZX=j}ct+2t!bU=jsVe^7 zu?0RAze2%Nxcj<9$8v>xeY&M&41HLU9yZ@0a%;fYOgjH<8_TcudwQbwn~z}WM;Ekr zXl%(msZEi@4Y>=c^;`4ZMc@mkanq_8s_Eos^rOsfMT<1I;%pI(JT8&lNdqFJG}JSA zweN|KnZ&*meLuL5c1S%FtV|ri=q-MI-s~-us@)Tu-)?_fqGm2MFRCJvfs;mmCi*jU z%$zghLhEp2Yc`JVg?`?j=aP|=m^B(ZNM+w-_XMJ~oztQkK|D$doHopi6;g{jx`J;F zKbD@2k=HBEJSmE}Yhtz1NiJ1C}!|kCglTbQ5$Qq$|t*6 zU%*4cRAIB$aMiME_p^uucDQBA(*yGC!H$>oDfm8SKNU9j*!LEcq*pqL8wA;|Fs}3g zpM-vAsXGWxwX|Yagr`LBsXF;M-7_MXEt)^b_BG%JpB;_?P7j1s9vN?qy%2_yUu0Rz^FJi#SRr`! zAO>u4sOWbHtkzCcKp%{6l^rRaVwnoW@X|e{qh_gn@m}j+*+5nJ2k3U|u$_i6k>^j) z!>-02of@1f{UU&Bk-7pqk?@fN{EQ@uX9Zy7wLWN<@I35uk59u&M@$ar@FQG`;Do>! z^UbsO&In2c?dAky^g=HpkQ1KS;alBW_>JQ2|x6aMhP z!};%8Gf-<9wCNe#PP@B#i%7} zLZf(7nF0XneQVh`m4Bc2{+}i&S+UQ;D{;H=GBO|vsR>hw6oTI-whot2#RvEy+RjYe z_=*CFQ*6$){4xkj8;O%Fc@k+2jebIxweNrYsg)${qm<58^)#SX+z}H-l&r#sZ}LB{ zt%1M?W=`Mk=&Q+0i|ge*kDlb-^PU4SEKvLQQcoCHb}Ie+RvJtyU6-yL%&qABy%mM9 z@%tzGCTrV<=Bq4m>ur1exAK*f9x+C6Wt!axp=zd*!iqG!dtQ9re_k#u39K}~)wGqW z5uIO79L{^I&g3PAog!!%@bKEQ$(s|(J0H)N{sxa_+dMa?cX!mrg(SLlYG80kB$tlG zFV>GqNtN1mkdcOOs$lz_y_y;}D@9hdnJFtPVB~cq-(UP%Me+12Fmolr%eFm*C6IF4 zo4%h|&D5QzCKo)Lamrf3E%;G0miIqJXn6g%lXlLLn>rm1!#TQowunNLI69Gk4JNT~ z*|dqgb=hcn(f;vT@fYbzjK4@5EgCW`oC~MK5nqO{)Ub{T1;{xJ7G1b$oG8`QS%cp; zVMQh4QB(7~9<;~geqPTAud8Dgk z9(_OTajFpvXAV1eDHU{cSHa{zLNS(jx8fhw8cLrYkCkE9r~x z?8C-|8pL6dDh*Vdh{=oe&sK!q5=p*1w+`0|Nw*>duxG0Vjm9qs;GZhz%~4Z@#ENw} z`QMt{WSTpgaCk-}+}a2I&lYXs85_pjTMyMVsJ8vr+xC}&_Vf0y7GtV}J6+rIe9u32 zA)jI=Ik1nf(hofha0_yIVnkkbkSr+(oUj}y3+t_=Jcq8#Z>;QZnk%$ zRK#i7c_A(zXCxz8d_ch#c_sGT=%6Fyqkq4(yKj*sEe}RFX5ME9qNka?S;@rldMcO~ z+-zuX+XH=l@oh$$=`uEBoxnnKbdAcBKc8^Eg~5t>c7 zn%)}ID#DyJjH!Y(W*QPEmf+#l#kv)LRPL30<3mpPEII4%_dkR&$DL8hne-e)7WWnZ zic6D@GYLMc@shn7Yb@hPrdh|}&oK^pc+06=qsMAB$5XENOm63NSW8pa*||vL2~m+{ zYYwIJQ=L6l8=c=e+e27JPYIJ!(VqlOkKe*v)>>JYTix7x z_inyA`+(bpxjQmGffmW;mOF-DTzYZoL%(UXYmtuqS6>c*JI&+~U+2xo(kGO)T5on% zhg9KX(9$MPzX)W;rPR)t9G&o~)DY?F992u`5 z4Z%MCW0ub>=~L@btQ?6WfHJ2$WQWKX1r|%Hj+#|!HVkwlBf_?~WHd%15^PE%4jSOD z$+)<Vsx~z2isk)Bfk^!QTe1BG`np$41DjFMzl|0$~!KzrYP}QVjh2 zg+LLy{v9d)tgY|w2&VbJ2~|P$9uOn9%ePs~BosFW&z;#n0m>j3FbefSd|Jt~Gnf$Cow@&71bbeGHKI&wI!whd6YJd< z)QJi#V=FE}b`+xm%HfGrVTaUa%dq3xVQI^Np0*N{OpXLv^wey~zn-GB$jp@cPxNRq zZ8O}(fA{Y~`a9YU;&-xndpWB}zX?c!dmk_%=gtsmKX}$KUR#&zvF^lYDtNzt6tPx4 z3HeVVDcF7Eoi9)s8o*xZPNQabyvIs&|1fB?$m+CTC;4_*78eGmL}q%8J`x>IJvk@8x_>Ig z3&T*H6?b3OX}=83Ke5sMNNCcgbt{V&))<|?>_(LoT#z^n#l^=XIvfThZ7a8XQ^AfX zoDE$Gj}iMf8@+@B@1Piyuv>}6u}^#=zmuRFJ95KMhu%58O*o%W{Jf1YJI_OZzr7*V zy{R*uP8)vn8&W9SBrI#>`3Ta*da2EY)W?1ftU-MSwL_*V-={r4ZxBB~7~S!c?PZ?5 z-2Tm$-xah`v|r0-zVNm4@s+fN!09Uryq`o z%PiYyQ~q7axffMsf;U#XTqN#)U6 zwQD|QE=#cgh27>;SlI)-@)5H4TIg)!x^d}=>w0JGxw7|4&CA*xu!V2eM&yJ>Xb1%eZJu^loq|(&hW<#x^iH+yIEZkk)^T=tDU*h<(H5MMbobe@!P!Nh99p zmbxYMTDa3H(OnR|wbwYjHk07o_4S48n87}^Hoz1*x+jG2*!9XI+7E#BX7u8mcX)dD z@vy&q=U`XM12FO;*U!w28dDpl4}uFF>mOuOrQM|DWgV}{`2p{Qj+J_e+9KnVz8vyo z&Aht%RRFS)K=*zP_Kdu|EW>wMT&^n3euDv>OBblQaPdd9L( z&ru7YJ2fP7nm3#j{qnC<CFySAw)?ryxwQ^SPuna;g2iMZMZgy*y%sn#`s%%HR$A1$Y z_EZ|jP4(sA0!#9QDIfePNqPD~(`N)k^1lRL)JDmyP9FvdhfS ziG|j!_ICCWu1ow(K;g#=A$$J#l5LK$cLT{4CynV6eVOo6QI!1U;V-xCy+3zn0iF1P zT6ep(1unE!4|0!l>KmYloY4)7b3_Dizw^pL$UhVkeAecF7DT%Y4u@}uw4#?(k&QE3 z=p)DW!?ZrKJnRIt;d0+~uc^I*(gq>otRM2um$-me$gm-l^?48^g`c6>tP8FxkE9K>L=LJK%3GPEU@PBz=(8JY&E?rGb)>^cQ z7!M@>B0jAJ5;6+v;1sO}dW0>6oZtds^?Ygl!5o|Bja>4ZX(zivL6OPhq6PO0;Ty+$ zgE<=W!J#2jSmz!d!3f|#?-!l?MVYxdAIYTZnr=G-jx(sZJ9pl z|HV1ECM%PSN11;~dx`_TbPa)hMDgc>yjV%zKuHx=g%M!buL6(zw)4@u?qBO?S1h!` z8Swm@M#q(GfBi8s?SXLbZ!0-9&%ai?)MoJ38~>4?pX84~_NR|% z=Yh@eI}8@oR#2fm>I#`wxVZH0{IR?xXtfs!=Yi*jO8j*?8sIWSAz@D!@C!xs67u zszmpMp3H4GR5Gsfci9d698h}>G-?JOI#1T?hu=i|&)^SZ7)IW3$=^PETr5ZS%inpN z*FWZmdaGNF)<7=bg}~58{6c~2zfPnk19@MSjof#gqo&p$d(#%%Kro?6%6-E=5Z-G2 z)qOLV;64;pWb|b5IB*jrH<=G})VKeZqdXae;iGYGFV-GB*@u5#G=bQ1WaA}XG=p5~ zhD_qhyZYtxWhtH6gtQ;utvbrDA>lvt3EZA{rW5BmTKX?bA&>05bfW?vulNI8QC${U zmUHD=XYDZ15rq7+eqAH6qRi~u-Xc%~1a?1o zhir}`*O4%x0*A?9*efMQrlbRT#FEg(eIhK9q4s9g?;=c3esukAXyJmfRo2)qeVe8C zYt$5MSU>zZ$1s}=>f2<%h;ll_41)Y~H(LPOozJ^nHv|5Z*&8W2srCqLV)EPBOx-nu zc-3s|t3Fxy&hn2~p*eK@SN=Qw@T~$eis9#}>9{=3>S3Ed(}xYfuI2i1Xz4-LV<-4= zOsWsnfA?l@EE=5KmRZZNxpU;Qurg~go)6pMTGj?Vn5Bjc+%^w^RIbbAj-hvP3&<|H zqfOH5j2ZZBnpOc?7ucjOw?z~(eO+I6D(`=eFr%Xe0#xBI3G1$f2-kRAcdoOx7)ZmNNNZStda zdi~bdMsj&o#LN}#jYQ0f8+GFQn*OU z=IAi{L7I?hftLQ975BXMem0;Y`mcH!sTX5arIF7uZjOnQzzsWDYx~VC?rrFK1#pan^S&DwjO3k#jKOBE}<+Fh0 zNxB33#Os1DUsQ^K4uoVvXfl`EOF^CH%Sz@ht0M+F|E&n~%dRlLm3p^i%YWvD?uf0Q zLxerS;5=A)*oWy9OKMx?AF=;RI%zaJFrF#U;H!Lax=J1jPhhORYxMIHSYSK)BzD%P z@tJ7ZDDCVGY0-FQWAeAc3K@?w2WY&X-$q#;oJ#)`&#_bOEya$8#AESgl#|cNm>Us=DGT;0j8;hrU1YBs`IYni6 zMEDW^(R>iTNue>+`_kUDSdqeU1)>~1h(D@W;A<=+j#M&bS1H}x6$+bar2H%o=>mV? ziqswAr6VGTMlGAjJE#V5_O&X>cj@HDud=y{QTU9glj=C6K&jkKZ;t14>!XC_M0d5Y z+EO$4k9Pf12r!q_T90HqKbS&Zn)VVCALHBx51T~CB!S($N0qL-NZUUz3M8qhJRPmM zFtg7+=6&F;9id`NBisEZxp}0*e3c+pN}%eBZz@U!fOj?Wx4bWrw<&M_!7L7$FsjgT z8!Ou*klAcU)q<7Oz2o^Wx$3}qw;R8Vj1Pbz7x$Ulu(PJR>lo>{;B5#0-OYMVt-oy! zst$@pP3?HMR6KG(Yt7qe|2~#pykoVTVw-}U^NBCuub-gsSs2$lW=mm+C_M4nZM00V z(VfaihGQ&~CYH>g^zxr%)f-GND|FHQ#|S;;JGLbUa(Mq)fqM_}>ZWUJDim zI|DXI3vN}oCXFOW*%pdA-~%CEe>C|Z6t)LEd6Dn78+;B%+t%ETEp%gW1{5Hs&w=yW zvWu4+zT-3_)aOkBUAKOBGLEuTRDaw%58s8H5rW>|OC72__74N&9L3|4Xa~XcZ`xzV zS@)OPNC4rGUBB7}(TPC3$(I5hc$_^Js4GR8>y-1lfKI5Nwht#XKo~|Om8t~ zPxZoOXW!?)lm9wFIX z57e~{Y2p9m$nan%q%7ARcU}q349_0@To!gp%Kgrw&XH+dTbx2SfNj{mAM?N@$OF9X za=Pg#n{fZct?J6k1q8oZIEn4dK}|v6ADKdaXJEix4Il3oAaAk45HY8GC;$|4@~L)L zPHk-hy0v8f?)BzIwLJ11Wg`?g%6Ias0GYtl8I}8YS zMyUQUw&nml%ohN&kP20xvCVA6pKF;5p>%S7`uXcgXjJVh^6#EE<}9e~G`S)C2`zbH zDQ7@v_kA==!)$#+2>epL60bQ1GM;8f?%(z0Q@78A)MWk@DnDls8#){E5sc0iK7Y_I zhvy~BICm+_>~|81V5&X8@KW=HHcNEQGY?7sps4LUdLObMBLBAbA%5Yy>S8_B)IgQd zoK&uL2V~uS(RXe;9rTRj%&-NI5}%iZP?E&zM5wJd!4?X7ml}Xdu9gYSlY6+C?Sm<_NeD$~eS+U}Eca|fQ#^V8-b@Sm^Br&5!lKhb+QsYp2KPMB{RumcQ#<@oDc z6FS#I+(@o)i&VadJ264^wjc)|l~vS^QPm#78`iFyNC? zU=$sP%n8o)`W3B$z!vx=Ji629J^yEVZ@7u2xO4KAT_68WVKz7=@kKKG$9%9=IOC_X z4jEfS8uxyJI`pPVsV@OZY$I;~#YN_gE=?;k9pbrU{_KY?Z~#dO9Q66B+0kzK2^CsA zycHZxu6b!uH6vIGsy`aiLCy^W!mhxd1<#@VZjKDm^e>OMPj5hwiU3MpUS+VttIQuy zu6!5Ty{M1sf6Nxl3m+I(r3c#?Gr?ruN2x>t0jFGT$%}nZcG#CWtu&FuD{KOj?yy@ z=r|H{c36G50I=tP&%O%~CtzKlH59L}D66Y?i`ohzxh{0TBMgMq1sxC)eiD5AP1|iB%=&e}Xn?+S!P!=&Mdm;icmXvH7g4t$M6*u)Ndz=Vb1UgpWCJk4OJ&soR5qE?4EzH zw^n|>W`C;u;zy{(S0^^gF%WqT3Ei0mYvw@-?fscre0P&rx+{{9h#>u&}d2J|B}Gf5F_-l_~DNePT@2`uaHYE(UKzE(URGY;X5 z{)fi4Rlk$rt9<$aT8?pHCDD-dt?}0u@6Q>BEdZ?1_d(IP3>5zkS zjriFrfFK1Dqmd{Z)6(YE2X(yjRmUaSjp|9%e-2aD{h=3Y9=|+57fe-O zVjny9Z;Fl81DO-Xhi72h2@Lc4-7aRp>uLx0>Mhz}ACd zBoWQ`$?7yPo17qrO+vpjP1)rnW<&qXrH~fhX5Qme3Zfp#v=Xq4K0p<~lg>W*ABCxQ zYg+|I)mFl` z9$|<|XpOu}0RnC&w=K;tw8|hdCo%cxZ)zZEb+fIF==j|~8F=B*iKO|(HBxZie<=4J z`D4@&c^m#O>+zebqtaXH)2zp(FCO9L!$X$~_f0F7CkLJnoAH11G|OU~BH01D6AzRma3nNw3;)vUUl-e1u%? zF;F5~9^n?=yU6|StCw}#OA;T!Her{X*=>V17T`4olvLI3n}@aYmG}+RWb6KlvzemO ztbfQ=z?J5GgQM)}Xld8j2F_!}U{S}P%X?ds22?zGnm5@n@Ov8qW+y1bokgeri~DcJ zOKzC~m@J#kW&5u^y6>i|Z&1mrMV=dux1o8DXQKIcj(0t?di6dKKgIQ$Vpj^Iz|L-3 zl?7Tkl+c>n-@gl1d00~x5UV%HB}!CUUXRbZDzH08O!&V0Hon3jp7wJS-(KslM{wbs z5Fr@Yd0@0M^my6oj3^kY{%ck*--3BO+J;oc7y0Jl0Y|}k^c%txj98G1Tuk5pkS(JS zvnYq!z{BL#uWyCb1OZfN#q2gB09^)U%m~{B@@K@tU6>O)1E$l2>T8wsj(+&-7SJav=K{v4}ZOG)3C~yB8B{ z>;xx#R0xYW`Cnm8`%DfIz@qW3)mSOm!qt*$jkE2Mb=ykRTzfHkFezh~o%@8LIT%VH z^9(aCTvt?7^c^uCxs0K0z%`&*KvkSH9&P#10fo7^{rO2P-t5B#-K&l?YR9!z_wazE z^ecR$FBm{AX<0cDEmI97S&Dm<2yk*qjV!B4fEwCaSrbB8XI*qhNWF!OzEifJ4G|}4 z&?fsD*;K&U!vFDN+eTX@jw~zF?h!cD+bY_TYAR^?)*I8Zv86Bbk`eyg6>PUEvR*S6 z>(Rv_OZYQaD6s9r`8crOZQ~er0jc^c;pArilUly_Wo*`3FSQyWPFUiVYCD;KnfIU9 zS)xL*Qhq`TM%)3E5tivVc(|DoO&*HT%8ypIhDSnU#@IGZ?@2!kz8#oJBN&K~hS(o5 zocww-LJhm(k>K?HCCnZ5G>q}&@K-n<0|4Y3Fim?Epvam5f$@Y7il=M<{?hWtD10j< zE>rDlEhPRBmfJS-)-~X)*JEcj<|A5Em~3Ch>F_icPx`A_Lo2IMKOk%_`6@sE4IWYY zi#a*@J|k0Y-#JEVwz5iw6FzfV=Sg8whYUOB1wmpB9KhSDvrWYSPSIr0nE1T>PR)1R z1yRHU{;U$a1m8=T34kQV8E8*5WbG3Yc{lPWX|F|tFv@tp>~&oydLR5s8(j3=LBW)d zW0*erFp(#>e^t_zU1qDU-_6#SpheQp#V)8M*WZLr8|wb8H#W7ZbmTKW`@qoACXCUL z9WSI=99cOBJ&SqL3C?2#yt$46m)+Nz?Y2A*4i z#iV1Od^~KPFUXYJBxQDl+>gYsxNKa|a52Zyw~hxEZ6euG3%MRk@6v*9*sq=jikSgU zfe$yh|ICofp-4HT7sPQNU1m-f=iATS2fc+nUWf)xZ+u+q^iCYfw~(nBwGac&Qza!o zqTeK{30CmQzhx9OpPUN2WjaP7M_HC(4DOx|;hm`Ad;ONb;7u?jWP`!1YFtC94JX1~ z`mrr1?f%p(2zikyok61NwDIg{|6T+B3mE|c#|yM)_ur?Syg^x}+Xh3)C+@q!8Rbcc zh2Jw!V7JlpVePy8d+=h~n+SmHrrRIjR+HZcFdnO=sUjnajycVu5d?$EF`|fK7P~+t zs7-`&4}}WB6P{NO@l7Y_JDM_++uug#%<4(K^vKntK;keeB5CT1Z@601@|##d!GjcV z$VFN0@~@!JetgeDs)4kj_aXixM<(VO9kc_>@rw)f-1`sjp3q)xBubvo)ag|@o-0L|?fE38hYbN;&fnaI9AJo`+R~WEukpuo?jz4wt-f2+14PqoB0K0&4+x>ql_@x(%g;O9ugE_y=P?%%rFA)RWtlY;>{}7c zT9OZjd=896kbMo{kARRbO0R+X)x7>?RgLh>_kbq9ZtM;B6r|V{AO&ZK5Y3s-)_}zS5T(+Vm z_cbAZ0#3ScJ1Ys=oyiNQZB^t0k$?}}AyUDVSSyQ%kQUa!noO(QDmHy9OIi-VoHIBZ z{~5crdWFe>EB9@TA~N2m8rY=TzcQZ@+7MibY+=JfvrLm*k7jHPgIFcAdJF5#nDT z%q+eYvWI>t)l2mbFpRq~7=&TIO((g+QTfQbgVm;D*g4cbsBTw*>uZr;k9ooHdC4TWS2g*@ zc6A-gp+(^d7gMJD*dn)|e&~O9cP{%#)oB^3_&l@0AiEOWhuNqKF!v1L93zSQFui&r z#u0BJ1i`IjUjFu7LGDUUK%1e#erD(@c7)dC#k6+Nr}B1A4|-6FVqIquEZxbY<4I~CW!Zg7lvb04j0x&z!KT~P;*l@S*oTCXUPl_e|Bo*(Fo|uU5GBxo` z1MrLNHGPW3u$s>>jwUv3g4~%Bc9lKGz*@9HS1YCaF5r3DErAu;i=q}rhZVrDOvT8) zb*=)EqiR^W=a{MB?!ux0!6x}O*}g@VHWz)8E3&fPpv2pA;w&FsV~~%E-Q&$WI)O+d zT<%$P8u4j|-^6p~va?*6NggWU?T1sikbJqam8}_Jc+EKTuyZl%YGq=aFRcBMB}t@# zAKidJw~vJ*6RzqtC0wA^B8u9AmCxDuuie3~AGNH&U6@x7^b@L?kc8Is%hT*$l!P_v zomapACKx=)e1D)0?Sj=qhLa~eu8Kbj)B-zDJUabzZOHfV{?_f>lO_dl+T@#=h(5{EaOtRexO_g=ASFd;<@-E9lf= zToa|=#E*W0-01!XISmpDOq3f23f=lZxzn~;_}^li`=M4urBiX*YZt=|un)?@X}04T zucH}JmGdBvVa5O|Z&E+MzKX;+;i=I#pe zn+1_X{8BXG6kY_EQohWzM(@EZ5AkB)#~Io!q{Emzhv;SZAzL>c+ZUdNdx%}?U-Ngl z1u-yv$ve6PiG^L|`$cv6zO~3V+VY$qIfqfubyA&y4=XGv^FZ0S&r&VeXd@|NQX5vAsd=u%71J zdwP%HwaN=pl;3}d;U)Iv3fC<^5YGg0xO09czw&U6Zkk5tZ{ffiD~;8iI&aM12?WI1mM&kXYd*xA&+;PIa;Z*GHy|PVErua}RJm8#dK+Nc!|E-;*Q}rS+9V zAig)9oMpzn;E#f%o01YB6qwc`6lyr1u7r^BKW#=qdsXks&Gn2%);GWtz15R;fpMnC zxBOx2IJD9jpP&4HT)kyfTi+KpSX^7IxCMve?gT0ByU*5VKaGV1FTm@b%6Yps#~r2T6>&&yQt z2WwCKfxXlRwkYAF`&QqJ##7d1 z8wGhP9Kd`ipd(xeg@}}pD>{}`5nEB0un$$_KQ8tsWk-1cjwW^lj|y9{yjcmAvmoOJwX+g!Ls28SFv4=fkWRv`vBXI{#S3Ad}f&wDE{18>_c zhXnBDPuOYiyzT;$--P_kS11O=aD|}{9r2bw1mhOR?@`~-TL)@zTeja5K(c_}`gk|c z3Ag^4fcF2=zuAMB!HzP5X#PBy8Yf01M2ze71#>C@b5`jF<^b%8|q?0cIXs&k#liULC1$P8bU}~lv zycesAkE72yFNc~#!T(PxT!#bb%G%f+HvY@@>lZY&`dIqOcb^l8E2Epmb=Q*RnNAw6 zV5Ohlx5sQ@Ej-=cdkv^#acgkNf4Ia8&TeVr3zQsWbSLyI6MP(<`G_MECGSVa@-Wm1 zzxp_?>MO}%3SYmdH8hfK0ix>u#8SK$;;nAl@sV5p{;zpEww^$pg7^jTjHQ#RV3XBv z+*`jj>nuG}(3l_O8%=G^*J3}*I!1K?ktB{?92Qc@Ba`Q-Vq9>Mb`CF<4l*iC-Kgr7 zVBqlj=KU4BzxLG`I{FEh{7o{46%m)wD|MRk=Z=eH3!ldjHV1v) z_UWLKzgVvy=1t~r2v@r)?sm?+vCdAqKVX^BZoZ~xdUBZ-xTNlNO9@a*!-Mm}#KU|RtZw`CF%o?-3i|jc z-OXsdjQ|$$eHYF%)^XHpO008-^8R$gw^~(65m0StZ0Rb`3N(BI$>6fP=Uf)Ftj&+y z`mS6Fu=D?%cipg;8MUVzX{=jhwq>9=<8EPmVxDAgzVb^Gin_LGKBB<{DiNX)`{sJ_ zIJ!gcz7u^#z5^*o+v8QTb1xA17Iji}`uoG%H@3}UJ^mK!IadTS4a2us*+`j4dArvO zi|4^rb*K}AFID*?d$Ze_`Y7+ir>(6u6wz=noW5H>@}C!XR+T4G`wC-gvUAtK%BXIL z>V55DnamSJPD1!*l@rv|1o!oZPHx7**?{NMM|JIUzIbt9WR!1d6LK9`*3_~0`pC?X zI0jAjB4lLv2q($kOK7o;XQ-xsw8mXz0{vtN^0dBK{Je2+Duj^@4a^Lpe~Fxno4|M6 znB*Ko>`(0N+Xw$#k)}qSCa)%bX-vwdQMi*{TJ;mqEaLwoItEm#<4)}EqeAUiYc9|2{ai?n%(qrTDxSH-yLDZg*-5l<@pk z=q#2v{V+N_UQb(-H)LzFc(G$^{Am3G76pUFfxM!0;}Cw_x*UTLc^UV;)(}gDJDifb}gX3y@#5W9h(|%B#iRg(dJjh5YU)xfKz#c3sqS`|% zjhw+J)mV8qy{dRJ#))xgT*@_F&-5=|3$wUK*g>2+4?Luu0Qp_Coy=)4C|;T7ppUa^ zBR~Ic7C6~J|8Km^y2)7!`SbwF(^_d2K1kmh*HNaD8ez&6mv?F9ymsw3IlD3^13f-~ zLabUK!529Tw*O!~P# zwUEPvE`Bv+?GfcQpL$u)(;1!c{zxF)*nXZwrPZy4PfJcHnw=PZ2jzJ~ZydpCj|PbQ zsXsvA+Ra({y4uAW$|wy!qd zX?rlDH%S=#8F6kOnAk0hmR5k5XN`xMejJi_s82PJXPvS^l=0?+#8!k$jd#CT2E~h$ zB9LGk_U{}a6!!6JcLQ3`mco|Tl+c0*Yc!j?5^Q~Twu<_{PNzKCl6HN{DbPXbuPyzn zb8>S91%+hNdgAU3Kr-Qfu*n;p=0h@~I6^A+9;-DT1yn_6{V;zVReX_-f+)D+5VdJd z-k|f-u028&KEP>S5OTx9tHws#^kxqkbeu)YM3=NH zYh|REA2~&P3oY|5Rjg7nkUc-M)Ue`<$s3Qu(0D$){4>LY!ykk6Yr)xnBjfll-A4)e zYx0cQr=`|~ctii&ce+zx91sfr`j?WE*l_MJlu%*gM{<6eSlGvC@pmGUI~hWA3GHih z;Us>l>_d0G-<=4z8DwX|#h`$NC~=)(+Tw5i1ROmsme}zjpQBz`<$ro!9KHv{;SvA0 zW^P)GA|{(W)%$47yT#bk3P2s^)xmVBdY|ArGe#&qBhK2br&r_er-ZR%snn=IzMz!G zi$lAsl?zQ4Zjz~KV`UwfO%UBs!L*oJr{ormQg9@RtMXnp?8;p!VSajx9&VU^l!prN!}z~O3>^BP2SE=rcY?bG%m5C zdHO*E@y@W?#Kx3iDYHWW74{F&Lj`rLE^SueZQTt`ZH_>9^-}O&fVqyz(uJK^F zU#MUTX0?Yx*G*45lPTE*QD&m=r&X~N<^lhP3!a&L-NZ}d>2^9Pk-lfJheY2_&h;;| ziMPE3piiAh&zO7g;Qu=dz}#!}A0F@?c+@f(5OAArvf$Y8PJ5;s0HM%_)Xe9 z4)Mi&d*en>Ymwo&NRypx$P!+(-g6(rzOr|>LVTk^w4_bHm#=# zf~ORKRck9?L&YgbG?vybV0bP_;kXyfiX7ekc%q%UhUL+x4ezU)X{iD!JjID3b@{zh zGK9(s0@T(pc{^YuOFVxvtl!?lM|srk8L97fRJHvy0%i-~EIT)fN3f(|DQTjaw4I8O zzL}ffW}vUdXGuk3o=D`mFK?vIbl!Q$bz>Je{%T2C8kNxuG>^Pj$i5XyLnby{N6W-U zZ2jF%-^!O2u5(4ky2YVNfFi4d5EVABnLLd_|vn+$wfS5-&zD?Yde@; zinrqt3QXTfQp_>{Z#SV%N@>08n)P>1+2SmM<+l%@sNEbh zb%XW3|gmYYxctUi{4;gp*n2wa0oagR!Dy>OR=*CF*p1Zdfc?1Yc>-(sv$B zX8Ar2K8xE|X2P#0uXs{?L%V)r95O@)0`GSRvD$MMfV}H?0xc$*gA7YB;ApcZwGKIv+dCsIanTj$)40CC^f`Y975?ix=zP0!eBbi`*ua?ge~o)H8X zC_B>5dpC1sQv5RepT3HxxVa!rni`xNen(z#4_>=k&hMLhy3#h4!-aU_5@oqasQxis z=owomiJ^2BOOLpeN^8*>@aHrQP3C;;7iA-C5HSKCoptq+WaYjS{FJaRceH zs*A(oC5O5$TVmba`e3ybs?T7IILxo4zGF7!k`mSQ3>HWII5wELEuPq-^ zL1GiYyQP;=LG`IW8c$e3sp}a3UR|d{!Qq zVeZ3k%5m(45EU!Ft&B;7^M)ySs2bi>Z_vcfNo4?w|K2`4D3DoPSaK=eF4Gf=YiQ$N z{^a74XeL#yTJ_hN)~e=fNAsjh6kTQ7V%+|kZJ54DY*`47Uc#c5GNYK;Us{Q1Tt-wE z$~6Zkr;r$Tv&2jA$g1$?#WtW|3kP>Nrz=piz%=A%va@q6)id}T>6W`iB#XM3TEfq? z9&4Qwqb7~A=;TrX1gd=Jt8A=Rbj1Y6M898E@^)K?bf3jB#BesUU=ynq2mpLW;_k>b zh)?e2rP}Y`&t3|+MF0ltW!FAY^7oAMO*%CKzv2kACfwb*UxX|m#2^c zx2rd?On}+G&*<*6wfGNJu~-5@)P}x>*W-J-lKyXdv>F^VNrzs5bpPZmlJI^s0+ zXr?~FITvbBdY|6HQ*QS5Q+ibq>B2jZWBcj`LI7_15di`{!fv%X>HcOy290On$!x4F z27xvf*T=ml>8oz7Wz{S_-wPg>Zi@$jta;x1*)``COa`x;8BMh5?yl>!;iA5CDbYyj z2`TP{%nM)hW83Qkg8tzv=WI^c|yM+{T6LL>JyJ)4J#TWs5 zccJ$?~yMd4Qqu~=k|Qnq$24FQYKB%7rZ*`Jbi<>tl9xd+g!)-Amm%g zO&)fcMv)5iUT-F)W{(65;POv2CIV6%Lpq9S+i#(L1a$XcU_smUV2W}X{jS065IweK0c*a9|!b1nH6k6FOWt`l@ml|@qTGr z$d`C0;EC~Jk@$B`b^S4{&ef&Ss==D=;puGA%IGNJ2u1+2Z{37nyiomtLHB|M`4@Ma zALYo`Kis}@jchdd=s_r3OaSq~!fnZ14sz~@RyqPkdC+FiG z_z4>Q7o>w1Ko4zP_y#ZJsio$dOu>~m0t;ataC2gw*$F+nsKwyT(1^XaKPjgag*0^T z_VcPB?=LP?UDZ7{-MzL!FG)?zm}}Gm)yKAE@sn`mo!iiFGR4kM-Mf#s>OO`XJbR;oAunb2|^gJ>}cA$}OoM zBTZGEcX{eBxxxE{+dq>bJkmRVv?rPuH-NY!m1oFTQ}oZ;0BV>Zb0kdDa_gdl{&6Z5 zdceA$-QaXBNzL@Ewg0ZsfF|MKId{#x@)PK$4PwddV~6oVedn9_ut^^x8}Lq_#{7jK zBvVN_ZzuLP0X7KpRNgWFNm~}$*$q^f8G^`NfKUh@)0Cs}7fTM)5YSwv#-5S8gBe3J z#aw~y@lEh8w92pUW6p~ki7;N+sseG|eI|_okg=jFQm@Yahf9kr+G<&U+}po-OO6si zttZQZpl{b6-Wjq)?(=$A;9N+4XxWMcqL@ro4u%Qh9VY0XQuXB=K&9v zGM!Qn|64v=l4+Xi+Pw9`itV6ZSyxAG)z4aCQ=ysDe>(*(*H!eNdY9Mjsx?1hQ zPnTB{()-0w&#?D(BcAQ!+xLmO*LSupYIaR>Mmjj!hPNc>=-K_o9}d4dY{jj~HS!!q z;#XsbS*2nLry~X?&0)Xi>`!pHNVAkiF=jd&{;Q7m{>tUta-o3p1}e9_(qc~#5#Kl` zSS=C^pDCZE2cMlCRUwZcv*#&CLNSW5iHKj-fGwYQ8;>k}zkm9E$FF2IOtitlL7{!> zHP*3oYpKyxc4+4?N5$N89Ja!|-{&gEn_V8DizdkyuYAiJaTjMaHtr)I;{G8kKOb%I zs7u3$K(L|sgmFF1yRcoP5tQB6(~E6`R%yjP>zT~YEFgxB%5+TrLD4W-wt_d<)(OAb z*^xHY`H7)l?EOo$f>E-X`5)iwF_ax53(e_HDP(xl$h* z?+~}K-Vr*T6MQMR)}0EJ2v}*Nouq?7?s2Ekfip3qT3`s#7B z^+`UDMj0lT9`ohu%Nv~S$m&4ay%^A2YL^&&>J~BAaTtjWfOCSuZclcR?V-9z5*7Yh zJCG%nJv?e1W?$}rf-IUxybqUh-?F%lc;IdXTJ@i5InOG;+4>g}+dd+2&HOf{oL!wJ z!<=TJ`$v7ywm?wPVJYkt^UmBRWC(Jc2ElQU0L|$ta-Go7~Yu?o; z7}0iWi8upTp09LB!gs(hlYq%UU{=UcKd zXC@6(bFxXS&Su!UK_`!wo+L ziC3e4+=L!whTQDc^!L;M>`Ow6MrDw=YISl`SrjOkzd*`(21>m^vaH_(Sp+~YI(UMQ zIL-Hr{AJDtKDAii6~^&sCHXxnFq+LTD~lsCt;hG)ARx4dNT=D54T6r7^$ShvMVHO; z&GE2V40^cO!;{OAoU>$&EBwpaMaLrW!bp3^3}n>0eOzwhw~(LRfoz+fcSEeG&pz;i zQJaHBbtOG~Nq45=icAm&b@2HcUliYOty#z;^s1`W5VRe(svJ{$w=(2G9RDt?&U*6P zYe!SGA7VQ+gnXWS807Z(IgVXV8!!mml)fAz(VqXq zI^CppA#D3H!ZR;MY=q-l;6kAgRv7c2G~_bwX>d_ZS9wLZ5u!339kf8H_IZ+W1e+H; zK)#_vf%fysqDju-zzI}|T4B`2U%4ul;UkN?G`{!?@|84rTa~Sb!e$lo?eeVP_mGu> zfl{SemdqgDv$Y1M*AG>yCXF=Iu?P|^F^03121(PAIB>|@t->Ih7rn{4?L<6L>lmME z*yp;l5}=3{Ck$4ov*r7#wH^&n&rF>7e!IgvDcSdXje8rDmn^DNJ!wI_2FKcAVR$W% zm4p#J>?BM4&&tmLVSNru^6oL^fpR?3XkbWLWEHkZ1l z64@=~TgO&;uO-^#OIgJcUpEX*54%?Y@r(UcFeQ)B9`>$xV&=T{s4kWHifzL+6An-wEsudynUp6F+O<9)j7EctvEFO`2sdA}~IH)dqkCR&neP!rjg z>{eiMuTdI6|B)bqm|tE8*faUm94%5WOc2sNfG>ZnJHZpg=< z^YPAqqov-;w|&(UM-ozHK1RTHz4~4+1O9%sE1-03JsyHPevZ3mv!@2T6Im|sqk6)I zIF+S@_JM!RL^xL%x)g(py3JNXCV(o>niSAM0s}=LhQrg90lIA!J3aDU&j))_akk13 z_uZl~t_f)2zVnFNe`ni< zf-n=pYb<)-Z;f7}bq0c*FDNzWyic0Z1|O2g^m_FKh3p@dm4T5tK=s~Ur?_hmegliJ zts~%-;F$1OriR#rIDu^y1PHpJ-}ke%cTdj~Ca_M+bpO}S4$Fa-b^Sio`@->4mA-Y; z`p7-WKaJfZjp)Nlz?%CJSLZ$Oq9>`u92!ZkfH>6vJs7xgJl(R?u-0BoO6}C&5`bjkhHZ}75P-5bli-lxqh&B0>PPQ=GwGR>IPHm1OOoZInQpg z`kOxLJ4lY8j=dkxz>S;E+kqw+#ti|kay0*B;Dq4pqzuxZa&_C6ds52{{vD|12Agdv zAxHF74Zse>{D#6q#NfRUgnvbOf^o)$cs|Z8R}^`^;(YC*E*G*~8liB_auVl@a?L?F z7y>siOWn9W0FgXUz+X$8otG7hBBz~ZAsst;N9(87+#Dg%2gRNfEug0kh#zH=^US1p zY><}>#;CxB{{_|zZ|5O~*?tjzl4s{{@ua_ZWsl&kaW}L~jE&b`{c-TXf?)lC`y?Q@ zODH}e)}~F`hbPsFfwc2$1hOWnU9@sXfu!KZ%=i4F7zo<|62y_aHXVDy{*O zqGq($2_g{$>%E8(&Rz`kJ=1)FkU#JXob1NYsfq#bNPzifw_giJOlLODy9}srCB(PU zqbHnzh2)4(uU&GDv3sDM|*N*rT#V zVqN|~)clHS!#?qf&LjD<_7^u_PuVSx1uO}v<_6CJMSIImQ;Tb9 zzRk!47t1S#r2N)*7+)R(ac&~HxJH65+izwC#WUdtaIloC36lKJD`_Bf7g7s(qYSM& zPZ!~i;ArBPbfUYvynIo#LT^4~`ww4{gU<7WBM36iSA?A;$o@uXV>u#AFa*|oa|VKE z-2Po`XOO<=U(@!pr@L^@kcQ-x+Qe_q#H&WigI0?5DN+N1mu`%8BiG*A<-6XzsL+2XPA;+}2g^?shfL=rDt zu4L=EVd38H&o=pL1V`1TqN7p#VExfB>EEh&FuzseHX zU?zRnPea(%05gUzyds-qZy74kUSLm<{g$;8rxhrLJ7iaR4^*|CgT;+%}0!!5O> zxNTJUWEFBrM;`Y+X(r_Oit#*Gr-TZla$#u1mjq`8BMOp0_shV_)|#wKk)srP~XB^K_-%*Kh-#PTiC^Mh=Z+ z^o62jV&qo@N~hn_*EZ>dI`859lnzITW*`Hug{vLS?<-E}56u)f+R zM#jv0xvg3Pm1qRI$B_g&rn+d@9hCL_9bBT|mG?Vh$LTv`W3370>s{oaP2<;ELdE?8 z#ro$90Dgnd_Ri!5g%pU3>sM7_24}kfnRn zC8)IEk|hM-VPmhv8%Dcm?lbU2|IGmOnU}v#eP`p>BZ}=2HWD1eg6AB@k7a4)ntCQs z{fS>hggAp=sC#K0^A4pa+Qc}XPOjlhj!>s`yEsQJ!C5956iDaWm%* z^O3h^x@g`{LtJluMQ|kTi}Foru88k`>)dmajFGOJ>kTPjn%qF-S9>wE1=CG6YQ@WQB4Aw@KFjTnjltu>a>gnDyj6oDBf zu+$0v@2gZWl*iC3^GDj~4jJy#zn7%#j9f6Mu=F~Q|5_S?^tpb$``kYjux&h+>(BWm zHt^5s^Gnx(RbYc~<-6_A{o5PE!6%GPleXi%C!Ejm!=jN$XL4r6H#uMyMsiYkx)=Ycp} zV*Qp>LkKo=S8!IFB#wNH`v^^!?!5h!WE4@cNy@Ngj#rVJUi7Yf&(kXrTngVKk()(& z8yQO6w;TWOue5`u>&$okc@dGX+pA;jL9s{t8RTj=xQtD{tisTr7;zuUyogFQ4qR@x z^`zSN4^s4UNR;?hUj5A*^}>1W?}dsZ$W;a2H1C^!fooPikHAFY?)B85E^+!*k|9Rb zfmtx}s=5rNq=5N%2I8E93_wJt$WXcddU^H#|Gi*1>lyj2kAQz^*TXP7T&*?JYFJAd z0v?JztQmBOWHI!&MUF`fSD%!;<9{2oSwkF(5^Fqs{X4hxmXY%Ye_~|()I>=Xro#aW zm~kOCzH@4zr}^*DBW;}1goQsUmUB4!WrJ1 zMOkB=m=GG?vwLf*%lg^#{E)*Jc9{AVcSjPtcz;~AoA0@U%$4NJRgtC zt70x+`btZD(vUB+e}ZQ?%JP56z>nexkEJd5y2#h5MgM6(Kb$M8ZjAf$mP&ThaUTsK zu5^SZKhQVLEJd~ao|wq5?(BYkowk?E;P-1Ij)Q}A#+8jBTaa_YiEJ^k8`HI)-w_Zlk{X>BK)Xw}?x;de2hVY0H9D{jeeTi;B~`TH z4HU$yb}zTm$dj-JWTl3~gj5xrIUfpp#< zxhb&Nt@)fIT^J{Y6ifM%&Oby}RXdjZ zj(iVB4KFEEPOtY$6Ks&Hq#2nig<&F<5Z6+Nm%~iO!V>R*?Z!oAduB3-+*Q$P+sN3# zpb9C(+O?Ws`$i5G%zyyd=7IQ5(_*P`KD+R~-2>TnKIL@@*xf2cx^*J4pg-JbcR#v) z-5cT>+`hgtQ2b59xnsURyM}aKhgZ?#c@cN1uerVf4R#0Yi&XYTyDdA7KNY>N6qckp zzAkIPcbi9SK7iF^-zm=`eT!jub#{m4cS_>2)Gz`=P^BeeCg4x)r|yXVL?6sA z*)KPQ-BQml_AeJ9)O$|vMdY@2Z9~zuFw#WlER9034DWpszYJxEjcqZ^%JlQu|9MQ(D%2t0g&trt+ z&%AHP@4{6wXE;W-p4F2nmUW0IzdKF2>2ULBnl}9HmBu2%rESvaz=g zeH?9d%)&cWlprIiAJ8%pt?Nzp^O*kr*fKkWOZUtg=x;WuvXAIV!9L;yo1>UPYdLRF z(-Y}(4t|k;c5}(y?Ver(;nBPa`Mf~S?fm0T+1tK5W&P#sFan9m<2pvaEYf|p0{C)+ zEXfY8OU>s0Md&06H7dN|_uLaya?TTPUVVOcY4TCh8Iox<&BntR$BKFyG;n$v) zmt@QbmeRr*7nN%{o1M15*u0eY-*z=LSopg)zmQLlwx1tg-^7>pH2xMySfKU3+wp(r zs^bb?H1b~kb}dbxi!p+JcaKHM5*X*&0e|Nw?6#UXvr^?UA@*$M7{M)he@>|?x>k*M z{dG5vn~?Zv8Phs~o27Zf>(feuC;0+;Yp<6&xlN+c+Y17TgKlTNTBhKA!2-=$=Q#JB zyG2WRZ{=3l_2^6@ZPw4+d^IMLqFmgRsa8XwXIi@j|EJtW>P$ALqy={#PIT=Pd0rqb{4 z15|%@EpA8dEpr;z@YQ4x^x&c+TulVf7`iG&FslVDA+a%-V$6JolXMjzhlXFK*VtRe ztSN^4n(L*i)q&1JXGX{; zRNr&%Qrg(zqP`~}<2+5G+jT&>R(E~~+lzjMjen{#OhZhO7MA#CsWy~}(vEm$jGfF_ zNlAH$*dBBXE+#>y?`0O_y8D`tHED6g5bwLJOP>NyY*McDUhXEO!fa?y1DT?230Y#H zY>f?;uCclth`v2gVtFr^B5t{IjWRdf>pAHba}}xxP3cA1G4DIwXR=4M_3lGfU|7Fz zWdD^3&}<})(h7i8-42k(}JwGDUJMaKe{*$FUztPv;AZoE&L*` zDLXR;79MukhY-Fdw4`zz=L#S*aR0my$r!8G83}>5W`p9yzr8@&BMk+SWJ~DG<1mx& z@pg#U)31n1Qx?Vw?O#rX7tJ2lz$Ans&!JdvEn^}BbY3K(@)*V?<*4xN4d8Ly#yABV)p-c2=u@Y;K@_E9>>2g7%eG$=T9`UdPe> z*Op?<-&YWHrPkV5FH$1V9@f zuTh#54AVP;-SDa`n){;R2J#h!=o$Y9&{m~zy^|wF7xT@G^+N2k+Re;8+WesE^+4+* zNPoI9u9zAJs=hJ~m^!U^A7un>S`TMe=nRcWArreX zxDPxJ7%927RAVz>i{`!uGccg1m+#d9vQcx{mO*~;E-I{Df3Wn{iOHB7C>FYX=){A& z6xu4m=oEZD|0$>uAw?=&R05){x_%N6C*67DkyN@-@S(Xb44vt>^3(FlDajB8B`w$} z&Ml>)fof8}6z$>f_*%DCkZ?&f44_=frN06-iKPIT_}TaXF2J)xA19LC?rRpih>sM} z&I^Y-5|f@OYc+O|g; zJK)RAQm{=1q&c@-qurBxrskoeILA(INeFkf6WqStD--AKFXivjJ+vE0KyUSCmLFs|Usuu=FV=XV zSX%z9BJcMLJf`L2kI~BKW`ayBCc!sv{+u?5qgDW=$ zN!{v)!mP#P@R9rK06F7I)~tWD0&@12P}vibC!>iEP)|PjZ-Bv+D@R-l&XhUYi0|4# z*Lp|PkVmD~^OZ9~KGy12s3>SoZ}13N$N(C@s~-ZW@aT33QCOE;tx@BM>j=?K!BlPSdaMclX)6HW!sk%R2rm3U9pU) zgdUysr(zsXuADV;lF1Pkzx6fCCODxqZtGGNW^it{jbVNp^ZI=n*YxVFy#*mLGh1H< z)3gRi?CX?;5t`$dah>C@p<6xz9jD(r>>Sv+oa3$j*am!l7tLjwEI)EzMQfb1P+>$; zT*PCm;Or2(peA5vp<6#*12}p=hF@n$puK%+grpfrm)-5U6m*qI6F;Ewi1*C?c4()D z;xg@P8zN3>=8xd4-T@#zaM7{cRTI1L~v3w#xwNVKD!jSh6%7EYM&eH z!7BNJfTUJfrr!_H?vdX#Z90zq{iIgy$X5SeYBf8+{sShtzkW>bbQ-}oMXZG@=SyWN zD(I+nF(s(KSod{$^S>l==~&DE`nU%_Iwcwl(2{P9!}PXy<2z`|gVx88+{2$+GPdW3 z%moq0wbK=#5r11~?#z$;n=FnUZdz^sf3EulUB6q90#4i@t+upGyV}g7fZtr~#W^fM;Qa&n}vsB!VB18gYK2MH_^mE2# z;ogYJ7BVv$UTI{r_rzD~*ND}+)hCC3ZMO)45vWrV{W7$R;{zO$o@j*B2YhizD`<*| zwo5-F=%_O^wEs-&A{S%g1`_(?fOW28h-tTLz`mgn=Clg+w3=0mAVXW*E_{96D}?)? z_4e6yi+>!_=;F&1eaZ@y*q0!>mi52nQnk%gCQrv{tWLYOoK*8ml0Q5Dp}j!O zK0nybkn!;70tb(*_v9Ogb-s&=YlIG8J|@=lOsTa%8ptWSQ^s{f795$u# z&Qxj-4X%Is<9eQ9ICe=8L98zq^Oto6+I~58Oyk_clm4f$Sg)RjpVOR{mX5GTS}){2iB)CgjriowAy=w*_sr% zJ_W2_w$7pbvd2Xoz(+VO;wRhjxb*xOeMKf#KP2b*?Yyl!> z>QMk_dl%LtxsqdQ`w6dRtk)kwQt~R!N9XE3T z_m-3nqnW$vQwZh4^`y*Qr$gjkyC?YyxwtE5?#WB58w;NiuEr?v(`&>CNG1(#tIS~X z)YwA>B}uY_VZ2n=X2-y|@-88pg)d$#pQC0j>g+XvfhW@O99R-6^Z+4_p`b#%#DKW5 zWFq5FobQ=lp~T5689%a%E(^jkFwyh5t6n_ReaHu@7d?Gn_`@rafCwVq$+aCo#WXE@jq01hwh)ykIM-^GSq&y7JKm9 zTKdcC0iaZoZdbhTG~d3KucxM)&EcLlR$^tyiZoAu-U|$})#55LGV6DZW(9D6EI#&i=zv+4G`Sj9RdV*cMl|J z@Sq{MLvRLn4-UaKxI2TpLvVKqt^>o&ng8B%?@NDQKkVHvT~(`ARjvBbf;+c2Fxx?^ zguh_pXOH13YbXL*)SACBj92;1yw+;s>(CL)uF_)Si|IYDGU#cTl=Q80o?zVwqgp@v z@!ux-x)(;RVcySw8$&I`2I_&<(hK_$4!jJ}lQWH}D4!lBc+O5wdtZi)C(Y{!<(-Ik z3m0(5eXF^f3JSiq;1Q9mF)wtuV_}6eM&MRyG%0HazHi|ARbACq5^d=r^%Lhxm|bF) zXbjunPX@o_?ZvQ2=z3_Ar>x^_Ffx(c&#Mkp8Km#YnI6&~Gs?O)JdZ5t8Jv_p1_g4D zEq!b%i}2~(j5e%JbimPNpuMj;MNL)~T$20{C5gCIg#XLN9qo3&UH2?BpIgnC5|6!( zYR_xdTDH@0c{bf_cbMxna~p9=ZIIszEjT7=?WmgAZ88#CwSMtL?at!Ox@@h4Xe)M;Ai0-lZ#YXQN{4y0dz`zj0 z@v`+ZAq1+vtbS(dn>941s^c7k7bQ4ul&7OZa~?S+=3#d}>5!gN(4{1S2Zt)_D2PuV zHX5(ksSZCa=Fq}nY|+GkgVlg^iYo7iLGRRm2bLAdUavv;Rq5KC?y zq?oIfupO667mePaY6*|=(CuB12}u}LmVLb;Iv!=Nd%uM;(-)x*lCO4sZLcfPHf)d+ zAl!SO-u+-Ft8vl7&|{B3UA0-?Y%v=Otmact)Cim@p4-F`Uve|`aMGeD4d&n{P;BiB~w9C?Rv`f!_5rzAmNb~agGVe~- zNIz;JuqK3-(K^EU-`Iavau)8L*PZCxGL01*QY0nw^!SdUFa`5qB@ve=sPGNMZLKhb}Zv(DzQQ{23w7YQ|az((VK1VEHDHHM()}QZ%wxwjidX8>FF^ z#(oFcbnfTfPNWze;X}TkIi7s0u=g(KvUji{59CIm6ZVG+e@^VFssyd3AIV7zD7Tj7 z*m)6KQuZGr*w=_U)EDgOQ?yJM-Lv4JG*DPsDZu`vlWa1R#A{&=yUZP>D zg~WfJ@UB=NfGT?jrlvpaMf|~UAb0xT!Sk6PZ>v?3*$-BY5c{RPEk>a(_CZX>fap}6 z&_T7$R($}<*lrKRnJ}=Sd=Xe-qUe+YwbY3G3NOj6cI(@AzHM(#dtf`}{D%*#ueD+* z4^yY0RINm5VF3z}^|mpf_u45h^j1#$NOBjr+krMy;nqv58DMSbMqU`yH0`qe&CMk8j=N zKBB*C;X7{O#JMp3FpcWBJne^jOvrs)sK|i~= zh{kZ|+j}F#`3GdAyE@E%yzpozuDR&YC5nzZN7}i8YU&S6BMTTyls}GWCm zMWaq2>A_ll&k2+f(7DJ4Yx39)kjk~FiD)|3X_W&b*2CSU;ZI7Y2hNx4G0`20FAeMy zPq>zmPz%xLkv76f*<~lc$RzfPAoc10$%B`WDcSH+>T)5a4=VIvD6lWX$AYM6nwOLmu}A|@!}JMXhsV*UW{Q^Y+O9Djf~NT$Z%J+q7x2%##56}VOZ=c z=_spP{kKRE3-6rZ;BQ(=dErQQ=Fdscl|j8lS?x(dC8;L)N}{FOvPvcgaWhuA^j&5X z70SIr5A0XT1k9El+S;(NRck=5D%+^!nF6>C?;~rkNut>GUvq^fNyWcRy4ZSqJ8Y1& zUnBLqOOpF0Y19@PZ0rL!ls9f4G)%_X8u{3ug3bF)O6Ea-!AFSc3?lYNKv7!=an;^v zg1+(h1B5Q{0Ot+-Bn1^s`n1xn{P}3R2-f+t?_9J+n!NJvYD(VCA9d6!9#c$692i!f z$&t+Yc8k&YPx~{6aMOQzD$LFXlgE;9`_(a%NMvAkm!Sxsdj!eWC7*iXET-!njpRG$ z#Hl#Ko?{uFn%ClQ{|?j2B2OeFX})GP?FVK8RGbN5Fng-;GcPn;`#lM1(hmq?Qif`d zs9YI|po-eipk_FX7?aX5UF@$7M1}roYvonk$H!+*WS zeSFMQg~?tVw*gM>J)1ZNMzz|Ay)YarHiAtZz=6!TQvkD?I-D&K5l)HZ&V3%kqwVa^ zg-seehfiH$e&Ke)4KEx5e`9KyF&DKtdLoUP6yEGd*pLJDOd8d1yB+K7x){i4>=ttCy`>1OSmQxJNb|3<2czN* zEL8#Pi{g+D4s%pFRy;~PLk2i?ruDy59@v9uANz^un-B_vi5Q9czC$5%d~n2FZ;o|%2U{CTLw{dtR|~`%dREOc^-b>UlWr01 z`_n(^q1QoEu;(AwKDEJ^lchd4Xg*}Sqw(0bKj4e%qsG9X4$QXs3<@5VYX&$86 zwV*IODnU!3k2!Mx08#HnUxVp^9jHpUKmo*byqqHbe{Vwt|B>y*3VF( zccovY_0GZesw}OJo`F>Ans3W2aH@qa0w8gnT=hUW! zDKpCqJ#Cjyga;mL)5g!diKckv0tYbMo6^>OAdYa;N474ROv%92RvmkYT#s;0`|TTV zO56}S3gKg;i;u5wKiU}GX=fePJV#%sh@Nf~f!niZbQ0j+0|g=QJ31l3htY%mjAR_M z!7llT08+aVy~T+a+;pv+R9s7W*SLnRcS-X7Vc)U#eXW}Zz?7awJEar>3|S|J^8!Q{ zqOa>hE|G{yF|o1aao1#4r_^+KR0mdyBN~i*A3sgsjIEy?(=XpdW&$o`4Gm?~1$gVm zW(lW17Q4Sk`oMtDac?>|exfq1@#9%9fXYsVj_(keA|4n5x4wfd6bIfsyCHtEI9Io#iNKe}WO=}gqAaMjt>g)|MWy-Ko_r$7CE z!4)R0&wzF>FE@&0fM=9R*v@c5Mif#IUE_U8Qs-~e;XIeX3p>=#+aJK+_S;*G0=}<~ z{{AD+xyFs4Orac=hfU(uSQZ=VA8DQCKiqZRSC_MfM}lT(8n4S*oiiIhJd);%4u zD_8LCdGl2G(_dcmFQ!+3A9K3?chOGC*vOLAPe4mdErZq{LJnkG zlZv?^!v?O4>ykXeX+JMElOE$Q4e^*TFuNEM)&{r?LoF5Hu+in3Se0*zK zp-1DAB#ld=pMBRnC|iR!9KpXzbOaYk3NLWB&| zPI_%a+H6<~DVLIC0ufWG4t|%6iwr^|fl3rEDGxMrLs;jbi)KEl;*QmGBRb3>A^sKF zrpnBY?Q;`LbOz)}8Hvgycye`3??8i&c17JBv{glEEFGh2JgC|TXIQv&1X6O}ukg}2 zzon?)+iQ$Qu~=l|Dp9|jI{peHEE@G%MH_slC(P$V>}y_$J%QCgi`}0a$-Xu0O`%DH@is7<2TVl7zE{5DUeK$$4ZuP1mW`=j&ia)~rJVE4KyLn1 zDp6vZ<&oIcqsjAO3b~=#6_8(Smk6aoX zg`M*%OJn-=++A|K{R1NGx+S@?J49Lh0gk^cXh;L=cgO>@wdUpL%Z`;q;`HlA#4`zT z_h;^llqCOmXe9uZZZFZJL)fa5TF|DtuyBx zvVBd3)4btdgHSf^6e_P_gn4|x0n2I9lmB;p)znpngE;8RBB1n+LxS_0&qaxbPUe=d zDd;tiy8So-CgyUD3j2=tC#15`3VUe8*fE$hH;)5yG!%Lu;ywOk;&xCd^twfoU+6fyyrh|Vfjl^P${Fa59u^P!ct8{xNr#D|DOJ-JHpudsDs zDJF2PDsS66CZkFHgC;IAy*P9ni*bLT3qXoW{SYgq_`IRGZOzLZ3cATA@2w~b*R%tR zjZv{L?K?RuV&dZ<&QI+K6IVh{e`|dlQbjXV4P$!50#p1J0p5{P8^ao^rYbw264AX5r8;G9nGqsju>tsEmKG^D%!fw6ohT=6<_u z3n+K06vJq`lNMlqZU)-CQTO*2qPYgiMOU1P2S`$;`>xx7{j*k%blK^q1)5$^DWhWQ_I7c9^n@X>Sy=@LyrWofliHB zwNh__{q@?-+{c9YGRn@MN!zMxf-v>lI2Hn=Z}s9)#gWr6>0L7NGzx{jGHiLs^&0J& z$ge15SikJVIVl02?7+ZcX0_^%G8$%64LEf2O*|U)`0}dA3yEO8U>U@rP(wv)ZrYaI z{AxUu+5UZa`kQ5&_(f?Md2%9(rI4X(DF9ip_I!kS5jIb7%uLnE>^PEPNd9qTfHc^(Q7ZMzpV@xl_4XdS~iD#Wrk?HJ7njpyMC8#j>Z#9VtZjh20zB; zk>IlsLAi6FL(wkUp2j8ZeIw5(WE%$SsS)#UkRoTGU4d<_O8yk+#ctSduLqQf#BtM` z$dJ#pd|j{3zbNYxxSg|Aq$W}*1%1?0K0U*1~HRLtd?|Brs>Ifb*#wkoW~h>QD+bBSu6j8Z{Y-iipuTkbUVra*&Mo z&A^WV3aN1uQNtGIT0FK7v_I4vQGVGoI9gJB%rDgi<7P^a(!_{O`LqxjTmA{P2`QFd z)gXJLuoUS@A@7c|pfWcHqg;PiQxrJ{k*!sNWVRCR|;1VsY)FBnJ@M+%(zWvdgwAvKjE{6oLBpne=6wY#yVPVt=B_EXUIfUKc+3rU7aaiA^^JiSq3Dmh0!ocY}KhWseOhZSH zfah${ZJWm_sj~}ly_=aWTiLMBiy7xwTL-ylN@l8N-eoBTC0J$8?E5MWm zR?NTc?z7I*=<`|(R^0RDDic==-*w;ZEXe*oH1{#?B1$xOhz$V!HQ_W;_^8@(8_v3L zxNMU3MHsT^zA?OiEBn)>$Zy2^N(u1L(^~S4-gj>+b6vd)YEtmF?rEbK4&%gk2GueWh`)PL2K{9%@I7n>kH2CFBbucat2SA@MDDI zGo-}8iCoEO?P_L32n;`KBW{e}I;mY-@1 zT+sBjJDf@&6_1d=^_6W_caT=He-lT*oPdW3D?yYpB`mx4?=6EN(2xAG^`uGf zLjiMG@KM`n^fGfjVS9+3ZJ7>8s~BB-DCh52{mWCS-O|6_U-)Qiq&lrORUJ&aHMSFC)2IblHEU+ zJY>h4WyjYv?XFBo(~XcPX-@*RVYp($x+CPY%reaJRyS%(dVNj9)>BDoC+~Zx(1Jng z!G(KGQ}Avx^R33EiXRC%Wr@V5&E~aF&H6%}Ve)&_V(dLB6TN#B6@1E%r+4V~_R692 za&O=|8kq%Ce47!Zn;$M?$nvhS6*|oEx_TFQ2qehJqE;IEg-}-&%i@Wu+Q# zUl{~4Q*T6G5wVUdfDN2oEK+Fl`<3a!*K?SeWbMK=og{TsXq2rji+{HzG2GI#>+<5b z4_74Ud}8=LNJMe45M_v{{Q*_UUSH0eKZ-v#lSYYco@f_ipQg!XdFxvReuF&m`xFio zK?{3v`XDPJSG49rw%fv3jtl~`hTkvlvhj6!7Qy%DGZ=0sYeyGMHewcNKMu)@f$Wjp_D(OR+GTNa#V3a#4qAqNKiDF8vz&K#Q2D zk1@Q>*EW|hxca?ixUwAifOvoPYKa9rHHiStn2ElnfIgO$7-sveP<@J;pbH=*(x6}4 z6@z~5Jw;(_FiO85?xyZ~6HX^V4&zl3CQcz#_o<;|yj^8A`FhBSS*4lkoz~|~pwpMK z@Ub{{Jq23=eku6FJ67(=lfz0Rz}&m?lAZ67bPUKTeTpIA&tXFhW5ko~f{l=v0^MrB zMx1R@Gg?|^8zAMjEHzqrltQ2A4s1wA<|amLekN`WdQq`M+9=)QnhGAoI7`ExXWnkU zZO$T$fFzmJgx^6A801t&{~YeJn``2Icv=Wab=pW>GZ{IK&j6f(YPAC#2ZAmnvu^T! zRP*p^p-iB?0k}ZfW|Z`&&+%kX#Tc;QGw;x57z_;V0kiJZgtvV-t_oH;O*YdWMhe8B zg3p4t!1=eCNZ6B-2>--J0k#v4tlpzd+hFeuf`e+?aY8CejRL?kt8?ia@D!g@RswI?j+pqpfk54 zM2@#LI7e@O?B=3x3;1mL4%vndp+>p#Qn!ovZ~PrHB2Dw1UueE!VAcjD z25C|Zxg#IMTC_TT9-#!lr{RRpUy8p5ugsR*lHCq$`PYuSn9iyf+opcqN?OD+|=zZAUm>K4|j6yhrv5l6W4 z^7gi($L3|Q0~14Z{^YXLcEHn;fs!~5{8B@=BDO;Tl1+3i2BONmR7=ylF$6?^rr1CZ zh|ekqz0!L;#=0uAjg?Y(`@tnHLOwiRq7`Ghmr%QN0wVcq?94(a1G)7B9J>-Y75la{ zouJ+{7d#<{GfwOT8B>Ur=zAHYSW+OW!!p|rt@8aKib}YN!|zj}Mtr=&D&Z-i9D*nH zmoZ7{XX5+4SiDtDN{gMAnX!?V3<9g24#&3+8144Sq|j?`_BzY~kLm7yV#4u7LiKb$ z&%CA?ueCf%m6>lmA3}Mr?^mEgYLM!zf)$0CEY_a~ewx>9nQ>71-ZCxwCD9aKE~t?q zjHRmnUZyI_TLWC4(1m#C=}VI24oS8)Gi0z<>d%meY+xzoRP%mj!q|)TpVsY7_P{{r z2_=%Yrv?}hXaxA#e6)qo>r7aJ4hYfT1vDVy?!7BKQOu~1k%+8TV&1rQ(~o}7-Tr~h z`!)S80X?SqPD~7+HUeMW%dpL1>VdOsY7_;RX&F?P1GRTuh2Rm-)SI@%QNToV5#?aZ zT2mYUvc?;LEdL29`kCvEP;gr%X&nYS$3Sn34u3W7cCbitRVK1fVnV{MnQ8P5$Uq1Y zWCZ%!Ii!XWOH8*Oxlr@upAvCJcs18QFDH|tV!CXm#(Z_-KPDyk8c&`G*S5Lg*>g-+ zx*Q1}dA!m29=oWDxL!@K=gB!rYsdM1H%bm~o0#C_xnHt?6Q;GgBHWr8hj@1HrZEfZ z_P8LBq8lthNS9QEJ^TC3Hujh)EkU>i8+$2ZoGA%)s}a6?D@lUl;oTTa!5G0SU^}NFYp~IAHV{f( z1=aO^^Ddv3#DpllI}cuOfY=|HG>LUV_d5t@Oya=HOP>x2Ja}jFroj7kUFTbp1bdVT z=&e8B$ZettyxpI(uOx~6-xgHzBuNBr#PFi9asUm27fc|l9!REnR~Y9qv87_HWDeG_ z7>!Fxe`Qe*seekSPjpBb9oiL%Aoah?N`}Z&G79?qvNALIW zYND+B4In=qv(4%PfW1}M#5H$;m7EAst6BlEB7RqAPRD_NzzzJZj&G^x#%qw#Ah3=b zqyjEwcNCEr$eQky1!HYDCZ7`~2bn2afp4Edn2?f{Fm>+9^L@_kInX!$H?6c)#(ha7 zad-#$v^c0}5xDNyM4L6C)+^h=0X%{BotU@AYR@@v7@7fpa~mwV|7&TO^y^UDCpGN5 znNvccnjAF?B1S+V1<#?O%UNoKyph~nQMj=W0lxMeUWn^!H@7x4)@tGGf^*YRO`}D! z)ris0zKn(zZs>*0+ru=~+|QnF^3k)&3>6`U=-Xy@-Mf{Dj#lcDHwA*t1sh_+c-1<_ zsHKk^EyYEZOy2+lSXE@P4UA*cO>QIcbrQ6@=HI_FeDl2UIQ2rMV6u;6R~vr18hJq+ z#5xn1oe21cgXdF!J-g2C9a1YrYiHeTN+c5*eKVCUSjNwM%V?}*PC`nl43qeaxn-nO z@=`BnFs$l(3HPlSprVCfgkl!5mkQ3weDO6T6Y zFhip(JyHR!#M}^Nrr#M$)rM+-gawRNoXpRZi&6g&CTNZl%Y3PA+of-1$pL1~2wne( zpZ>J=804xme3kob_8_N1E?u~@QBr|DetCNO*>_~P9KU<|fg+l=RX+)SF)G%h@hUS- z_~e$xnZ!hexkb4@+^{aPYat{}Fjz<$*g>hxe0N+ZdOBm5Zm4_mmod$>Rk zg#bP%;ym1; zN|JG?XIuXBc3VF)lrcv6Ba)ioY{iTwhBEnEs>1 z^6?gezDZpW7v;DWCxD+(uPBIc3;*%__-b7ZreGamUhsAtVz*c^0gb->5jEfay}keo z;I-N*uA0h9v@GnIwqrn-+ug2Q?3wDeFsn&oOE1zprBunQOqFHH`1TE37nod(klY~| zgEfsoM0r{TSuOO6QkQL~q zF$wB=ai#wZ+rkQ?8$$q5y_63S>H3~xAHv&87v?}qXiw+C=<=zg5ohPGWOO01PHU*`1n9Z5!4Zvpip z&ipkv-b%%nrhgzTLrCOeZ^#;1h(l zb?mWet!eGTX`I;d8WZ>9VfpnP!3wqIGkU~TSY=CXDMVQ{M&BX~RF?7}+UeIk5Y}Ih z1OG9diKw>G1T~5?!a&hcb&vyWVa^yeliX9L!$N^PKG!x){K3qYF&O{2KwILk29V5y zbZRckf9$ASBHqd-TW1gN;`qoEGKivQMC0?h18k6s4rwhSFzNikg@UeS zP?Il_OklLrJ~aCNsDqK-z2RXrtH8r%^99pp0cv#xTYNTAGtjdx{B%&~O>9Oij7x(O z2xs0PhnK?q=>ucOfvdh{Sc7({!zp&>iFx}t$3%;fB_X2$W5xK-diyc+cg&h8B+__6 za!5pG^)W-2|0cg+iT`$ceG+}zeCmdj`j%QfW&gf6etPQve4OhxBmNX{m2*WAOP8kZ zS~;`SImFVR$cBHn;gzK2)2(NO$w|=YrAIQX5j#Rl`FVs!t3;)I6>!`+FBFbv zm|f|WKm#87kc%DuI*;3Bt~yWL?JSh+6Lq~@vUk``MMbf{d*ia3AcKZV2mAWUoaOI4gKt#{=+AJb}9v6F{?@s@3OME4qr#X3YH6{m5U|z zpDXV#K))j0iHlMq$tD^2mVj3jdO2%1f-=nEQ*c7hj)jTMHGi@&n*X9iugY?}f}q*~ z`fD26f-!TuBa2;%Oy!p$G3OnKW#PpezZ_|HA|3jRD{D62f}{sU4IjsB%49c7AoLh# zM^TBW+Di$&LtUpRlS?nic;CW{z90vQCxoQ^q|e;ao7>tRs zqJ=Rx^7Tb1cqIC!evTxsS8EG77u<`S-YbdK#72N zlVdXY9Kx;rd{C==t3hQbo@1T8UoKSYf|39>m8rcM?B zT=EqeD;_OFJ8$Fa#1|ttOssf!YA3Hg;m^jB+Qy2eKZ-#7%ZFSt#y@_X!p)1EvRXCr z5RsYIzsCguN2QAi;dXTopBa`9_bYN-Z7WQ6((KTIg$626EZ~?(V#yJcBO3I*?q7S| zZ8_I(61n3nWoa*ASK|*quk#AELCBh;|Ja~>@#r98<~hTTO?Ig5oO!oYzHMmfAP-kH z`ka_$%NRA9UUj(B?>}JU9mNPlWrW{v)TpH{a@2Yz6E3pPi=9YN&CFqV>bwij_F2Xx8!-`H+f<#oS@SJaQoN$+QZuNZW$0Y3UOaH^`A@~@x}Cv)MhZ- zU5(KMxD)>npaM;UX5Rs~*0k@$ITiSeTEUzwwsaRgkVhHFz0X_s>Vss0&lJjym*g1i z`gnhkwjI!D_7*USs8;* zU`HePyjKvg{z`49eLeBy@=CA`_xMh7peTt0y!>~KV1Hd6v}TdTHPD5kCXY$A2&G_1%7L>7AAi` zcVQm%ZgaRUP{7PH4l^y3d|a16Bx`n`u$(&G;VdC|L8PuVHM|z2vf% z4%ML9iVWPl3kxe9FsN#_4}(gYhnHz;ti2Ldym>ffV0s zcGhiZzF)>*V0M7m+NWBt5fFMHRg_0`bY%T2k%;;DRsBd!HNFh}cshc%kB1{$=bDE* z7B7G#BtFP2mg;2x`F*Z%FFKSkRra*y=5`1&ah=q!zeD6v&^LCUzcp#og44=d>oQw}}GzGIQFHo0lpF`8AP^`d=I zP;-cJv<REAbYKB7y&olBHBD-Q|X{2O3monj_x9G83ZzeJ@pj zBrS`EsrwzLmdsu%5rw=+BM6B;95-nipG1As%;yrPXPAYo5<}VoQ`kMCRiB-D*wXc z>ml&1!<{r>DFjV{tchLCN|x}26XYP6VASiOGgOKt8*P8E^B5QGcOb7PcKdq$MdmO+ z&deX9mfhs!jqVQppg>7XKE1uDhdrzwSx{qIV6{p9OEGGmv5;3ll^LUC+Mnc0AJB-< z;;ov9j@RQSh{}i2Lud>As9p(*Wco?*(e2mjd}zy=H;Gbo`%>GH=3-@5G?CC(_P=rN zG)c?-5geN3vZ<=I(YEYwc0DYDVVY0;Q0Y*;yMvr8np=!KoW$`xuwDsL#MqaTw)?6> zH9vW#2*6>&xmqij-L07g;DA22E#wH_bB^T^CeJE^@hp+qq5IsqmzN#2R5W(B_|LJg zLOB&)GWtpYYF%6ch+Vk^FUtc?d*Yx!Nuit1wPZb6FlB^vyXkc3nds_C`2O}3ptFy& z%eD0u=61SoOfsR}X_De4l5r28AP1zHpUIA`RrCC&U$4aS#@kc=^HFXR504#?e>_lh za2iD2z4alYO6JMc;gs`u<2_VgBxBVltkMv&?eI1M{;GlI3cKe%;B87AQ@Ed{rsQs2 z;pJr?036LrgSaXkzbZ~Zzocyepql-U_R#I|Jo1TcvDjvCC*UNH5rVl5*f52%fQ(lm zthPTo1ed5!uQX~Lce1V|=^`fM!3EC$ph{J&{L3aPFC3Ct)jWwu%c}sV5U0CE_FJgJ z^@MT7-J9UR)fV8A6vFX*JU4M{ya2SiMP4K9n&3}>f2$d$=i1RWOKz;z-L`x=#{0c! z)br;d`Vs1(R(MJj5VFJmCJK%iugHbANJ1Sm1 z87mR3u~9B;tjAnfq!Cm;a)9mlcJAl`l9XB(kTy5_xe6P?d!IGc zFvv*=N#HH{1&}R5y}4sYKN^#An6ybn#u7RYRlc{V=8;Uf;`>33B)~n`3)4ziQ%()Q zpnZwL1inu%2Qux7zy@DhzrlUnkkxeUlefx+=oMh`KnHESr z?ZL~0-42txq!s~DiLCLW4GOhP{)<-&uJ1R(Pk_Vxdvb2xHDMaDcM5ro&lUOB5)@u> z#uR?-p|{*WHYQjlB2c37?*=(r0~zPI|0+cxG5;EgMP761V)=SNjW48vY&hYG7$Bw$ z7?E(j;}3j)kMT5}4rtYbs1R9eCsojC-X))sG4hO!+82Wua4SO!qUbqbaow;cZ6;IW zmsVMrW~mW*-Pt3VP0M0G=b)?YAZ~h=vByu3g?T7!U-&1;U;CZUqsMHVmf;2v7G_Yw zM9bJphF$8$MHVo7>6zl+;Uc6}}(ce%Cev<_Hu1um4WK!?FV3Gg2+(LKP#fe8Zi zyf1;<`ZQ0Ofn4C(sq6;G0sR_FlzyQ~d~y8io$HT4ydU(QuMQE*XdA8%uMzD}cu>z7 z7*vXTTehPC_bo|`{3(i|+|zUWrltb*fu3$Hs9|*3 zoj!uLZ=ubvwdc1c&BqBUGf$H2(AT)*%QEQW+sMPw_Lpry)+@DcX!bVrk^`Cpohl;l zxiuAhd6n>AhloB)3XQ*y5(yoqowPn3N~09Gm79J&-*UXi{-eXSe~Y%``Q$G8 zrs8O!s&vK(B}DmchoBRuMqeEUP+I_GS9PlW!_FCLmP`B_az%*dM}Sir=39V6{*lk^ z#u<(X6fJ}&>MD@+FUOviRm?8xvv*iJ5(X@H*l!s?-gS6Zto=uk_b0)nAo1uO+Dz|& z>kh1Jt-(eTwz>HAf+%Y0?9c&hy4^w1!H1=*Zk>(E!YzH0Af2+;=u@Q59am^_2#BJu z;dz~M^499`y3u5Af3VHh#TZDPEaF*E~xOAPMt-ux$$2ozl>3NoF03%%X$0{Q-l&@J)&n|BISvOf7VPC~If zp-fNDs7fArywC+ELDZDY-#|19u^1_*5q~omTCtQ9mm=(t7+RSU47l~7czbJmfVDgo z&1VE;S?@t!4{K7{U;n0P`z!UvIu;j%7=0)6^O}TZypnJSrnnR)?o`@m_;8-h-MKMl zloMizIZcIRpGh$I527eyM8fK${byCejVN=a+RlE-Q5Dfp@pAETPsZHWU4d6?O7T^8 zMBGlzJ3^C24jYLVCJ$M62`vzF5qM)A^U=W^lIP;G&zb}u_7053HDx0Gr|BEpbh%Fd z%0A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 z�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�o%C#`7i?Jy zWg5ksyMKLP$Gm{aiT|y_LA*K*GgjqiS$3=VgLQFOs@+J$VRc%>j2Nz0k9z?_+ zow4z*YT-oE-8%AV_Tz-3DEKl0Th}0sS`gkB+U@}ilkS&T=^v0fp^}=}m&Gov+sZ!e zcOMQwFnZ1CrsMuyxG?UYQjeLzotdnt4*USY4NZ&G&rwh>#;CD)k*)i2w@2q3`X{bP zT6i0&44I|=dfECofIbF#jATIJYkxJhc=t`7c2S_LO^X5NZ+EEle_=CcWu-=qHdULZ zMa5j_Fne#bbvZ9W{D~KEP>EgC6O|-g_FR{B_X#PYIT2f#vT;zi2vDFEv`WcHgQgcb z5zI)k1@S>;LE`{KFVrNX7wo8&aM@kV1!?b2S%IWI-D<0VH)`mi5deBCL+b~y>SJ_fSY5;FoqdcnZ@ zb?ML5Y`4Up8BNMFa8v5aXSUoPVe61=p9vgKZ3dtUPYV5t)h1*Pb6C}T6{Blv{bk-t0=~OEZjfNu9cnyFoJ;MG4T3||gJ6+Hdjll#A56Xbt zYkxT&*>A4N@g^~s{Z6|A@|a;G-O#5}XH8I)g*Ph*?9)geE=9GB?3#{%zWOx`1tVAC zeE@n*^V^oTMZX@m-S+wVpFyD{2nv;J#YS9-i+bufvFcWx$_PcQr*F7IYqeNyyMmrV z!!1_HHWGD@*l#QX!ctl-e7@Gbj4Belj4v_9Z$IB)6w8h5Xb2cSs^RPD0q?|o=^GVvi>7u;p@SnnX|F&&%JhJK^Xuy{bVJ8|j%}-c`~x6Ja@&01 zXP3l?dL>eR34i_mLUd`l~_FlCh)Hm;3CTxy(Sm z&9h>)J*4k%onvK_n04CICHn@nU87gM61B*0=Ohu&w>n<%xH*w~Bl-l_sR9^ecPGp)fKjGUjg3jP28CuRd z&rlk(M~%|lrHl7NhM2_QP*)7J7)RdB@C1U6>I{wUy3DV%C9-`TI$F4~YP~C4rs|Lz zqA*pj>P>9Tatm)l!EuRoO0`TULd$)&M#2rVF)|u@#E$bmdD%GvV;3+YdN-R^I0g=I z*P2eIL~2V6FDlg1_?5lPf#&J1g9C!dKt2dYq=b`9WN>}(BOuc!!F0m}-4c3=@MH*A z6G!ea-)uu~1*}K7O5%L%9QyTNFL~|r&;~=U$mo3!(b}Y^79zE;TOV@uv7JtP8w<{j zIbwD#(DI7ztF8UgT{O7?_#_%PY{CT|sol4*3#RgQ%=DBmpIJrCJh&Tt36!7O zKM(=#-(i%7T59S*>3l0=c2&K0U17PD6ugArp|sX#hDHl-8>qtX+{TWE7cKATVUiMo z({&+vB7S8RM!*JsiQR5HZZs}qxv_i0U}AD;I@lG4Xvc%Mh6Bk+>#;~>xSc)8Sc&&# z$AtY<%5oa?b@}tOM?m*e?>}CrJ?%~nXA(3*Q?wP;K2C0HtG88PJ>KS^AdntQ2Fl_x zuIDnnD$iSDo)ocQk@y|z!!C06^~Ae}5jnckbiFLtEb!X$edTQ4-9rli;)c;CfciL= z^US2XZHSMYah~S(5kGnT!RIDC-H35R?_lf|{-$xi{p%^amJVe4`f>RHR4dcKhKsk# zc570Z_Ws>{B_}#VI6s};@g&%cxoVzvM+=z6#cm}!*OW#Mz;o}&?*8oqjVDW6K>f|Vh$@D(r;9`70#{6hYHc&(&JERT^B^+me2=NyLKyTj<)EAhQ^6_xEw zDTbNJVn~))Z<#`5N+cAknP&^rr?#t=uGLEIR&@}PJi61XPZ=RK$gY-q#G)iNAn<7Q z6$V=n$=#_{Y)0RNe?G52I|Ju*-4S6q_Gj)&a|=Dk*-*N=Zaeo;Yw6J;o_@`3%J3X! zLK-uDJPh&lUVB_V-3|78@xxE?rx;>{`DA6*p2qd?fkn&u4v&Fs=-g)6@KA3<uHEt{^few!P{;18SdXxZG-MbT-? zz|K^4s_|^sQCVe>0I%!$<>+HW>Y}uuXJ1qC@kN4<{w^8>c;cK zy)o*ZXrb&TQq8Xy3L{q-Nb(MO1IPhiYDK8c=_W z&V6%vF2^^UZMq`OD4c~ymOr%HULmJlzFrfmfLS){qDqHU=sj%f*=G&&A z&<_3^#Uw0);|=(H(gH1 zn5K>?DeXA_?m)lwbdmFw>#@Ny4$gh9@i!s;X_a+1+uu-4T8aEm z;IGzBIW7^1k2ltKt#(v1QvR9@8Wn&2icUG8xxk){{!FVIX3lCXr^WtY?%QExjWK?h zmj#JxWjEyZ{isEqbjAIlH8Vg+&^wSbkp<|9J9~%))5-&gaL4NNymm5GjJ9ZoYSo1N zjEJf2{HPz1tdCDuGP2xhFKV6uoD$W_x9y&IGAj&nr!v*Xm(y6IEPrQ!hp{ccU)`y1 zpDJ*63Of$Ad@cXvb3XK5l$a51KqFL z_B=`|Nxv|=xYRP`mq+HHaX-H8fbJ0rR|4_)`_%OZlntI_qY45CZ7=?aCwz`9T2fWv zh!vd8e(rupB?V*@UgM!^`_Wmli~q+MqpyZBFZ3g`J5y0#dXIEzmA!HIk0b+629!=+ zV;pquZ58~hcKKm}sdJf5!#mqqbxb(&VJ~!DVZN z2r@x4oP)o0e5B0}?|HXK0HjVX2d6&AW+IHgg!HzqU8bK0M4bYc_CuZc;djsK(AR~u zK>Tm<;(&w^nz+!m3}#MLcc5RU1t|9 zBc+a6nRSwxtxpRdVYnKAET0F_;bdY*f+BL6#1_tzz+?^Z1_V^b&9>GiPGPwB&xKrt z^pY?4I|3w^2mp|LIwKf;1N2TSiHN)da1d)RcIUc92@)rNUbIFkIDcW)8J zDK^zF1%E%SWy?=ajM6L`YF3ZOJ)x4k=yOWTFR6MClO$(vZMgb!p{iu` zJ^I`SS?7+z)-HY3x_$+uGLm9NH0;%3^C}3fx5V)JbV2Ltfeya!%iTm-RnjH-2ymD_ z^*>naV1E|B3u304gZiQI;FM530}+?>6B5~bTHsZ;?+=R z_CwASnZ&45i`I|tXDMoZX0&}l&+Xv*C5XurrwyZG(OLHphA(?HUI+W#X>NoR2#Tl| z%{NsgE7am{Rv{Sp=4-p3viYScg_ji?m?3F}>^{&wTV%q(p3bI2{Gezp`anoYsdRjkGrf9m(!3WDdFz{)ThVG_P$3?Pc=jf%=UTM zdXr!xvhG?F3*1mlf0YhcZi?FIUNbIg_H~yonz(Xzi28jnahu&E`zOFap=sBwBr5Z7 zbF<2Afr8E}h4Ef6MPz6x8J5|n?|!l>^Y#9@8*N0;y{oH)cy^in=c#5;m$`6+;1#{d zYHgNj|ED-gsDFEkyBnEGq>GpT^DyiF5;2j_J*tAO@Ah!emrzB96fN{_wEto%J#SvYBO0d0(8tF(@CV?B?KUDr55!(Zig3I z!OH5-7!+_dj|sM0(qkoQvs&)t28J`6TgmZ3`};8nR8mp#5O_wq%SjwlOgIc2!@=*5 z8uy>ptq*x_tnH9#7x-|GRmVAVNN40UAe^Cp0%C^C` z!FNHXgVo!gGwsX1{$r6CQi#~CGQ-v!f9za;=2aBLc(^3@Ff zOr|2i=rx!FmsY@q{Jr@pmy2k`#*RO}&GXM8l^nTTF+RWFDN=QNHJT;iEQar=*SEX8 zpabpQByaN2SDlPx!6;C$)zN2R09SFHqFt9Fagx3}iI0OLwVOh%De7F$c^gnY&}z+k zlIC-zebbLc{oNibB)KkoLSD~(#SI!)X0asHZA-XQ-3`anO&NlezU$voA+|+Kk`;RI z&2DdKqugo3+m|EL;eU+LYOWn+?6or{8_@Acwmabu@qj8c*kZ`WmW@CsB?fm8x_#(c z+JDD&tJL2{Zk)o`71sE*hb&Qcf3s~S*j9hYFcHCxk;;kKa|L&$MBY#_e<9hT@6?95d2CXAHi4b z!;5l5l(B6xR3TKB5JW*^R8hRNi z)E|V+oKUUQM#n;biRi&PC3MI=ixmyr(IoCz9%4dQZOyH(pYZE@mySu)2F1YRk)xVr zcO~p6j|f5ogL1>`vOf*gtuWQmbq}}!Fi=g#lryE%vu-%;8<-ecJ{D|JppUM@Tpxqq z?WbpdQ$K@D!Gb4Fj*%h$RR@heT`CW$cx_@U_zK%M+7|lxB1wSmuT)DZPYhAwA=r<-;qw?kEh)iC@Y+WYM zHX)cA_JQ&%VCaov%-ftJc9Uss8KgzGy!&J^5RB`goy>WejvmVMLEr9EO?t{NO1N~j zk;YlakCEI!5;kGo9~PbVZC?;w0mqmtU13KwdCGQzI6(72SCBruZm;x7ybFz#iYJFv zaX$#(IHy!?_DJt(GLHMuAynoJ|3#C!dSV=^#0ITffb^6AD(2PQ8-#&LQqwdSbO}c^ z(V;$(>9yEvB&a12_48gnw*-@iC5q^#U$98MMa)HN2O@KouD__+4Sk}PGyrV(gLOenI6nB!tcp73Vm`?cw!RsPY zF5!Szqvp?|n#`0jukFN~kHrIonLaCz)}axAI@xDt%MbbQ=(H`9z|bJfL!O2<%$*QP zNs2gC>=J^NBVSmAQvh=(g$8Pj$_Va2yf+KQqRrl^vwt6bz6IiZns8c8bPj92z`Tb& z8zsX*WR3>wgKTmM5vK}%d|rDTACOf6Ao7#%QBLY_qB>CH_C!~`p$34PX}OHf#$5q% zTE~TQ78G!{76Sxvob_hU&s(Mi;je}FVvfd`l;+2Qo zWN%UIwB2P)6`Otg_V-5KBj^PQ^L0{ltk$c@Z4;^`1n~Ll-{Fo%f!)oy zgqEEiyC<7vWu^mb^0a=A4&YxRPqy#Elr`9)S5J5gLqOF(95;Ga>xEY%AnAp+rjdW4 z8+ohW?o^jsxZ(clZ0K5%cwxWf#ZBX;(F|lkr=^}2X?QzCcyiHa+_SfFo_(U$MbG(~Q3VU$$KEf#YVs!F}uCFg#R>t(KVS9*Wm-fJipa=WcE{wR}i1g$+OlFHHTk) zmMA8Gq;5UR)AS^|l@^mnf`;#QoYsHc$;9S;dD!e(-`G?{64Q!*a9K2yp&vi}jwQi? z%wUTyF=1L>FI}%}+!3SQrgKDm{iG1F%pHKl!K*v>3Mot#y1=^q3wSe|D3+1)20;2o z+`$|}PZ;;geFP{7a-`zVG-2o2LRg}UT}xY%yOwhKZXiK6Cvz|AfKroAxM#>KoZG6Zs z0i-aeX#lnywrfe265Ua`5oW8LVdgpOGj)u3q8P!`1yTL|yAAV1SE`je`{ixLUIcsy zT-tN6On!iHL?(++LjA$=mJAYpz;*t|)0W4|< zKGz3vM(w{DUS@y+a_s;j?C~bp=K^;!G8yE<9&n#5=B}3EV40r@7bo&_xY7|g64&Yc6?9lB2l?r@XZDP{rNOO?BTkEUc1NodtQ5_h){(+JQ zwU@s&N^YNG9^a@rLV}`%{gV(MkN$@u;-XUJ$ytB zJf2;u0G;TvZ$)VTmFB&@)l6&`&AW?CVsdiQ`ZNq&qCT4Q6`ywE-g?%`KMWHMyaX{F z;@y|L$2=G{kxsK#fGwD61vE0_uC7bfi_%-Q(8yUZB5)N-ia1~T>}K!|Bz__)e*+38TWC0F%%ZOaEMzfN-0RRgK9gQ!r`0)nrw^>Zw zjB_qaw=W~Ub9ET`H(?U#+8TloJb7sIYOc2P&!>;~DCy#_!Owr9;n&@+N!~5pb6Tb& z-)rl%MoK3Wb$en$=>G({sDs_rw0rk0cs^lzz?=3ZSUpE2@3Zw8A0Ohqt|$_A&;26S ze=OlYC|6b}E`Bj1+tX_)ZP!$RJ_vJ^#;n(P*tfN^?w*m1fXairxcF9XKY7^Jg`<65 zK$mboLHxjk+Q`8{;bJO=~#jOb=;v zM>7Y*qT`f=6~%RGKl0Y0I7QY6&Nr&(j}rO3e8Ack13KTUtHpPhyPhb;4|{ol+}AIK zPW#LDH)g5J*dGwD$KztDCIM!gREuI)8*1OKM5uexdC}oV#NT@S(#L%S+2t`;%NF*t zw~sx0e$Je3w1xA=5CLwqrVgd_?mMmevEVt-UUw4gEg@XTI;Lx{YDYKAVF|hXnx{Ly5_$J9Q>E*GP^N zDIiP!vI57qbZ!>hDPIKj174geku$0jCRxSx(TtAsni1}seyE>W_Rx;K5#-ELEqOSi z`)6Wg_gV8FWEMaP4kE45xvh{#!wVvTrU>Za<1fA*gd9pZ%kR&W(iHkHn%gezt@iK_ z3v8iHe4a6AT{@J$qPijSHwq63es=->x}$v)LN}+!{uaPa$!X9;7mR>$2Rl7qm<88d z)emT%a(?|D3(jxu!`md5qy%kA>I>-S_H>)Gy~%TswejBfv?{RFC>QdEYYS93glZsv z?62?Hdl-(!l&CJruJ4+!(Xiw-@hFGDWv2|qeX|YSe*%xKG5KI8=}HjFUxk!*j+qYN;D#*7FKorQG>}~rTc5lU}|l4 zZ~U|AO3RpmpYV6m#2|CC22@)%F<&^U1}Aj)Wln#J*1%Xx(f*sfbd+Uo2J2=~0Fs*f z2Q2joRqt2(tSzfyfcQYHEYCgWub@Ze(T1dc2b8BK5!!g1AmX@XJRQsEJ9_b=1j>qZ zB#+{v^~K`}mfS{yO}dC_{X3~~@1M9mR<5wp(b~S8?gQxQMOyUVA}_6-&PxXnZ-C=r z#GUZ(_um}{)6f@5`C9HVRbWxj zqJcW1(ykD*gd%|aT?5}7+Fp(!CZ1tKyy zGiV$~9agC?m}KEl!>S!%rn6g^Js$7UtNj5h3v&JFCd)QUST$W;(i`PFpKzlY0e|}j zvlmauAW>7YMmA)|pVlc_FUzHa{H17?{7oLE`l3jz$pvzTvXEMXL0DGnN;k$uzG?o{ zcjOwEg^+~pIy7u>Gj)N~O6gYU8C|Vo1+{V#nsr1Ktdf37-NX2SNfZV*AFbIbl;7{K z9MhD`e_wA`H?~QU$bph!PsHJjzyPyq3qZ= zJgGUAazjj#fGINMWj0tbALs`eCJ(_T^k7COyi^Cgp_1+k*!eG;wf&8rym z;8DZ{v-$HHV&r2&F)h!n%1P5q_WprBHjeVD)$Pm6ne8zH0kho!ivNJpvfF~Neu3IS z2YiP@u6e8QM04IY%Pywh<_Evyr~)O@!q*eL{`Tnq*`HZ;s4{|0-wnr=b$6E4iT!;R zwGTWX;@7DqU*7PsTNCAzxXR`gd{$ZXe|7!4 z(QFcciGLXj(x<1<2)bdT)+OCA{$TL71*)Pcly5_;{|{Nf|fP<0S^f(<1f(6f06=WU_wqW(llpu0SuJNvq{>451V7 zA!%8=-J3L+|6*dJIOxKZ%XYPP4JHxpb6)YqaBbdXwM;pjh0c|BQ~OdqWax;u3L@LQ zG{2pW3R{75!rW!LP=)UT(iEkh=MvtKp}UjldB+b<3Mez@zQ*KxOWywQuf6caBED#T@c_XKll{_Mu>3vm!u@kF z_~lMEUs?W-Uu`)do1$or3Z~zW=e=C|mz&>*Crf0V%9GT5p0>60NlZy=Nbm(!@$Y72 zMKjqKVp#l*SlzY%ybp}6n8-^fXSZp5_{5ol}?;6RD9-#*~74^+xRe+T2

nUF+-K$)FJ&jxRaIm2Cqr9VN9>tI~k$DR*A(pwTGf7-3Wa3 z2TZ9nz}PfTfOeCdXXI^_$ylq~~BSmty^y;p7n z4t*uwe`nyhkeDqC(T#Xhe~d2~@$vWr{htrTq;Z4fBEeV5;SPx!s(B?J-?8uUwaN)U zVs65L8sffs@UbU-^&Pj~th_8Ir&VtT6gBYqM=~*EXD+tofs#}jeag`c=*XuPR}lA(EMFLu&6hUI z82B0EI=pXI_cK8?vQIKui1|2ey8oLHS2kTj&Lf9wJ%VWH<%snr~uy?V^6MLSU|Bs(%h3fyy7v}%J0iRF1w5*&m ziv{1aZ#<>%vJkMj(=;az%LXczLpCqi}nF%PgR<1gJuJZI7;J1~jfI>e77&K!P!EY|)chch^G zx2VgLX^hBzl_Rt7$A_&;)}#P$z$a4Y;|^q$_6}DUkRS-`;=50DQu8+QRxG`xI}Yn- z1q@iKvUEr!4WtAaJ3ZIF7p7Mz8dq=*fS=3`U8r;mXmrraxxRIvYyn$;S=!D1MS-Ix zZKi6RKUCA8oiWKCDCYR@IOC=G7en4h<#d!^?R2IZ2czQ}RC z2FnK#kGO}r%0$4qtWlFsch=8Ac0~QB8YMcq`LYRC8mkIVAH3`a2lt^ZN~4lC-L#uF z(6FJZJTJ?9Aq98yo|^*JKW<7Be-pENl+6e8xF@kM(`Ez`7by7nY@6th#->{1n&(X*tKO2|~ zq&N;U_^J|+xRZZGm5+V9k&pDvL_LWi)_u?zwljCZP$P^=#5n8Ot`#1yo4}e#%foRN`AkD#wG>{vk zu;f|wIGpg}0YLxfKd=TFHDglP+>H$6789v2jxhYo&;HtZK}bBGI-#(rm=e6y&UMG; zZ5m&m;Zp;o#|N3OArtyrX4(&|DcDsE8U0TzzhHsq@5O6PWi~lB$oOffGN6@=MowCI))jb{yJvq^XNiYZ#{13>+j2zK-47{HVQm-X(Xrq9yg zMqM0oIb^4pR*DF^&o#f6;9ng~fV&HRaz;Wm(K;o|3q!{_06qTxzO7Nk80*b3$#FU| zMrnZAZIkVi^>W;tyKibwrO{n<|>Tu2LH4XSi^vkmtqXPZos?NPnLOVzyF+OK<^I| zj>5&S&^2kpN1ra(gFKc9YZ`esl;M(Aj|?p1S|YTpt3z1CW0+Dx+RtV2R0{?BG=C&k zopcJvY3pI{MN;g;v+hCDe?{)UE?3h_Hi>aNrhs$L$P*b{y*<;%oACjk>QYBshax)6 zPF;qXRQ>r5$KrV>hm+cH?-NJ+M7nfH6g=w!UnxWYoTUAZm~l7+ngG6CiNDBE(HO1XlExnnt*0GWsJCGmXa{=%a?HjveyTEA?tqhx$g9z_&k8|P!aKM8>yrC`tm0oH@Mpo5>{Ool>U$rvNYeej}=U67SP_R){et{97j z1dSIP>7jwB$3Y{|Ohe5bu)~Afq>hY0Nk)R(^iRRMN`hINJ(3%$A5#;AfFtW=@IKXR zK}zESK2(!t>7yXNnt8+qvGwpyBQwX;jQD%zb2l}dv2$ilO+zg|4}7C1(E2vW`3mEL zdi&Te4CxotdN<;~YV4(8{s)CW(6-hAe%;glQ`J;fRu55va*mD_e?`9inH3_X`Laz2 zi^98Vos}l{Zd*Z&^?r_^X2|iZJ7Q%bs}I*%T&d=QDGAd9??xN*v~^lP60ObAz``#C zFe<#wbY24OzFycMp^I%;H|JX2jTgAz@aN(~1UMz40o0Q4JiU0Y~P$hkd$u z`?+?ng~qpDy;mB<)cO=Yzq{OTjp-Ht^LFJ_DiR@8Ip9_@9M@F0vuLx814tKbm4E+AnuQd8j7r$MIg?&5dQ2S4H5#Bkcn6^n2 z! zF*r~r!_clppgw~pm*c$=)N}n@^K(hRas!4lZbfZ%a3Ow%caAv|m|vZp?}Ez$E+dcx zr(;c5Z2IVKNc*Csl4#3IBzSB26s1ze>Ha&lY2YzY6DP%>>f0SV32Njt>F~c?Ya2%W zuk2IHTt0VWBTd5h6|ytw|A-iPND?=)sJNeHCQMYF>o8a#ido1f2AEt$%Ph79Y5Cqg zLYzqDxKS$0r8W)gRT)nY67bs0>Y$=IYvJ5(wNW zAVX*CtR-U2e_lfxDpNvJew&Zgl0G`i!!BC>*`j1&E!+IHQ_q%oi&!&R^JoQ&0|Lc3 z|EC(9CI7buWu2$zRdR)4c?4frBln*%!U%64|7T|oqKjDI{V~aLd^q-n>5G4g%DU*s zo~(aJ83JsJko(LvMA@4H|M#f>y=MRHZ~u2;n~kA4(36a#EA3$rlQEt&gOIiFi&usZ zy9zGQa?~H49MA#0OsjlF2aPrb;#8@Y1SfxB5K{y6yMXjN=rulCRJqpkd$vf!j~6HD z4f-=emGM6dU(kdM(EX#FA>)2VgZI>SAmsx8%r*xTi_fm6@d@iIf!fL>HBIY#_(U*G zh{(;_J5@%r`WP;=Wb+tN^O$F&@w)>SF?5;=mYRu5KAD&nI1^`lJd5r{q7@bGS<)Rs zG&6a!*E8~~+>Sr$^f+o=g?tCl2r%z6EKcZA0bL{ZTk}rE`0;wl_h1|x?%0TUgt8sC z>UsgecMDp|e-taZ_cIWgOn%W%H8i~A+%L9(4|rNE5wc!1w*R6U<`5=kKuMK%(wQ?P z+jL`jJo;aS6QkwsVC#{g^5WzVZqF705P z!g{I*GauM#!#v7dBfQ-&S9u`UcHc;0jpqW!F06X5=V=C)!1u@Fp?{W$KEA9U0ZVn; zXFz$M1eLN_{+qUxDcC_q&@g}N@0pp>)sM1-1r*}ka%EncW7VGtR2+iP&a>=+&y4~~ zSx>2uG8FV2I27j~DZd)0f9xl=%06^GUTH5FYb#&=pGO zJPNRk~HU2mtMRbMkpRdAkRk0|M!1;>tb@C{AzShZa-XYE$kMBa?@y)7WXBqlKU`=lJl(=uBvpH`+mGK+=!(Cpbx zrW=8wtUl&XKd3i9pzm;=e^<$4pkKJX~cfKui@FJi`qa>K`opAgx|m-%+?Py`x~ zgFS9To%%#SL9+d|b0LmDO{s9z}E<2lmIxQm=UmiD!LBPeBg zph_6TpWxI8%J8ccE`aGkSZ66jz`syZ5Wi>Xf;`I%An3*(a9}?92{$~q3xVxDLKgK% zJ`{F3-5Pc()U`|lwXF<0)hzP&HwwMDIjw;efm=vvh!}C_4(omWiP&uNR)(mQ$v^De zr;IR5dA`>p8}o@s&1qmT_B#Qg)yXIAaoS#(=@*}!QJf!Kdzusv2n1M_TUh>wT~j6yI*?&04J7?Pw8XS)TQ z4&C|nN4lExOf7CI!SFe>2hsz2_y8?e@SjQJ2BoW~gbyl+%bT$2z=r(SH z?Y}cl0UqUh?UZT7OgaDO2+kYY70RpbuOG918mr&seBQpxQqQ90UlN;s*T4_HN{%Sx z&x=sM_q-K1H*P1^kf2%C=e}DIik6fS%1O-0Oeyi{q~-r|2qQH3eNQ@JK;K6xAn{Xcw53HhVs(<0}74!sz2AwUpBZu*5DY&YD z93vNt4|I^5`a{l%i!`yslJ>bxe9vckEF$wqx+hg2xhC| zD4k8coOF-8z_yw-><%~5*c^J00o2sT@{qt2svj$z#jUcn%fiPXKZvhn%^43PSfwV= z=81_;?Ei?A%Ix`d${6;>KDrD$W5h2z;xy={>n5v`XahY=4c!8RD-{aCr2QooR`seE zI?P622ktkHgAa*^ob(Tk9>xCx zLM5Z+Jd<(+r$9KR-2j++CEu_$Es|YRBTZ_|ja`>h?!qWmy~n^pp`6xNE%jxb+nY>cW^-qWB@sAbbG!rUJdDqd;v&| z$9TWXAq+&u0KTW*u42iNdE&3VvdM;W2t6s@TcLikt31qJI+jB_wO2yMfFi>`fHc1x z(~$+fb}HXhWIi7&H!4==Kc8Uh-Sb%VnII{_cv#jiHM`Q0M(m-^Ey~>1b)migaTfY4 z#Q#O==EFO6^bRAw5@ddg{sjLf?nEWi#k%kYuqsh_zSl^O;|PVwwel%fLcT&X0E)c5 zZ9{`;n#q~H2`C?d@tVIWyHoPEC1R^1qcc*B{Bg#-&n;PP_ecF7bI&QZ%Qc z%V8F5FJF^9Ou-=8Aml1c_p=X$)xD-ig7OSrdvA)1nlOF^AO+*~BA8AwDP5Q( z2MFTH0V|w1p2Ny#8=PhV`05f_9UE^pqowNHz$aI|Mg=RG=#x*#|DRU*hH@wj zO>YqXSU|=ex59Nljp96FUc81tM60`ApMg0wJj0lBgVRCBc7t<<<_XZUDlWZ<`2$4U z@h)Ye4)5wLjIUp&0KoLijvPc@hvzulPlmED)LbC#AtO`lCLKQ+LFY`cP#8AV?Aniq zoO-|8gz0&hUCoUHEzGE~>1vlE1&}-pT7OlD>CsN>sdBbj6XL|FwSd8!Ex{eEqJONhz>JwKp?Kd9{- zt=AionF^(=O)G%v$uaiDst=QQ0#;-UhCd%8za)h{d(1&Np6g~DKas|Q_MT}e-=O>v z1@ZL*=zEUqgzQRotD^$uKP3@Y@^QX8Zi@6EOCEsWQU07LgrRrO%S`3D3db z02KOCAZGI5Cq21LF3tSAkmh9dOOiI@fWQ(X%T72}wr@Cb=;yZz8!X|PEhs!n8z!r8 zDtbb6zvbUFE# z9EWb66_v(h5sFATc%PTlJ#Ezz&;i~GBO*Eb8m%ItZqLbfe}vw>aFY@X7`Yh!Xu4sL>8iL2pEjq zT)I2%ZD|h*wq6PLmHlQWLkB}>##4IX6Q{cYB@x@Qhq9l%kNDAKtPmsBKG7o9OLPx5 zQs}M@7IR(Bv^i)fDp5P@rEV!Br~LLd6oFDs=y!)9W2nF!)AC&mo;r#Pz!z9YQxLbl z1lxfL*e-hS$Kt$zNQ;aXkxNZQq$Ck{by_ex<3XP3<0V-+Y~6I08?epnE}1unzx&XJ z_3};6_Ys-2SbRrTPPCCQ^HBFIGt0&%N5Zm2p0lB2M!~;8Dc?n_N8)d0V6Ccu&VPL=vniA8D#bL^$oUC z#U>p#nRZ4#)HuYH1w7#oxpt0Y7=DMrf9A-357u;=B4h9yD29l&vSG#X2&Hf zI_6C^M^vr35xz7NKCL%(KE*oS;UIx7IteQ7RM1-M`a)J0 zi68Qa`I>6T5KuQtAtJgm!_iVytUTX#expa@$AnF_%ZHtj=dz`V-ckoVM@JM+Wp1{O||L^ zNT5U7Nx=_GTqRM@zS06Y&8p7#p>Jj-G zBqrK!e2wWdkt=M|=S_9ExixMMvonLZ2JuVElsRo@Z>hph82qjA|#foc}MaCXzR{!JK zp2&MC+EV1BY0dXIl`{(9Xsch#)!&TjO^ZcS@)dc#y*#X{EVs4e+!?FExC%r-iF4N4uPU>8?NZ_3jT3FR(BX1>QBIDgSSr8zvL_Ju_hZEu)vBR15ks^)_NuJqw04@; zyog*!OTEtDSe zb=aQ91X~;^DyvI+Y4J4tt5x@pG19S9D0BX}6<(-BBsaqJqXWAiYRY(G;rp|L_Ro+L z-CBHpHb12CLiN&KDY|v7d8x-hHB37pI?D$~%Asrc4n_(IOprsV$!at)o)97wx#Fc! z^^GYa4+R7T56+n^ax7pvOq?3%+F)JWcKp3>p%7DQvCgq=Z7Nw4lx8cat9m~A=J02~ z*jji-n47Fg%eHsnyx+aM9`))O{5KIUGivf4=w+FAi~sY3rjO6GbZnqOg2mN+6xL6> zgC8cQ{La+L77edp{*zYu*HnA0?B_uSYLt5v9Rvq{+xKmQbkc1S8f{WYS|6T$ab+i+ zhlhjurQCpP{y_+DlkA% z5$ytA_n9h-(jT{8i-~>Yv(ojO){zuZUBE!0RX%MdwHdQy%YKQ;KrE1G&B%j3itHJ9 z4fh#5#jc<0u&tu!U6)maKCBXy4JT)?BG8evWRf3vLiMSo1f08QWkX-k7rVBNN?h;# zh+90ebQiCNTyZgc?~(P$Urtmpv^U}#a9Fs5$^Xc8{I(IC?7 zTrL1U60C$myitK12j^soI!jR4oGLe%ek5>^gM@Nr5R};>Bgme>D@*V~`PZoBU??g{ z%0GQ_;oQ;Rcu<_fH?rP({tZ7#zC1ogt0lNzBFPxp59@Mkr8{;)d=d%gM19$Je_t_f z)5Nx<#XwkpAq_mGL9$BH^A$SBB!zb$JvvYUh^hVGLA=G1Xb7@~Do+wFS^)MB#`$PD zn%~1}Nnxe~btQaPbj+_omrm;ihH2Mbef;cL@kR`se?Xvzc_J^M`8Ot@oNzPoS?BJF zxghgRxUM&v{1el83vb0KkB^?omW`y>y4;z)izO?;+>*Mb#>c$i0AU9)5B})2M{>4r zW!E=vDl_Cs%mHVvt(G8=Szx~p;X{{|!i}`cMs|KRd`96gEE<+%QvSVl0_)lFraQx}$^&HCcluKKyaAZ5CTiURO-{dRh*dp0 z4|{P4AeISO#4*|>V#)}F%Eh=Bk~lCe>_93I3Y(PXg-FH!vJ6i|DL_HUBV~u_Fzg{PFu6UiVxbDIttO6 zypb1c&>y*XqB~zOv6^2-pmi7_@gs#^``*Cn1H;sW*Li>HOj~~1H~yGEi)+s}d}^Qi z1ovM>fIuq^b(+**X-ss83lMdT;D^EreM>5P&Y_sZS+jOHm6c zX|tdcQ~$x2U~6Io1h$Qp?w$sTuvaSapA{P}e6Oo3I)!6!q^Kyxo$@W-%6d69U_pM} z)QI|aL_Blz9>Eo{rMo2UCw}|n70c2+Izt-nANQPA6~sK;gin;r!{kEnv`moG^&ICA zEHqvBx9!e|w+zanNP^lBn<(nq6ty(~`Di*OqTI-My*|t=gq>k9K8CSBK89hC)R|E^ zzGSsDK$^7qh0hIl_dc4~GTF=H_^o-!uwYEh%qRKnVf}mA^BZ=N=dEUl&qIm6-DLmZ z6GyN9VN(vP(ZE>aMWN?BUm}N6c$E+8mMvo*IiT6Oyx>Wexx~7lvM0*DlkxJorwckw z6BD3g#_in`q^GnD+QIG~lhRZV$WVq#>uO1s9RXXG^aF;IWsWnSk4C@Ff?rd{ir!2z zT;@gDpGQgi_NcK9Gp8mNP-(wu}>IoP6%Lg5ZX1 z90S#GJZqVib?NB#PH0qudi{*E`_t{`!#b0?42;w>Lk0eEeBh>uI>+X^IlG+AAFg@p zsN}zk=Bmu@r-+>kJ(Zklo}OK|x@6MNplHI@FZd(wW7?pxa=uV1ZdcxUV)K8q`$D(| z+MK#7^Tjwp0m8MXc(I9TRj{I=^SU!?G;DDZWEXm6c7JmdhWqG1z53Cuo4Cf|fHQqJ`ETQ%d~(276+7*9ymZcHIV>>jHRhUX zS-)Di_A`D)Ccl&CzqgDf`e@z_wK+Lg<&$s%`Kgx+u4Jy-$2-8^lzZ_us{U|qZvU)% z@9R4yDxO7czOMqFm>vnUV(9+CB3ve;SG+GgaT_EEu@TDiXEk{89#{X<9jWpg&9K-W z9q0s_wk+ciXH!G5G%a0!TD$GZnF@Zi+D4LzC6=`K$KR+wuWqsCd5p|+ zPNMgBrh5?qsSAc$q#K5zAV&a;HaB>m05OBSh5q%=S@@n}LOO4-TjEJ|`y+D<445^~ zY)B$foQOXsq~E1KS4JDwtEV=v^ydbTpmkMWPVj4Cst|vvGWtRAj2tcHx9kh3SFt|` zLpQvl7X?xED1@tCUCJS+plxCn^4DwpM;u$rB{GKNgMq)P>f#|;+Q<$>z7s}9c1{SD zZM6FC+2_C_^6Es;8s8rsQTG`J;$h~g7_?R}VQ}f=X4~&VO}9($Z_xG@++$0FFe%pK z`{Uc~{%4eia94sax<(Oof_t~Yv++Np{!>JPv3@~h{MP#h1Ifx<3b+ltz|hz4qxZ(+ zh))iJfL5KIlaXpI%kCL>9~JGaSgHZDiU3m<$lu-C~;%n%F9n=L;25||cx@qv<8U-4(EQXUy zdVxud)X!-t4A;wG`C;qe2ng|9xt}imt;XBPbuakAD;Bk`K!c~A$H`xU!qXl7Xu=OC&rli*D{#Ln7n}r-r<5l$P_? z$GK3~hrdR{*y>W7xlCf6J3gzzuMb$EfS6{QtgfMZkx&25iV|ui0~RY^+APD-x=!(Y z*3SIsd_0KAe5??|Jn8w8IN3!3Gm2UCggvzP;1o(3r2whymqMD>)o{;Ce8)eJ*l*Bbs7| z(l8*{Pdv!$4k^u`W4RZiuHGjf%{FQu~GiUP1Nl1tq~PZ&kda}HO&ib<@<}#QXYMQ z2M5gyaYH}~yP)<}vnWLh>)(2PLD7>(L<`m}4Tm*%r30>i;j>?m-!>1*ToZy`cz)<< z5qvT9m9eGVa=qN}1s$1zkGb0h=1qR+Xf*aYnBNI73og0jiC*l+G@_2m-!0hG&ENAUx?%1Mce&vF7`! z$mcRhDhn&T*e~kLp``VG`*XB85Kg0IMRY`S&P4uxT9S3Lj`@d*-OZu(rP7Lk;S-W4 z^kAOX`>P1Y8vh>1iZ62K^E{$G*nJ@#8cM`(Wv*?j>5xjgs$GG}O)V?^Faok;nnprG zP-i11#`@d$F|_vRt_Jw!vkq+M9E?LqHWWanX{LO~M*kwSlp{2=jGPw(!MkXLpLYXF zTg&7e%X`Gt2nYkSrQv&U=oxe!!esU^6kt8AEvgKz^xA+XKMU17dW@f}RR{5UNRp!0 ziDc}N3?*ufIDl?~F<>5YBWjb-OGS$uvRI%03(p0OHyh(wm1wyh!@t}ZP}?a!ohiIX zU8gC36Abjl(gBgLNazw6h&bNj2+N8EqLIdvP3w7l2aZAqo_t&=em zf5r(~C4z@@RhJ9dgKSmKCFeVo%wl&4-o4q1mQy?F_3>FUnt_F9gfTe*bf0nR(U zLXg?YFx>rB?Sy5Q(`kD`_SC!e;BHe8|Mky)OUaK>y9ZgxiS*CUlm7xd#6udL)Y^2Y zI&;*huZ?~_<&fJ>?7>JD94;7yqezHP_>@UN&m0U5 za$J4m&-0l2>)JJR-cyvgsHu{v^vfdRVXfqNS3M6Nn?<2-7s)Rl{VT{+SBajq9$1ZP zmVMA}^ixgfhjlrJ)hJH}2MZZoCg9%!M8oI+IaD;N{S)0C(Q`$M7^LRHmtx$rr?-!X zWwxIEHShj6>I{OeD=Huz8(Z&}7Z*zmxEqqqNHk-O4vVXDWw=k+J-mk0v%a%QgxK?N z`+d*I!6!{C;k&@clBsV|Pm7dmF7NJS=_oHl1_pZf3r&+_{0g~O%&29;XBJphGDgD> zf0V>+8fe`*H-CY=%IS*!zfr3Tr@ z!QR=Kro9w%?%dOzN`UXLgP=PQwfQB%(jzzGDU4;WOJ zjQA1?s=}n`*MUo`!!X!)Dfw@Uz%9pR*|Uu?3_tva$nkdgh4*g8@Jn|klpsuIJSEyU zX=Jdm6vW-PH?xnQQCNpn@}$O6iZa`Us*C>TagfAXYIHo0@Xp%l>N523p6uvW!ZzPWeb!r- zONu?WRYgP?q(rHm-d?-9BfFEl5-t|j)SqcKrPs=U+(sgMb1ikKzy8OxWc(Z(k6Z!PDVH}|t;{7uD zd2Q>X^+=3sEA?~O!SdZ!j%r!oyj3Z)RY^_YUk&*2?Dghv*Oi!&8?1xVUMl*fig6P; z17P4fnhmr>St1QkmbFDE40w9(t*C^u$+QSNq3iuQYW}}OLns`<^qRkuZj`=f-v`dkqph-SqSfP3Kr8^%P#cx^J0D_${YZrr3cL zo77y+(mTw&jKGq+|EF1Pi7LXb5oz;qW~CqGcz&=#Qt<=pVbZp4kLF-eR^`4rYcQnA zrJc&1?*n+>F84!|)^(1xYcBDlh#9S8zxd;J)EuV0(z!B1`7Cc=r6|0y((GPFW>2Md zV0EI|X;78|90!{o?N{JDuHW8FQjlkhx$h6B82t2n!sOXg2daDT&+>-sN9a+0)DEd+ zQaKccJb?{t70`b_r@M8!G82`So`7u0^dz%wDBxTE&x=PD{5ioU@balB4h8Ddu)g{T zw~Iv(buFvLM%vZPfKEHZy|Fhmz!L$bWT)!Az}~(53oF`vF4u-x-RoefMmwR{=lT4i zfgkRw9vC=L6t9kwN7XvQ(MirGFYa&2#Z@Clb3R2S+4?ghHnynFKnUk?^II(jF9hC; z{E?WPMwd8oKkR{J$THFSk;%b(zwRJf`B}Jnu4ec@4}f#+EP}>f0-Mytlo|r*7>#6$ zbU!ESa~xbJqRV9}WH03WwDy>x+U?oL!1)OYZJc-eTn-#xr;9r_zh?*+-a{r;?MSu$ znv4e3AN!KDiuQx*ED8dPgIj9;A1gtlNGFE;q(Nn)SceFk;xC*AP0j;YY|f`$?gs@k z=NZB3{R@#+?TBNoqcv5rGlh1qAGF_amF1Ib66>}a)P!sbrl~aRe`R|Z93)eJl2Rl zc(gIz?35H|ZYn^<BKs_8(1Iy=qQV{P(TQ zfM_H$`E$X?@8!eZTMA>8pr>HK7~#G5;q>!h2$<*dOVB7*`@QZR4|CD#)GwP3qwo|? zgv)n!I*iR}0E8@jZPcg{@B-2K#oDYJx z?~5yS+zXQ8W`zF&rHB|Je@|fghPF0}Q;84jxl0#Zb}`oN19-$S<+ms8WbRq^814uJ_E^3L2$4 z;L`01o7T&@(ALjc!TC#K>|gv!e|)cVzCGTyYkPl1mMJY)wDmK7p;8sA$xT%~O($+0 zdI%>%Fxiu_1s?$hr>;g8fo(cApKUh!Lz2&8Z=jo;dxjGb@(1KE1PQ;A4++uTVYHeF z2;Tx+uQy~E7<3t)y&RMw5l@Rv(i>JkgY=_=*gtAWLWsCpxN>A{kaFb70A*PxQ}VrJ zjQmQ5q(;DX25PIE9M;-;-%nL=`F+NVsFgxt|BFkQ)B1+hS{@)2hEjL-9v2?v=z0fFXduKeS;sd9^{muH;;~QR|n)=5gG>MOd zl0yWi1VG&CJzd6iOaB5LlDJH3%;oChjWfOuwc_9pssaz}^rju$DU6x5q;r@keFfPM z>P5rKiHkDTV#C@};=g@NlhFy*RAnBJSydc-!=e0|t-COv*!=ZLt~qr=-qUy!O^x8n zr*3~L!w3I-4{&&&Q5bcKGoHwO{XpBg`HPk5DCiu6C#ZAsf{<_|O51%p9gDafvo;7VdPTHaqgaN2G(T49 zKlXTyDuG}};s-f*HaZyq>ks%P_0P%~2tAap^1^0UycDOr&8{G2b*W2*!&diqQ9XBV zS<{}cS*on#`wP#K?`_wMO1lmUETKZlZwtMC4wH#JnV2+;|N2N?CL;qCQ7;qfRf1-; zYkdF(gcU!Lb3d?qFD^B84}_fMsPdQ(d>#VXLHgI z(cxrDE+9x#Oo&${$-GKID{cm}Txz9$*ULWbhiNxUY^r;ApvrOBFYr0^Od&p`-t60m zyfP7pfkT>r?AJDOxIss&I|EgtZ#h>+l1Yd&98j(taEKtn-HXR$+oTc};))73P$vFj zK_Limv=&Gld<2ibqBhlCHl<2F3=UZ&l|O5xz^=_d1GY40`ZGdFlDScmMr(mIKg*)_zMI2A{2Ltq`Or=Qo*qi-ZzQ>)(8GJnB@n!k;?g9eTn65sJ!o)O>LPnbPc?oGaholvx7 zg6DI@JBCC^!g4&*^;qUMOK-Ok57XqZOBk7fV-mrVwN=AKa1pV(DRLA^D8?o!up+bU zWfgh`6N7{I5k21lt08@}Fc~aB^k~#^le>4#clg9(BVuORP5m)AT;X_l%J>D8{tcmj z?f&Hsgu2>4rnGGIo zlHJkJT`yn1n}+3HmW7Fl40kew)27%l?ijj7t>ydqsG?HM{_H3dptG?GrQe>3y^Hy# z(J;iiG&Y}lVfY+5ZxRNfFaeTz0KhtntzTy+37rIyf_`*fPKsn!-j9Sm@S2|D{RNKv zq5~VSoYG?9G&7J~{|vH%LDY-|cqP31m?cFvC0h#+!!?u^1eM91qxwwD9^$~sXAcnb>d$|@3t50cnviu!ITd$F%W$Bvt{|+VYlA}XXEAv19_yg z?Le-~|8N0>;cE?_%1Uk<(s<{{H4d$w+`{-Av%}X4j5~Eg0L2YJa+lR0%|6mc^2p9< z1T^o$7xhBvtrjHRK-VN?+#jJ$zSg~!`&{{Irh!ymK^1%?LB_5BphO-qYQ}-~{vb2i zWvm$%&@@D6K7PJb`KYo=t{Z8uf~wibw|j`L9qirp^g+Eaz=R=NMQ<c_*dNNamIe7a1VNQ=5cpM?;A+J2~|Oj9;GSzb+l zC%-{+2y=hP+K8N4sJ8x$bb@(Iri15iOU3bAq8AD_064TD+WXh>b5 zP9t&_)Z_;}4oYIi0gG9CVU)l6=9xT4Y)S3ns}P8mp||F3J4X|m=ua}vkAru`72(_7 zCyMx5r?z4yF3kHvFW89~L|p&A-rNO+m0FX8)dg0}n8aACekml2>dG|psi7RV4b2Zd zO}7Xcp|fCzC$WYOgUUaU7X=uLAMB8+}baXSuu(Hpup z6tZq@_vcQd2?=VQV^I1Pl&4j=*X*_pb6bxnM~QTZ9DV5}-1Y^2}vc zV(waNe#6IaX?En1@e8i~Fal8($KHN@x})}dmM!up<8I)^c*7bYkLJ|z~Z-q*>b znWdppT89L@`ma~udrau!tJT3ds~>>OaT-ghaN8|KO;UxqKi?d;z0E{c4KS(vOea=k zBFej^aT0VOyo5-VQs0b0@-%6X(iJd6g}aHLju|(PXdh7s0b_ODT@1luZa1yI&V{=_ zk5l91umnu)6aRj;Q7Q)#)5E4lx|XO8ASPY!Z`TJ4&UX z@yFPg;;;8lgU*LcIvY2j`nE7hi11lki)(){D@TNtb;^cB*ZVu_3)Y5z5QH&9YBtsj z-4`)dp&&1+$twyn(5yuPB#rgXTc1S)2qxj`iQn=X6ny=TPz6KIc1Xo-h@x49v)D=OZ_C49Ake3gjOF>o9Mv9^W~Q z-M?$+{?2$(6H52@rq`hH{>EnrvckHtKXM2hn{NO$`>lS0IP=MfV~V0O1~Nl8+f@oZ%I0GxHp`T<6Hn=E57b337-WF+py}33YRDIt6=Z-Vkn+#efBz zsO&L6ub;5klvR?OLctOt4UWgr$Fm@yeHtY9B=@JVfDVYaaPRi@_5KFz!@ZL^c+7Yo zhUe0$UFRmWKGui4BuP-DDs0lE#qtDE_34m?E5j)~>03tjFNeXej``>)awR8M6`rs; zTEm(>mYoeeSDvJEXP%>}>9(+X-4tqF3F}8O+*zHzKZEQ`OgbkU^s&ou5w{yvw;mXN zbr(w^2KHZZ(q2JlAkC=h%bos>2O~6@@9t!m%Ftnk)gqu`t2a=!L|h^KIwFPf=n0+H zDT{ImHN)s6;ZjnEfL6L-!v>CBdHN6~bx&oR3~|u~TI{G_;HbFe8L|t|w6?w&2wBe7x^=>Bpyj9G&M1es<%<)3PPYq!Yyfe***bc8S6~vKi)~ z?&sS?0pTUoFEX{;Wusrpo&Af~W^c+1LIL21LL#4>%c9G}$@P1x0F%*$gFM;unxHqI znl0}`o4z~-@7g2@-c+COz7MP_hMtdAi%}Qxoo<=a|hU=rL%PfdQYCUhBPOj{>NRhc%=%+%Qolnht zN=Ptt*VO8ul$dROCD71yinB87@EUGczT8+j;$b!T^C{<`RT+#!o_$KuW!6VT*L?N+ z*Ykt{H0JAsY?B?4d3{jU8tsd`+Y5|(@zz7?B2NE}AQUE`=JR14x;6^olnf$hpVeBW z_B`?RJ}nXHlKdEgL*uT|nkpstHPh) zVHQ#x7y?$J$YevKIa_ZeJTCW{MUKn;2&ni}ZjUbv-M{foudxenYU2!0+J#5$yo<22 z5O#xkZ>`C`h#AdQiZP_FhQTv+Pz;J`Ov<9C)z&=Drf9y~nySl)JwbZZAJiMHlRPD4 zPtKbk-`CMuMdgXeO0WxWz&OIjZDviKt#idL z>d~~JvX=h0NYSv-_sv)HDUKIUg9h?dD)=2%0Bb86^>SSMh29KVwUaH^ufRcnU&4`;I&%WL9tgSe0nTH$28Ipn%b?*!C z9$k<;RWbz5`As4dp8ADG`C?!jrHb@j>=_QkC_j#Qdp$4dalLPl2)S!O(#sCnjlbZH zaC&%9iVP-c~);5zQ@CQ<2<5n8lW z(Jbk&Jgaxw*APG`2w3)nvb9XAomD{*I@tu3K{un;7!5YELtyf*(*p;DG16w_RJWob zT^6NmoO#Rn#ybQO>T-0xv;QPl^#ZSq=ku(M()}P(;_r-h17}*|t(ok}u<4+$xE8zn zB#l3IqyETWw4)ENg4sj`tf*6p8SYq9X1-ZEU?46VV4+UZba09}R92z*{Js+Q7j!z0 zKD0Z7ByDjy?RT{E-O})g3qOOFyt*|cBHxf?khZmr0r4_72rQqYg0jXTy8?i6`nBs@ z?A}+TWneOh;w%H+LuzHF{nQJ{08h5jx-vWRK zA59T7bKXIfX*HC4*nRxnlMc=zZAz^1K?RL&d5t&sS}Vh)J?9m>C|G5 zQgEx`{s!ufCR#aHK~TCmDk-0BfkK$0>+8>$kP>7wz_Kx^6Y5~h;M%bIf z_qU|b%iZOcCa;j;SBS0wG1#oWlE`f~8!ErcA{b`ilVeL)zYnK0pByZEUuV0-)oQmn zP*j$1J|e8{(k@8>1rz?{4VFcS$}CetUW`5*;7Tw*nU1t{>?fJ&Q=Bniwpyz>YM^@+ z;&)!girVw9b8EiZro3?;Y(ANe5k;*@maR~GD#$?VErt?lgJ`~mhEj_tL3ICkC;dI^ zp5)x(30>>4D(jGZ5>5-5mE9s_KF>p}IlJFf^lH4gV_T8U^|~tf=(08C*aKDW<@CJ9 z3?1L`%Q~=Y)b(I%3Ps!wRh@cVI?kDduy3v+uVduY#i9l|V0r>NuGDk(S(m=75#V3c zj<@5WP2C{uZ=)oR>I&i@6m&kUEtJZ2QeE)%dJ9+9c@ZK-ll>f+$IS;;bM6)`LSYYl z6`bp_*+)=VpDYCGUZ7adTO!DWRQ0=xb5#iBe{Vz4bqdZn$cij$3;2D!D>rhvw~6s=i<6W6k!7% z6uW6}zR0{FCqb>6sG{KF*^RE)5X~&`9c~D}J+Nz*#x7|LrULI`vgH#>XQ@s~=4a8D zq^xZNih{X}N&9Y4u^{22OTdMG67tS!$cb#3p!<$6hQXUn+t4-3*|3pP;I+@|nPvMJ z#4}0^Wg%4ES5qN&_zGFM?!}j`mvJ|-??*p{>b6b~C*T~yB0hR`lgy4aWL$i*ZRkMs zgB?hmmsa^ma05(ZzJ{En$HpbdD|g{RH>x zw6Xcknoum4y9k>;>=LvpZLly#0UcBza#ixDIicOC3wOiVfRaNy#zweK)$&ZGav@Z@04^{Mj5mT z#A|>>&%XDcKZ}|PZbo%>^Y~=<(NtBY=}DBecaC6gWf(~8&p=Cz!TZNH`B@i+-u65` ztjVb-luwgq`WL$J$iyTEthVK2)uLQBH z^Phy5z!=}yr?axSdBYm(HL1G@$x_IHp z6Pm-;q6u*AMN|KwaMjkYJZQl-AePJ}?gLB|z(Yowj+-^pyw{&TzeN10oEyt-HOS+n-uv*&s4 z=f1D&cLiqJWg7E>?P1M0_d3iGT>Hk1x5u9>AgPokP?x-uTf!j*S`vW%!gP_nZX+SB z*>rd`J9u0QNwH(`S0tC&uvk^d-Uox8h|`=vhFZ_6{EG#aik*}<6&6H8(aE;7XiS*O$YBmo*m!pqbtFF``l zzgGxwvpC5hcdX_XPuYDL0s>CQuQta?Ku$+8bBz)4eWolK|8G^$UC+BY7t?c@r?XFR z*JWjSGho)pzl(ulWI>Q1j1a-*sY+AO`AM(M@bqlriyB{!d?D)r&yh%<7!0>iv2iGd zkCOQVd4Dv|Y%v(Oir6)Nd`^m~eg=x~yun$z#hCy_N0Y)5`sO>lou9K>%Ko58ykG2h zMi1rD@vb+^*9whu^HGAiw>F3qRQctb_^}$NrJ57^6?GLasj=l-;D;9D1Dy@PSBt#9 zUL#o^nYWBxJgdI)y4wVny8vKJ zqvcP`>7d6it=WN?&b(w(mmk{Lk;jcVg{9C0FEl=CB?H{4vu8mRGet6NBY=b)1~8J&B@X0bc$geC4seF$q5M zY?G=DJz3@n*gMqhx(}UmHfodM(2=>!myS7c{@(uMU#?OA6`r}khNQ|8w`6<$1=^P% zOtg_F^*Ej3!%K&{0)-8{eaC>Vg9Zcnk>~j*++)NT&|{JLJ4AH~Y{M&E@1tZyM|Vnp z3-VW?=m8m>o}XZAPSBp(sk9Yo?D{v;s@b?YJNDKOn@0{^egUNY?!8Pm%w^*){LNgx zZ>eYD;{!2&!l3UTi`uSjwgh4kxiT7Juz>S%#U|XIhK+N}&)CD;jW;ny!a>3lCc@|; z6PpU4@(`ylonZ~+GTK%L?20(Aps8kRULsU&W5^bAjEa$b{csJ1WaxI-!&^FdX_Dba z%a=dE&D6#e*M6L}G_P>P^u20)G~MOw4UT!(f1m2d*;e%VrX1kGYQooBZVD%#MS57J znHV|g25r4qPQb=hL+!zH6LPDqX`AmMQ|>fXKwXmO$n+7EfGl1Kfpc;}Z~7H=0XFY7 zT%9D^kxg=RLgqyyvq3NCm&V#iopSGN-#Wf@;;z80HatM8_!NGfbt{U6DG)5^UNf+a zE#J7m&}k})KPBVEU1}hHs7ZBc6eo4)&p8YYloKCf^iy+PTL>q{Ms*5}j%c16ys@-H zlS|r!TPda5xmMaK>#c2g$-JO@I@#p-n+*+ZrIBPvLUnw^!pZX#Iu}*p&mvKJe;afB zHvl1HZ7V&@5fSn}`?7Tb{Dh$OvDmUEfJL?eEE7fnp45N9qaWMm0ZUeSW^R|pNGBzL znG`Vgy&Y7ET47J%!N&qI{e1;;U>-HCu=oN-6@gy={JL5##sW$U++N)`8g7hM7?qICQ?wtfKv*_ZC(+KAI zlgVR@5NYEOF7ufn@K2P?N!NNYj_2vtm!+MdGGAX%`7{3r)`{i;1l9?i0S=1nj~iam`*YEiZQzzO>29O8*}U0 zW3X!!%(Y1eUpqz7#!39SQla?tt0$x4ugGQHfcd*{tuJd=b8=3vT0XqacK+5;-U;ti zj#amZjTpu7QArRmDZ^a}jPFwvh%UV$8fwy9uWse5h$5r+@6Ym?>lBN0`rc0b-VU}_ zl}vPr3O~3|2s+CRj`uJg+q_3wz8XN<~T$p`r!KT}`c zNGCPC!laRBFagtR2u;vyJUEi%?4V6i=i>!UNn9Kh*0XI${dk<$CFM!tEX>${{j(A5 z*5Rj7Wz1^BF4NUmoS1_jMx$2Rp#hpxM;`?OiZ>vo zjTrfKz7@J`IwJiyz-i4#!w}~Df}k;_`KU>SW<}qbHs@XXot;_O)#;&0Y0mr1P1ddl zJB|tuN0!$q&Jbbk+=DKyFK}mL>}|odXjonS6>e=C&6_ws_ug&uVs`M^6?hG}dKidC zh!~k_LCd|DZqWAY>&UYEAJjCXmo)?sx7V3C>~c}uyrJxzU`99Nlc0BtVBgXe(}Tt( zzeo(~9C~e3zUeFvczxExBmw5s+1!mz$fT^ z`(?}6B{s^|npF*t$dYlaG7BZ6^FL^`WTp5wE{M2PrLz^*Pb=IDDE;0C<)i9I)O74^+9E2^&BKbm-V^5TmL&(D0_ z+T=>kqkfZ@j*X#ARnEN;NA_(Q7^vanZYcFrO!xD2ykG>^_zIFs}Dn`WbH8Co0j<=NP45i4rl1D#pJDlQ)4wH2iM|e(aDrCi)k$ zLdDU-JYsP>rUDeQ|7oH4c#qCnPcV~*Nk4q@*Urv0pQ)3cn*y@;bC!|TS<+LaFH05* zpZfU2=^eH44Y3d4L&k?XYPftzV)Vc1(dT)maJ*oo>g|CW&C~O7B@&18FLDzL4W;KlgMhD)ycj-o1A@Tz}9$L8FzdNAk;!?a#wQF5(% zc*&+TcY`IM=xTBlnzHGCX1bva<_aCHvEt-@X+Haii#+<^T6=PL@iSnaF|~_`bNx^q z5{V-sA#by|IgDCQSw?RC9n*^J`)b*#S2}HpyF4}xeFO(G6D}#1Z*lX&y~VwfdfRO$HZ$8k(rYSE5^(xO6SHHL_0+u(?_ojL+fO()ZKhQXFB^9B z(vL#pf$ytUy@!oyy>LI1ybe-;Qy6<9^cjCJpt;Goi@=a5)flzmw*dE4>%!y&mgbA;3nWni^ZJ|3J3)HF@R=MLI28)COS~TQ9bwgXy~KB9u3>|TuJ-L7$#yHtj*N(A zePw))PvQn;{4W)UYJ^Oq46)Ea&M$#zlmWg%b!8Q(Z=4Z z-f}J^1acs5r>k=HQp8$LafV5;&uWHU3-N{)fe1Bc^?7Ixl}&p6z@a z7w0iL3_Eo{uR)}^AH`*qSGOw8ys~?2b(=_nB_9R>_kV!YUwIx5{>nPzW_@hjzEf** zgZtd+0ak%RrQ+@n!8k0myvdI`Ou?L`(&7YdT7mDOo$d80Pd4Hta|MYRqjDu!rPdZC zx2xa#9zDPO&XJUF}W9Wq~MCrrk1UZotk{x^sL6{_Edy0D%yf1D=@;ce|)uI7S zi%j1XhUtO3Tk|F0cLBWFs62IQk$g1*5&1}gOIQsafhPuKV(Lo1;Qj_jLgcp} zVliFAjSG&oD8XPvy;)TD0bZe>ImZnh!nI1_gLDvwqMuX?Z-9vYvA7z-PzpjMOoh{G z*@5uDN3!jTo~$a=U`e&Y$zC5z$Y24eK*I#%-MkorBs#l-iDMjPX~w?{G1& z0GF)sg!P1Hm|}jn7VKs7H$ozwCfrZI^9Pc})dX#m1qvji5#PdB#+jNnze#P}a!U09 zLn+=8UH>5Pn|b-iY{M#n1P{c>=e&zBqK4#V=Pfh~cd*vt&g|Db;}U)=o5XRK54(KbMWnaEPH$JCr@{~Cmyd`ORE@zoUVC62u@ zH+C{dr88(~>JkAv<}aU;h|UC=Or6h+Xi##UA0K|rbt4WVk1t|%=sX^yJqL zicz%FKoW2q9APT0`amhn!i~0|c*|V@tRbQ`Ixj{)@%2MQ8k;T>S~%@cz1OKN_;?Go zF2E8nqUs#|OA+zYD@@=~19%{WTIcmR!}o?g1zLGhYJ97i1LWvMUA@~(X@&$Q@z4ai zss$i^x^I`6qSZHMCi1N|Lcf>%(*BN!Ml3LAxCfS7&;J?BH|Knk4fJXs|B~y8quc@) z`u(kX`{Ng>^S|+z>ZCHdwiHMYlhLUws1OqDLG5cryI{e%)ku~{(4`pHu(f+!ondV| ze@B|Z7uQrE{sE#81zQ=PJVDE3njX)(0ZfxeCq91p0{KKa2`1~2e7ssKPC<;`fl;u}2@AghdW`)P&tQ1lV6l}i$*P^1phvej37n*9^AhMw(UE<; ze%I81-@<8}+*NKjf@2$>m;Sz;g|D*yHPOas$=eZM+5ycL!lJ7Q4KNZ+vgU1?=~w z&6-zhso>DZCbP>V<8RRO$WBaAlZXv&L*o>Dru>!1R;|yv3d*5^a@L#`Eg$nXRj9ww z!;Pbz9z^Oj;_&)=eCH1D%-btU!D``pb9pB=0DVl-nt*Z^FV28}=faMDuDwr~>Er?u z7Ct-7kjP%c_k9AHVnaIrp>bZb$ z6N`-#A1dgYHngggQ2hMZ`HqE~j=EaRfLWznjVfGw(6L3u3IlskI+F0zS5Oa(%o!o# z{=M0s+8*XRun3MXN8}wio{tj^J)?T~aSMP1+^w!s{tJLtP}ShRoVc}h9#k6+?=X$c z8HW*5$h6}27@wZJomT>(o0FqnVLMDcVYBw{j;auU?vRO+oUh~ao$nx$$?mWkUx1nt ztgpOgYC@DQSK5>4?({lli_W>6!5A3twW2PwV_VxDR9g6dv)DeWJD(toT)pAPN!tsV z-YM!$LZB*RgRolIiyhq6As_bYZ|C2+t3l>>OL39sVsneJsX^)N5XaAo@+OiHj)rdN zrq4+f+{&m~B;RT8PGCSZY-E;ioAS90A>aaQC4%2&~3SnKB=5_u6 z5*+dC*`_a=iBR+4^HaX6BuAI;dNZbW&Jve9>^66osD%kUGoRYmG6i*wzdeIwdL0nj zHR2t+Acv^X9Ily1ThPdfL{vG(^EXjTbcmMfzEecR zVENlTcgF{dy6Ehc;Rho9h=h!%sH}UY`Z0Pha1!iyZ@n&KAFFv9((3(Oeur9;v9UAD zS#~@p1fSR3ko|4Gf1u!7y-=H8aY$HL`1tMmTEhe`1 zQ!uoi{eps*@3&fTJ(F+xd;_pr&BScTquE_sMn&1H*JTG_d8hSEVu0o?8mrCE=d;KD-n3W!Z8+9nFeCBkWaT`J{*H$H7%0y9JJZLLOI|aVD z{HESZD9wv%3_br=5T2KJelV@S-|TKhbd1B;LDMS5mYLQG$T*2r*;KONRO~0(x<`_lv=EYajO?eS2{EN5is?dkx`Q6I{oIBblHRQ*j_!-Z}YYm5`jG zEu$6Wk5TjJ9TroK)Xw$7X+uqc2MyWw_rTx}Zl82=iqGCj-Y(P~3SXG$Auuq-r1e5X zCbzSfgwfx`znOLUSF$c!}NoY%ZzI8pb3z)SPRkj46NSEqJDWS$gpc zR>c|^%pip(fL4UyhMAIPx4)ntcz@z~@Fjwcq%wVQJXtnCt6{{j;GA|Icm1@jC`GAb z*^UjruQgq2=M;4msBM5_n(lvZIfy0$`B7WTZs65cw$=IB{YVgfS7I#@mQ>3eOoKQJ z#A^@k}F48$4}p{7hd9OgxV*+7jB4La>o1Qw z(rx?!me{3MgL~7nT^QcUx8~w|S#+PERuQXkd{_PlDr7kwN^j+c{&cZ{K=k#{HC{A>zoH>9k-b^R!D9x&XsJcs$LDg`9J{%D47B)y1x zS%b}vkMoHLy>&O6hcWJbx3t(T#=u@?)dxcR6+R*0tYv}xEIIZKP+!%B+Y*n(N$yuY zrjWsW4$`imkS*=$#DZs;DzDB)cZw3m8x5S{@ywo4JJoeGvX#MB5>B;qLCwyI>zHwU zlDOA$^5{S#6E(5V=`T_Ty?;@C^GS4mkn|fcrmC5x>oO9Mu*Y}Kc3M_Y<*@wrb#~BNGQ$W0A$yzJtSpe-ne<-M%Drr`eT=l z(F?qle|uDxOx#Igt|)7VC~bV3evSyShSVpNmzhNfov4g}0UrHeNS0TQ^}8N02l{sN z`<-v*^{oA139`t*L{suPCW%*_H*|`G1FM1eiY@>9$6xpqe$1+2o+~aHSE}vLF5QFL3OZ1Ksz!8T^oPtDgF%XIYFQ9I3GbJznWl2UVbR3O~I5Jj!kM zN*>WXnY{)0l%3t!_RogTmgBTjz1bALV)wABVZek{6m z4tE&2R)5Fb_Ql*gL(rhRI#7bPU)uO)K1|M(FfaW%NxQ=+E4`jD3XxkCM4Loy>6po+ zzf%75^C+lkBbm>dUsmcl1GpGFntS&o9a%xL%cmm*-j@FAj;_c)xiC6C@DwF(ZE5k@ zI7baMoYr~_g@Okw%4r~G+fpE0J>@5Qt$O&x8MXA&uOIOTkb|wa_Et%0Nc8k5^p-W{ z1hwF_VOSbD>eeujGpc@P-_<$zHf8BH=5K8^q9fxN{`T#7!nW&I@mY32t59ddZ>NkV zB1h{H5v%6=BE1#8OvH<6pUWy$Puz9p8FE5_ zL!rTwQ6TB@w|PC;n578i05iJ@a5v#H84jM8&V5IP1^rRHQaYEY%xs<9M0&+GoWlHT z9S=zuJK<~cHK?6mCU_o!m>W*AMi3b02FsoDEs%bq-dL?`cj9i5oXRDyx_8mvG?jN; zL%4JPbA^s2Xu6ug)~4{qX_cX^7}=w9Wl9er3j#}xH(F>rY5eZrug@jcleIRlL}H%| zwbPnFn9+osKe*NOQa9F;7k3SE4R6>J$x^)(t1Hb~`tJKdAOBmLZ8KN^*MJ6{%2^-= zMDI6$f>j|Ryj<2gvZNNz+sdJH5m%Q3UPPA!KJs)hK7?f^I0#opd1>9z{g?PqzD6p zwRUiq@4NGz`Cm8Z%t^2w0j{}JE*0Rb_b1Y)$n{#9pkeWRkieoXKm&7>|BT2cZrUhC zg(b*LTN>B+Jbsvjgyk#ZPOsRqz>jlb)NxUQ^*@p^UllZMzoyBuh)gWiNI4-QVRZIF z4pVrFr*tPG2ysQXAOnV`PQ3aA01=C>Q~fyc>0Wt}5U!0h@P>~xP!-%qykEt4Ka(34 zioGuj)b@{&3H@mhe_cyVvYqhc;U#K*>GX84Xmf3_D)Hn9cwp+<5U!acuJ(Q2`p@FL z$8cK4+O(A~#*&&1?T#s+eov=}zC7gn7s)4egv#(T{gGYKJR+G1UjL`fKGrQ2-UjGM zk{$4Ui%Eafl1E=Pc{<}sdeTpz262F0hpR3J;UnI7^*1D(_UI(`UK~4m(*GFmk)&kq zYNh@eBn9ne|&4PGp7As(q_KM zq+XkYml%fh$Uz9|%MZ%C(`-P%`?@QZ=7HhHMdGqUn^8#EXhObeT^AtwFq?ulGxDKN z%|pK6st^%ctYF|Q^f7-raur_;D@lK>0@+_ zOEPr@Nn{;sDimm+qoE5zJpe=XgW+BF?GqA&jr^H`6b?taGsTfV45WE2545olNIuB2iyRE4 zkYQ^+Y^kgGJ`pXcB9o*iE{&7e+*t1SKZ;8On~~4yTYC18^|Z3P!2n(c51+b^nPne> zompZvP}R=wjsA881((cReffx!2)+xVo5RDmz4TbERVt{PshXrp^@_c>gf8%uY|-!2H}`7{`J2taDHgr{sJ3A(PHVt4-aQ zi6D{ViUikN5$v5sxGZOHqsx7d5OZ^7Fd+TRKj+8I99D?>`&)Nnz4kt@hZ)zK@CKo`#w!@y^Q<6h+Oda1))`uD{ZCHkSJ^Es^^n7%))zy+MrV;~r|s7cR- zPLIDptl(VRluzMu=T^S@1+<=uq<6X5j)Fia5{PDGW>^8>fjD2()_%23!tZxvicws# zp(5=dXwmq4qi~QvWg8A*{pH*!V$S#5oL}p!B`oU|Lf19HjNQB5goq08Xbb+$e1#*(SMU6s2z zK`M|4U2T}gyAF9pA0BXmSEUV>G-{D5vf!n~v4kC{#NTpE=}U}Q#LOZfx_)?teK3Zw z8ewh~H(jqX0?=5YBWfNN9$o%jDCbXG&UGs@^F~eYjB5{^eBI0^`kO}1EVa9J_Y_W~ z9Ge*zD#OVkt{XtH`m)7C;4O0~W-$cx1X3+_=&x4J-!QhW_ z{-kAil#rQw9C=KO>`oi@lNDX4@T0NPot<{~0=KmzokZih?LHas2L)WV$%wCU!9zph z;?r{fk+$T0WD{MzC`;jKGqtPe+@Q$DL}6#DQ!peqcx>FG%iHHui*dVT@XQppdC2 zR&iIX5zFoeBY`-mK2|&LCyB<}h^}+?!=6dCjBOx0FLdsSG-6qwcN#l@IXRLaH9@j( zqo;#|Z;i~kxUs8VsQo6|GT)aDUy3)*^|ce5!3)={OVrcDD()u~d*GY00Ld~w`g_)| zuT0kfdj>KsIzIJ3*!kzNfG*X+_etobn96{KlPL4agvRNgxxdyc?+K$wv=eKN;z;^_ zqG_V8VJEI2IZ$%!L1=vU=UHoTw$~K#{nmN}Vwue@FEFq5cNzCuJ)Yx6j>GUE_in$f zW4fpve8fI$79=FfZdSsmXd7_R&>HM?Q`7)2Fyftfr{t9Q9xMR2xn*$POA;%8QKxuF z^?*!N@k7Z}yPDb0M)&;PId`f%FsQ8$G2oH3#7XMp#$RrDN!dKUapZB}8lOz`_bPm( zKeo9KpB(TAA$39#^|-1Bxe#nFC4nU9qRG9tY!*72zH5nrK^S!fT8{&oc4tgChY)$( zXIrBgE_2j=1a5iAt937DVG!cyhw1b=DI^_Dfgb_+Ic@`xjX%F!(>Gal-k!p7MmO4a zuJ)urwZh#?T5;d5Q==cai!p%#cpQH=w3m!h7I<9fbP8c$l!pth>KgjA)79NPd-rK0 z)Y99u+ubp(l$_zYhCGBrveX(RT|5ano0{c^fdolwP(ikSNO8Lj=Pv9n-hpW*!o%`R z<7sx<$3Nh;JZr$iJfzd%gV`VHw}#GNqAuy1*x9`j8NXWi*9Pp@~uyL{<5bLM>M1| z2NJIc*EkG#8qe`xl}3`Q+t9{452tYkrtj<8l+&zT481^_J~*R&lxXf_^JG{$V_ewy zgUIT!WQnxbIAOw5YqDo@mU7~~*i`u$N;{@9E;L>gOT1U|YgsJ?nzqWW9Gr6_ElVSo zSnlA*rZx$s^7@&465?isEvQ#_VVm7I5NISQ8_;_SYjO+L%n>v;H238USh(i8_Bk@h zW>bE(R2_Z!N)M39)T@oh1bSon=s4R)MwTuYE=x-GZ!C_OqGjDo!s5rn6+iO7tz^Lb+~KASqR zA1Jh4hn_It&2xE~TzYSSC%eIci*K5sU6e)S4F0%#$^10+?sP)Mizmjs*Us?|nKE$G z%LLS_B6}|yq--)FA!VLTj1#;9*-2NE$hb3^W9^MN)=Dph^*5#N;zIs2l zm@7zH&CtB2I`A^aJ3kM(zh8|=b48$NkoN>=$UFc1g_Qszzt^$}v3Hj$Y4#SI=#y(U8C**eqHKbSzpfCVj01Nu=PP2Lp0D3TcfNH1Z-i1=MN{hpmfm z4(w;1-t0(1!fWJ>uZ@V8rfU8F4=@*5ouhr4@j*Trs-IDK>C-={T3qNfQKy>|= z4}P~tlNhUBa_fg%14F{pm5~|JBAFAO|2{w+?8~gr(vN$@dug{ICx_xp#iQB zxwj!FaC-$^Jd~nJTf%EQj#7qmHyR;cx)IChNb~O=Mv@y&_~ID8K~ZLNJNq-Mc^(lv6p_`ez@|)<5n|_H`5BAfpAfdM#3fgedg6hGNNC>6-?c3q<_(DVp zH0+58eSxm5U}p^~Eo%(i6?^OD)}#(0IB)2)!)0`bfJ!&wgWKja@HY`aforS?lwkRB z-Z)H6mJf^Hv2SY<hL<3riH}+kXd-rt4@$VO~?bb#)ijfw(}UJ-%GYw+=H%t9cv0 zz7>(#Hh~w-QyQ?%YQhs8|1RmAUnY*#oWD=X=Q_|ma(WOJcPii0!x~SE=m~#SWt54gy7RfXzjo*<)#JI7V z;CsKm#JFp(CR|)VDqDj{?1ycpZH;Xm{<-g2Mb7=c-3%;RHGq<|#E9au&&L}2?k-?} zIe`OE`gzODULIrV^Z8v&C+_KQoIYsOvCTzH#_(Q8Kh?6^bm;<|vWE@W1Cm79j8J`W z`?A`>vRE2>P&d*1*Q0jeJ>id6>+ID3mj!^5$2vV(0#=mAaoDR{(An;^@_ight~<0v ztt{k(%z*n6T8x9ccCOgL1|AM*eP&nzn+1NRRz&%XZ|h)e5i$FSgW4!pe4xXRm0?V=NmS9>-kkH0ZN*x|7`Y%j3S#o%-yQuVl*LjGut>B2 zcZEkEs^{tUi}yoSU>_{fY5X!{EB_cj5j{fCR0FgZ=+P=@`@GV3+@zRA*I;iN8--K9 zlO8@LV2XHnm%&sfRA2MXbU*8A-sF->*8-mNtDV66LebqO0#|QF#8; zOexbglF0ggcdXdHz$TN5Y~rGk#Oc^iQIx;?#+xj`U8}RK_rn;7n`hNG@zY zH%o%+?;`~s8HW_GX8=a)#Lvnvb9*3h+Rrh4;g^F`oaskwxihX?EYMp2)<>2BvK(MZ zsu6-WLsQ!ox(?`>rDgt}J{_s;0xhK4?k*%AxDngj@764kz~2g+8yRP$6CVa1E=F6t z4}rbVS4pp3;qR0C0-}zZ*EgbyfFj06$PckEPQKLt?MXBKt?LI?q3{5 z>zr(w%VgVDW}&xWaK1B~`UCv6nC$w6D$MkNijbVbyVD-xVEw-f~Afrrs=U+*gde(R``~_w7iBjadh~@7ov=leqNe%cLFd z|1EIXlMZxj1x>TD#riL!pty3gw$Vg`J#JM$JY8^)Jp}u%4pO@H*aP1FO+FV5@r#dO zk>DR*-N2PncMi>e*CWLY7XVb;I!P2=rH;T#Fm%cXbFzSpkvHl>Ff`PfU5G<#e0 zNzZlt$d|khGhmjF>X#GtXTD=44~}FSB#8w53!KAPE8gn)99E0dxMIFAYO$f|fAbVs z4cS_fh5zV&1FBZa@x_+wX6z{VXy#q>-&2eAzdpgF%@2sYeRM2IK)pu^{*uQ3#vj8a zI(kTg?VV2`4WSC7Wc9lmnc7?+y;5-z^u`iV~5;ynia zRl)l2Z*Sf%cuZ*P?%rQ9@S8Sld1hSo$`O9vKV@Gt0P{RXm}X$&I1I=-=>@s3mbVTO z=0MrW)6UEpcoj!KLUQU8s0K3bf(9;&337207m;|TuwwxK+`^ddXZ%V29M3eX5Wd(5 zT_#T5a#P0-EFk9o87%sxI=$Q@HH1X2fsgnE6ow*!?_?cf_Zp|Kz;H6UJ08xaH@2A0 zd-l(5Z`?_c&yN#*!)jjKX8_?I7fLcdFcP1mdT&vHQ)!-9`R&1SuVr#zuOTMx!%L)J zvLa-WXp|C6!EpXN&vP!oWH~OO2S3}0V}0k|>lyDe3QjHcD#LxzHyE(eo$>`6v+}n; zw)4I0|1#2ZC!InT&zCH*i}i-*U<6(!uTku%ob^Ib#IdS6cmR~1%mpRDxwL?R$&L%HRr*BM{E=yH;#*OCnN;p7T4F!dZ>b{1y zK8BQ={&=pEQjH?-xDFbA02`|+{FwU2C^zn&)AbBIC~5+_z=RPI#qnH5&%JugXgOMI ztX#1EycVzD<;26;;0s}Mf>Sm|Wewf;?mhbA2ANT&t8Jk%*S z1x)VlAxx43dJ0S&Up%=MD^J~iEtWg39vZA$2%%ebQv6ugw0hcW-lHK!mAiHNI)BAY zx$mQA^z{La54hK7h~V)Lpwb7v^nWnVCsHM%ykeTvDaoCOCN9TMGiZrgIty+FsDXXy zY*uH_C-A^BHSi6)0k=nY{zNL1i;+iYQBw=u3NyLVO5GOQT1)DAHC|BWiZ;kg>ED2Y ziS~n|fHP5qPEN|^VyK15z-+(1tb0SeK~@NrfP-;Tprh=$&|A6SsDm)s0p-X0N4{gq zhMp=hEFWZzW7zRy3CO1hJI^Pedy2JC=YCunm7d17Y>4AQ!yloFkrpBuST_7Xf3CYwz07!|cG-KKdmeVWD)kki&Uw5@DUaeKR zLs%iCDQv~mAsxDI;CTh_0=s}dxtp+Luz(Rsj?wu6-hF+L4gno3^3l&U#s#l_E&PG* zGP#%W;nZ-YtNS`Z3<8x2X{?Hd{jU>2`4@OZSZq_#N~*9eCk|gxCEBRra!9#UcG3S~ zHNQ}Ke}5Zi_WTq&@X-tFT)Rqnt>ld;3O__;^u{WPINZnQ7x%mKtNn$3jR{$Z4WGI% zzvQYcPTNL_3+mz@zHcC*Sl4Ib|0R4?Zr1HV5u!?0gD`?p04MjU?Dib>T|xCuG#A>M z8TF~aof7!X?(yn;nuPPpu|fj}y{R7!(AZ(@++igXNlQPj%@%@XYn9wMul*MpSiRsgdpqKa zbX55Z&x(VlolAjnBhiP99?vu}V!ScdkFB$Rfm|rkQ% zH{{~=x%k;1-%*#47Rq|tjT#<~TyJ;+WBlDJ<88Y`q^QJzPS%u5-{I4VWww6(zm!V6 z>5TZkXnXvO4KOTFPf9@}JWIH25%~+0zZEY2l`Nn2*5?+3u8lYMiEblxZoeIOh07pS zaRMkv^!;!i)N=PO@N4}l$or`__@%tEA9s=+aWN0sRQPHd%=i%SmHqmVW7rv_#B+g9 zVBHTgnvO>B5BCYGNJ|r!F_DOIjfUd2h3lRol->@X!AaMf3aR)!fYM7=CiTl%T1gb# zj3$RWE(Q4cP9CMw_@fHA?-&iFl1YrFU^ZI>o6bD(46$S=&x?P~+kA06KS`wDS=~+i zA+vR&aYl>G{BjPhi|=DCe7zs(YVcLOcQfj4KUhx#cDg@b$jI3iSnVaO_rv6df04{p zlsZtn)fmg#J}-LPtm|4jz^dn7U7PU1)}KdsmqgiMLCj2*aBKl)9GlGijql-q-EWmR z)qkR*Uv&tU2jjD!uf|fjfw`5-gts{e=Dd&2V|6w}l+=qSVSY!Jt-Pi8^+zmQq*!*+ z=YNm>TZb0%a1BGq@`{QIH8jjHKB%l~3QUIp-+HC@>!8bxc<$BjngA${`viId7t-^uL>OxjTQfbjo(%r!8Zw=!vXI4S+6s4b_zPX{xyU&Z7pA|MS3QDT=4Ck?8 zQ{4NFpU+>g(R&>Wa10LvJqptQ1uXz(U;l0OP4`cxtQAK2xfa6YWiES#UNXR6OlNyx zt&~Hy|4EZ>TC18ZX^Kw0*j$tE4QG#ZKe4I5fA?#ytTu&CrPe5Ui53qBO3d&^t9UOZ z$jjJNK@VH=w8%bv{EwuQFLyV_8d!|>acCK@t5eI?ePea`PR$QjsFNdZsVdE%=v>pp z8X_Sbp+~w~l0+=;8eMPt+0iBY1wkn6FYjlru-h`7;bp%csOS{3^X3(@|M$r8b^h+_w7n1&#T2X$S z@U0*G@6fjDVtisGA^Q9~PDB|*fc^9bI1Vg#CM+fAG#^QZm7twe-V=&dwxS@(2#>T+*^!z&+~g= zC{$Jv_AjtNJ2Y@N<7HDfe$6+W}ha0auL-AQIe<8@pDsSx7S9=nIB%O78-2xtW!Q|az7g#d(+r* z#5Rv`x}Qi$*twUcF2v|o=qfGD#fj4w>BQ+aH96F zcM8pJSS{ZE@=NtE#3USajK6|1KYUpDlJ&d(?a+tm_OzoS^KftECmmlBCno|qjd*ZC zMEfRXe3$K}r&qrUePp`N^9P|DO(iI?8QfjyEe`a+hqkCLu#HSB8Iu*yfi1#gh1bpS z@_>Z#j|^aPMnCx7FqpQ?ydV5*7N&m+En)8mbsr>y?)>$(pw+Cccf-V19x#21DgNDp zIxC}*51Mw~8-4gab#<(kJ}CP8R&6Yi#3Mlhf`qC&!ux~53*=4wc9& z`agJ(>E8EY7Dmb=<2Y?)=s#%`k37GDY6=+aoe0s)S4jdl86Qc$-T5qOQtl!m2YSA8 z&O#eSz?QxDqI~x_0!#2;L)bHCBTe*vKzC|`hfOfFyXoW4nD_PYcjWd$AELs7$W{R5 zc0GS3d{V$8%1aX?W|eXAs~B^89Q}UTSu)05n;TdDm3n?`Z37s|k$B!8W5v|!;j3JJ zrvC${F^l*LI1Ax;I!g(qIwH$_N2yt91BcUQW_9zPP@oO^BgUdW6`O(Sj?z;FU6JnZ zx28Ix>IyQ3TKxiNSmhl4OD%9{BYdxj;4vTRn7F}HSzYtrz2niEEZfe}(3JnIFBIa5 zSrL!cmDxTOUx=jt%TC~6TN5Wt+|bEd%^9R+J3+Js$3EkiQZ4_T;Fh0AlY*D}u-G}B zf*PsVEJ^1d1}h|A1BiHzvY!VoH1wKMScX0s?-S%u`pjV*_>Sd%4y_riHc)ulW$71_!cP_ch<$9d;3}zQE1r&03 zdRe;Xl9BKak#@Ad;g50TB?%S&{_N zCP&FhB#eM$2?CN~kenoElp#pYd4QSjZ@i!HJ>Pr(J?pHq&ROeS^9T0Yv%9;dx^`9V zs_OzCm|u*{I++yV#83h8v;`q9JxLIkI>R22^v#CW1=9nWv4^}cRP zkWaL_^`I(%m?$XXhGJI$J~bw$EgWuqOUIooXo-^INmT@{no`@f+g!ma^?0$b?Y*J( z;;pSsU*zxk-{)ubEiIp#vdYWL_p;v?JRa6Sh(0haTY#|e!VIAeL9TDM9u?Y~V}^?; z^6pox+iK`?bUNMo1uzT=FL&p$K4W>&iHtxP)87^*yhKiKBY#m z$}y-!l@^!zo!+98wdhX^C z|EMUVsr*YAkFfLC{_EY4B1GLE2&*K*WZXMjZ*|6hPjP!1X_K^eSRaR)S3~Es4+~*o zFd-W!*JH)oDJEL_(6Tw>p0My;a@IZgdHVS|TNa=} zU2#cfNMxrPeGU|v8}Kaw?NEJBap4j`tU%uhddt5B;T4xY3F`17N)HFV4T;v2puL|_ zPOV)ctGYA9a)@4y@i2mgNX0&!vy7Ep*7 z+Wbh+;Z-A^f^WI2FeL266i3XHw|oiXc-2UCj&LYhzFxrNjd=?SyRN|=Xpsf=;U1uy zoG(AnKj5Qtd#E(%kt~sTVdb8KZ{53?BC7{dAN5WeJ#(_?o))`3`_BJJ2d(N;HHn^xS0lYV#^5O;mx^{^a)XF^U8BX(ly=Gir@IvVOLc-Z^D&wETO)WnblWz!}w4i$1ioO_M>W927idFZn?&s)XE4vFRqSy zAy2bhmavdosL1E_EP=20akKc?{85cv{t&M_fpJ8glAlR7P1%I8V)-4C!zmTlf$`iF z>C#?mE4BrOO=sWdHM+Lc4MF^!CLApS+R@Lp{^w%-wtjH{+-%1;l-lt*ptSb_vj&4KN^t!#y3hJIP&H-j4w%Rxqc=`E~ z(t8c~v}WM1mW=!uF4JxiU6q#NySvDo24QVv*N?zUD-lX8W@0j@EI|2I74l8RN+ zu4>okH)QMQ-EI71KdO=ynrT>0%h^in*U>=eJgWZaXFm3&=5@~ZqH#BCFuR4}6C@+6 z9I(18Qo773+%q=b24lN*3^l6VS?|X#d!_e+DO0Kr7VQ1Q7mQwSHsXA`INfI`S*ZL; z$<3Dl)X=?|ylfWgDAMQ|8AX#H?(o9rM<~5hj>y_sO~2gy>2)0z%)Om>e6YYvpFHm| z)DE^oqy&(<`dLh^v&nBLbZ z7}2JZntMCL--w{`mqN4O9#DYO2@$B3XL?FI5ikm1Q^85}rdu?R&r5?jz0oBg@@b&q z-7F$#TLmja{$Al6u24uA*6|L-nyX{4+>QTL?Jch~+7-`?Uiw};Bz=1c;3IL@Xor;0 zktlUm2a`_oWgTC_H7d$V-u?gXi3bR3ZVI5vmcYVu5aIyG9w$oJy{ehj{Oe8@uuuRb4~19g{0_ZwOn{N0@05L~ zn>?!n{0FDhE8pbcL36XUwssPc8~m+oL0k+z5nU5h=`&~69yO+0Pu^&_}G-+qbpjjYJ$2_wOxtr24rHVgfcg{c?VZg*Ox{_Pb=vFkus#uO+S3J1SG}D zzrx-^D5N>V&A9}S{G>bi$3ZJ)rO9b*{B&fsiF?E6K{P=h4z_#B6g7KdZljRB6y#dh z%SdaaGxTPTOkIGag!1=RK?_$u!?W0_IFS0O35#kx9-w5ZM;VZ?Fo?T*14DxK&+-=9 zf0bNnGhB}$h+gsBH~zDh0R61ZuncANpaz_-$xM}K_?`e}z4|>iq${-fX3svDbG7ts zAOi;OaUPssXye{J5!x9x_%U_>TLRQ$4(RaAI$3ck4<$>(x0MEA`a)GUo#+95&NX%dwN?_-MJrl*rRb#VM0x;H5SFER}n&&3QJ9F!j>XI;2)nmO9(O<2v zZ)%R^6Nd!6un%=*K>k1&`u6d$cA&s=MfHy|q*f;t>He1sk#oE(%qG}PyE}$|D%uf!#?5Q)W zr~khGkV=O_z>;Dz6ew4Gd10um{tOcpEp zaUT>(7O3X{HMH1*?IHmSz(MF2J@2oMFszl9g))Yzv=c6*9@(Zy&o^|mzDmcK7;L@o zu=&Yd%_~|Uc&2AsDQzHiGzx?oGk=V#&|fqcV84keEhawY*wHQvX(G-zfOT@9D{|wV zzl@OwSxVidc4|!;7v(hdd1>pY7H+YY9kkFbcy+$zg?y1)lR(ty6hs z-A#I>ek~M35%Cxne*@C9(4{8}EJmqyp@o*?_*6Psl^<&cH$}kcqlh!4;t(Wb$NO)z z^iKq6qZpCx0oIhL=3qsBxg7t!9c%im=Pvc$@|onJ&MQ2N^CIz=%fu2xgd6@kzf`4a zHEG^RD2#Dl61osuF0%f|f)QOJJxBAtJvRK!li%c!BD@^boy151Bnzp5cE_yoc6CnU@4zQ16-QC6{UybmN$$ zPs*0+v(bX#2de_;xNW6psUbIyo_CrK0uezRiUF^{Jnq+N662IMRkZ!Q26t$|9-MJ8 z?8GphfuIv~1va8d_gN;nHg`xE*Y2fthT#jaD8-%lG&eCXCq|!E9{x%S6%s=tTAv^> z9MW8N?|uPwwb(;pKBXA!W;=ZT?O!Kl zWc=NA+rbI;DxL|_kn~ws@uX7gPut?u>slS4C{>8S!y+3}Mk#R($8@E>13>e@YD57N zq0-M7LD;Np2I`F$KQCx_tmk(9(TR|P{LEwMQ%WJBmS&+t`Wmrj7yZcyf|S651mpND zo8^IcR*Cf|J~3+Bf0hUlIH@r9^;?|qEJ?L5AJNa0C&vuv+^WScy!wB8;q@}G<^Ba1 zy{vx-sVnQ(3T zWbJ4xzmu>|9qAv~NVRyKJz98x(EX1++tq>llw$q{YcD>iA2))igq;#@X-uqyKslvZ zNQ@B1e~xZVfW}DU+XpvKVmaOFO)MKiiP`s8F~$pXdMRuVrzPp9G#~MBVEr4-`Nz=2 zdn!@|&K>dyD@fjxDyN%MipDk^dfIzipZY!4oyA$aeb|!{yheT4)9l>II!R2>aVrvq z_hce{%;Q#;mE`G@-%;v1N~N6XCGT(J!O(HR*w+E=+R1ucO94#qanpri-{d>FKRWZ& zk1|d-NV4NGBXT90hY7dBK6NIvjikcLg*?KblbG_d0q=$>>hGi(JHO{3gHQ z9Pm9CPz=O+vN?bl)y$I_a}@ky6xw=4AxQVb+*UblaOWdewN1X{%aKvHy#492J&3P0 z%|xbunYw8>`82*W55Qx`W5Cu3W2I|TT{5ff;Z~9P98-xEHTx~_-mE4Hd4QNab+x8d zeH1@|w(Z&zH7I2Tr(csmNhg2bQ}_YQfk0f@xM7-QCPhw2zf4y2S35*U#?#27B>q`f zIPw1s;{qLQ2Zq)<%*8xvL4>O0J8GY4OP;~$U2=dhg$+1w@Ro|^)7vsCelHDiTXeo( z4!$PAh%!Bf^y&4L9ikE!<;U=vZf}H^OxlK-Y`~XpT^9DsefK%+;t_H0d~MGv@d6nT zNNc{>g6}UZ&uFH7?Z%(*xl6p(KMKzBz@rbD^MpKO{><+g&F_&vHy4PTkZ7_yCax`F zr){{k5VQ2?z=C(~P7$bQy~}?&j1_3PmQEs8Dga+{TplX(PRQJdN|j(|t1(F`2uZ*IO5o zTN{17;X$xh?=#;wxuwGVksxK13NE0R`yVmX5L|}+k)#x#!MGaU^MABL2zq>b>an%$Hx73`@Ua^&DnKUTG)MTj8vNOA%8w3SY!0iX(okm zf;;5Rh0>>35cV4`?lJ zKx$rkKh;09cEcqHpySFDpOs4_F8Q@@sH7qG#Cp*1W>_p9Q~Rr?C$IJb8pdAxv(__v z0<}ww@dIC3_SU8h^{G#1%U8>>m8PzYfZaLJWa~r2eZf&JNyxmL>h-^{iiNeEji( z%0G)5{atj-di(3&uL@@B{T*bC(yQebP)RGR)*VXEZhWm())xK9nOkc%x8{y%?U0(3 zt>0F=)OYMfYH}V}M~?-0&i?04N!BNbn`g$umXih4NdDhklq*{H?OlJ#7K=_~11oUz zsvf#Ovk&{wwj7ec;hg>F>{T&+Vn$M-aX~k?NMvTt*_5!|9^e*hb^OCK;C*lQTA-8# zf!R)R+SEM*MSJ;1r>qqBp_7SF4+re1y@FdmO+LY9)jxvMqa7-y_W4$Lvx~4b_&ovKeV~%US_HN@hgeAzenbWaL!SM>s{Gr*DfHy$oeq|^8hdB=%Jk0* zcbB`GTt2-vET1Sknvy-TQiN6=Qt)m6cVrPxHYRbLJwvVw4JayjTD{;N7bXFieF6f^ zT?J=R*GW}ojfmMrXOzw}B2V~dUX1k?AEmqF@_79{U+~#hym)_&c*Z6d`+c-{ffBk* z>sh|_chTO#mstR5M}-V6gOsipKeMM+6*#%3-%#&cgX%gtp%*=!9=~fX5T8B0;@ZOq zqiI*5%3A$$VM*T6kGHdrA`wcO{ptmZz8dBI$XVd<~ zkH$&R{*V5JhSZY1M_sxl`y&#GG%@!Q|ANeeznA&fn$~q6VM~`XKNXB#W<|Qw1EsE1>#Sr38KG#ho7e zp3JyrhG)6|=iVnAy6z`r6J-3?!#wwzev`>TK5Nm-*~uErPJA8Uz9S}DWH zl-Uxe&xQXr@a(tv(|+ z;~QHG5n>vlG`ncuLcbLo?OpvoY5u&xPBhJL<=r+sT1fh!FW~^u30od+j(|-d>_rRi z-R$<|*7#A=uzzVT&{`=SY!7y6UunRlI${{?g-6^+2$~Qp6%5{Vqd~QXC}MAlQ6S$V z5C~P=fLq`&wSz>8NK6Gs_wxd!cgL_3NSZuy+m(>a{udB&B~-&b!}`IVhDLQ8ofBce z(r@otYmhlRi#*vh^87uUu=V;(d-I1dw0dvF$FyC67#h$MJIA;#c{*4vvZBJlP>eBu zj=<~V7jByN1T*IXV3E%Y;O+$f+4Jk31g@Phy<}3=Zk5OpME@LVdL@FGLX_iA3b$vM zVmri{&n6c-&Z82{y9`%&72sPw!Xt@j?S{#$y%@=cR^DaHrigvtTApmd?M|-v(iWD% z@A)Za;jTtY}>xisGIITvTm4UMITqB z8*%4Kept`J@YRe0W0j;KYbK)z@Dka^N@O)7N`b-)Bv6AE+cwEf=^wmd1_oghG)}a~ zagvsx{MU6t%!*7Q<@C@me{g&n)p2ED<60!sTY-=o+WOJx+m*crykka8OCES?Q)P`z z_?3K%j|3wlR@VL2?HA!S1##2J5>w+{k(`|oSZIy~?U}M%JDAAAnS?z$vAO;CJkH^9 zC;akMbb{SJ?Dih)CSP^dN2m+kAs%Qym6j+qatu6{#a^ytY1|>AKl3*erZ-0Ksgz>(ImR9G<gtVhNXP6`?@H z7Fi9)3AqH${R7xe91A;o7`+E<9&e(=xw0g(O_6lK*65E_Jpn}H39(8`D-3=x@kB@H zh8@XFeyTf(8N5O?7NzVTL0@Ug&qyZz;SZ>BWABX&SZQSRSP2Dujva9RX z=xC6T5EMP6DUU!1LpnV;>$i@D{CXGG&9kJDI1c#=`E{BX*=Ql;=Nv}fA@XC$Sems) zVQnFNd*y6`(~lJ~UFhG*s)*A>OdUO4J6!sfJ5?gh^Vg&A%WcRUef)3gD$@RDUoS>r z?Q$V6N|`D`p1**KvXdVw_wNqrCE>WJ%29L*vZZGnTv~;IJj62wMRaC2zK_6g_2-#* zrXOl+64i%-JX9s}GhCYebkgswLNNc=tjhn)s{79@%zvLXsCU)D1-mkj&xUQ_lENsR zeL_RCNOvx(ugU!ge_zkv1ur|9yqZ?tG{U)nfq zv5i9-ukwGLRfM2jMVzbMuzc#VDU89vY1A^6XlFeB(}tob8{dj`9utkjaDJ;Ya-oVb zt>%U4{z`HP?x$ymwMD@||D?L=^{q1axp-fx>iatj7ZwxN#QvGn=YWEazOpHupW#!{hZ74du(J zVYE9`%po8aiLzYR8Fq=Ko%9tiIX^a^j6hk|2EOaAZo`-BLKhyfSO7Ls7J%4?{^*I~ z0~lHCbFw!n1d|E5nJE_VLw=tSsd9vf-4Z|=bpR9l@RjB!zCb_mWNqbmjeLJqXxZu+ zF^dWD)j)43reR7)ScL!P6XfY|xtB4s7ft-SQ0+AiV@^#?H!BvPmVrQB=%uGknCTc9 z4$ZN6M2%wq_WF~OEA(PAqhkp^Uq0?vH;ae%IN=fziP%iC;m!XXl8Itd(u%Y(I1cM9 zWbXAUVB7_j3ICiG4mtHp{loCRT<)R}(be5&%x|Uv!jJvr5G81P>jJLks4l*53O)2{ z6^8~9RT!?fwQi+ew5dB*`zzk6xi-!QD!c~PNks!}1IHD=HW2OKfJ?(kj~9Gxc)(}9 z9|@n`GqI=ECNFo%_m`@UE2M8tBi*3D_knKQdx*h3P_J;IM!mcywcfmp$%P)6SfKjN zsa$0LHh2Quq2v^X4zJ>5OdVJ>nts~=U@5O4VQlu>RJ(2CFZ~2yAVPBqn|>bToR6k( zG5mdnRGV4-`Y5TKKi1tqa7PIHiIcbu_Q-z@-*E9jVE-C@%E|lS_PpC;8a3;#l%9N@ z6cTK$fcHy`E3II?{(6zs1PlzM>z{^duXPX({czvuyDzX?)y3^Yad-5oPUT82vB-`G zM-8>Qe^d0X*$&BUr?^z9LTcD$u88ACu3T#V76lZc zk3#zH0uRR=US{9?tq;>oBcVRi{Zi1@y5RLb9caK@$T>N=W@D<6w}&_25%^%=C!#Rt zcQr6tTZhKZmhV%h=;A*rzFBQRUrU6IT*S4cmI5y4ef!W|a6_ z>;9W(7l;<$saKIPrA$2b#~-=8->e8ZChyWSAKC_Dj}+XN(HYjA7ebHl?Fd5Veq3Gt zc3uJ)o(gA@il%mRc${dr$Dbt4V)Jwlyceaf6<<2xazqh6GH zMZkBW?S-vf@az!B|IBS)tG^Z9o^rp`E>B}%1eDP#17&LU7jvrT11f#M)8W5AGeMf>~3lLV%NmPTTo z3Jv!&jGo|$c1{$^l^D;If%F(Yb%IQO4}Jo;W`4g6gs|phAM4Qj`(Ot4w$wq9L8Xm99;l0hfuq>6h(AhP0+ zc2r>6qg@=a!M6<0WjaKRV4ceLKiKr!{g-?PTzy%d`#=X|rS4w09q=Q~#5lG0WhlgR zAIFToF#?!f5YwSZuY^U<248~J4gLN@$wXJ=kg_7TzbSioHdF|IUYU(}i4z9@`6uVJ zo19h+rP?EeEn3%plSAmw{KeU7St)W&?~aDMCuXqRXJ ze>s429i&WK*oTEEHt03!q ztnHFvy;%50q$%(KG=GQj+6C^M?DrMI6O>urzQ_hFq6XV$8V>LK6vbqm4u9ji3cqGv zDC01pV(cE-Wu1~@n3!@`>zd@Ukoni|cZOiyG4x#t=jxDou@_sh(gi30U8 zx<;XXeVyaVat4_A82QzD{zCH!3|sESdYKmFWig;cbCv7FJqKY4G}+MnLFT&Syvvsv z39vO7$rie>;u7{|$1X!`gC#bX{1BR)F5oiB0Ll?Pv8LQHNtXDVL+lOOd^__f%;mi` zR$(aOLU|DO%WLZ|P~(QM|&ZkUuiYW4ToB---VlA@QNgh8hV)L4hn6=m*4 z5*jC7HA8E?W~sxm2Jn!jfYfrKP?qcR@Nv?;f!-w!E7ZY~kx0N>mKTz8EiR6%Rp&5I zoUju99@Tf6^sTjx(ZSl8jc?}jKb8Mzdnk4k)(_G}I%)Ac9mxj?&C8WYrha{2uFQ`0 z)pJ-TsCX6rqvIFj(3`dhdqft>$CD8IoAnPGGuK`VzFw|9B2_!<#A}$EMjFlfTQaHN zNZhdR$ujjtZuGQyPcZq<;dH2B^Q>w0@LqPwGThvv&~lmA&di~)ZhSHPMVAF+)D{#O zKJc|_`AK@s>|H0EjLA3@2`5ebMbC_I^U|KgxFiPH{Vu6X?Qq&Qh|rSS)tE%!k3pN#Y*7o7eEgtydJ_WElBX-i}~%;+;kzdu6iM?Rh}5>vNG)W z@q9dP1g?{qF7+KTeqVdJ2KtN#ONxE>a?RY&=Rv=)QIWJh1|IwEfwk;f|Ew}P4h8eX z@`S*!ReKe`Op_jmKqL??D6|H9O{#EkS=aYlT(}*&sR*67|KSnAjTQ>K*}+b_FzxDE zcS?I9J(JS1zKytW#^6*CPljQF#@8Ov-BD{?TryHqF4Vo&&ZeV5bQ?$vNLtwEs+Pj)XJnv|7+wWsLqu4<)(AY*wJwf8kaXr!w<=Z7&pSpY)7lJMf+-;{JIh46ztItm4y`ORGD8rb1 z7HEgleVC(0WMp$m-5N5rJGO6Fjcr;uIfj;_lUCO1X#g`Ww@Qx~!0XFkPgxyO5KGC%|I@0x8+l<2JwL?C z+;>RR@7m5j*V($(I$b0+p%j0)ezks7zD%*+LpL^g#t`~x9tOPW~{UdPT&pPW! zP>wrBJ1hJ5DeJ1HmGS$ z%?qlvpZpTTs!OQcm5^wY_aAfO#I+-Yj`LOWic{!^wgyX2AXyz|UXch=q8l`<@mxJY zF|_naIeE!r#0I}#AmoRp9-oXsOmoO_ZCGiv5IVpE-Emke9)l1DmwM^Rg{7%ixjoR% z|u8xB76u zH7$$e1lXW}_}~~c?P?g#rf3ynAhuYLj55-YrX_oolQmLv-m{S(inSF{I=B z{uAK#@z;Xq)jh2#uamziqL$f1Y{C07tR?PpXQ~2Wo^|=D%d`7T;MTe%Qp@r^Jyg-W ze15!hdG$q4yT{+sB>m`!Dr=rJ|B77Hb|^6~PReYdWu4HfboWi7>U@jB8({cqb!H_t z&G=GEI`fw&wDM(KYx-jPPnucwE+(xB1j#Q3ao}mgz+vgh;B!o;-cY<|9e!BRz?D$- zh|utUT)CE8*EnD>f-G zzr*mF_=-b|0@8#z#NJzj>EC=#_OkCxLF)y?Cvt-!j}aY(^UNac(D9#Q9h-e%4t)_G zNiz&Pai^IKU$_ijYJNR^s%pyABJlbCIlh5N3%f~7k}t=*;ZNwElE~cqbg4$_Q)F%H zNn9BCvnSukQX8K@Z9E5t`NBVQ2|ks7bO{C16L@&<+~lhMRS6qUz~0)8;65mo^1+Z? zA|xqL5V@VM=REbWiNVh9%3fiCGcz!`1X($Iaj!bwC4Rj8?JjaR+W8a-p|8I67(K0< z?Lyp87vOO$`}PUTCBH1{QySncd39uNvAZIOeP z_va-R@GH(4hHN}DWUQ-RxNlNdEnVLp=til_n{>yrM!PdYX8XStx`kxSrcBPuh8U8; zLdrOb0!;u9-omCMOO@M0I_h`4#>$F1XcckzqjE8;JvcQPPnJDCIQ-uGC%tjJme z5mo?Nd`7{03PLPK3ol>}T5ws-zf6aJi7cJ`LM4ub&h%M(s@*xT8B2)r zaL0(>pGI^zM?C0#!&pWZD~Wf4m$9ki=KLv&Iv$_IuF1~1&~iHQP4kmrfeis1g(SMg zVJgnRKnGJh1aq`dqSfA=%XMEOY&QNgUL{`(!EJ1{aL=!5KVz?=68hkA=m@oOMvwB$ zgYG^3gE?ln+a@A}|FfT`^u{)ASA*8=uThN+79T?lx(P2pBbyBW>yS#dz?H<=wn`^5oYnsc?MtWD+4(;nguCX^i!*`aWFWXe>nebFzdKtrP6|@_+1KWR&KmC?U1%=E|$4IyCi$Zyz~iF&~%OC zfRHE$NP+mB`u!K2(AsQ_XY1WomlLHB{pam{uPHP2)|1d-gKrV{UI}(0ML=PZkxe}YOTi5YIJ@A&)OGtaEjIO8e0)c{Etia3D zy!UIX7oegT*7WQ(_)1S~Lf-p>-xJvc+zXCu-8FLUrN^xKiFD~y`E>R!D%8L)|6KID zb6CJiiy;=8oy3W4dIqA5g9+9l&kS3@S6&jfR}GX9wx^fha9 z+d^gG6gT7pGS^O~L$DbBklIblnn`Y|GInkHc|T@AR<4)!jzh~4szqK?^Zuo`4leZA zi-}580lF5+Rfcw*#I5>04TGA%#_SD9$lyw?7p1;K7Z>x+cRcL%I zUqPvm8+ur`q}Eb;Q&RQX^`+j?EbHEbY0)7YDf8s={c4rd@#7q{PKe!{kjv}Q2;wZy zpt<`^*qHi12O1#!Lnp@Q~FoA5H>FDvyE=hm&bUi)rnPtW{S4ImGzx<5X0ya7hOcqNLqV%^*t-na4vkQm!TU8zaanQy5Khs=|WT^ z(jaCNzA_$o`(-xc&UI|gZ4Iz{zc2@2-6Fl&Z$Fbzl$Uj%w^>(0E@!0b!DoObRNt_X0+;xy6Fb z_k3tSdG(&)aF{s1%;|}!`{{l~f+_M;%h>K0-312?y9_u(S%tH9Bk9(?p2M=|)==+# z(8!sJ`WQ=+^j-}~clZl{_^?cp=}t9E*G1g_5K$^Jdb?}6pChg7OuZ9s4Dgp>s<{?` z4QyTthPefe%*ZBR-@+|GR6ofcWRJy{bf=(Og`e{3 zxCCUBT&iOr=c>U@YpbJUq27wau;`mpriR0a^I^*+G!}~KqO8Wkt(}BAt#+jDVd_@TMPuh);7ot+gfI#|n-u2|}| zpBcFk%-Rg7R*|lmtX>#iX=A3>5KN9jsH+h#bf}E93jd>MyD*~X9{hAgAO=jU&Dzmk zhlsg{uw9*5Nf&81ujgsv)qX=w#-x_WN}099Am{SH992T$Wq9C{52ukw?n`6AgF@ag zFx^{~VVO)xZ_h6A_Z3{94!dh%TY0bM|MufNpm%$xbI%wX$k;f&0x6E;*n}aqY1PyN zT!)Wa^+VYjeW#6Kmb-hG^A!jq;A|Sf-7Rj>$j@}Jjc7e{CAq)Yi;C8F+1nN~G`&&C zC(1|_x^Yz!^Y`SFf2Pv1m2X9P#c`V!Y@$lB<^LA+Z_T1*;y3{ z$}?r>Ljg3oxOL@~`NPMLbRM}epcA7?mx66l8Fvwyr?7REFSs1l_p6=_J4z<=HN~sE zYB@!^UUeLnAlqY2tr!)<)(e=EZh&jYvcFzTE9jWGblg)hOExrnOAY5Ve$3L1OX@PG zg?;fvwqFi7n`q5%?_aD-PM@fZ(z!MlW)>_!nsvXhK-5=yzDTpTz1%V~8y`p6df=W> z{yTYBz1mtbiY(pj5$V-7%cm2f*4wHky%dl~(a!niaDQ}Cq20Xy%}Za@#rFRx*I-O-VS zn-x@P<~GI`n-_@qggm9*;o(OnQ)Z5uCwj9G2{!o>4r_$Voc}uZu@lub12A4vryr_U zKVXr%Pp3?T&WBTlT2ZzZ76_e&NF257?kz=Sc863C{V1#%ir@D?=WaNhGWq6Xk=cw3rmv&*hon~Y z`MJ9?C~qZa`y8oCEq&3mKJ21R8-Kd)D>K6?>C&7T-gOJjL<_@W)YE9C9u`VKDp)3r z)(htbEP3AH2*Q+x1*SFhU|q)8XY}x>x_UU#z|d6cx4wPSik7URjn|uATNp-l7jBzv z*?gF4vRY56mC%6zsqgphCSI}nsmNYz;(0D@-&W_pi`M9kEX-toyB|994mC|}5{Jl4 z^G7FL4d9VK;>UU>lPeD}nTA%B{7et_>hr9>9cHH4QP=F6h(mNFzs!7nOJoif6{^-4 z9ObU9lO4&_3g`9ovk|furTAD$q*+?}c+h6$^xs(caOOI@FG|>7HJ}mSa=t*~?_YxN zI;-@Jj!%y>s1Dv_tUw`Lsfn6yTIH>m&o8#p5ZuxH&97Z< zf3MuI2}AE44Oo|!)s577$z1z=@K$06Do0>UTTfCd2CN28yD9iiyk$LYW9q&N!X9ml z(Zk+b?84V08xJGn_OZbJ@3MdSZ|_!3+*a@P7@>HBN)cjVXN6F*@nvD1AO?N9ewj9T z)V<^}J|RL-{L_K{`pP=8v}O)5)lt3^p1!bCfxsB9==NW#zgXj&9iFMzN=;|nZu&WcIDt+yKlI5mA$TM2pCnWu!ivEH2;o4J>nyI3ltG`AyJ z&j>$8gm#cf%VX`3(M#YBLQeGhP=*iGkg~0vt(GnGsB8Do;>&Rv0zqvfTUc_OlmF*#K~SvT9$JHL(zW5r;++Ao?)J z7VWf%vzxwav$m_A{2%HM!yNylZdatRLL1@k!!1xEEA#P+4CJ#08}8SRg(=p~`|&m0 zcxURisd&^=DwvdF_~I#9M+dfM+kZQhS?0V4MB&e_2eZa+mp8m9?{LEi(S&GpDWq`3 zfo^+nqTlX4%tyajS-6_^(1I7^-wk`=<&7`O5of&bs(a4g`+Si(+pwe>-@RBjiywZs z1ob`P2T{Mfun~@7pEJ;4c6K$Re7HS|?B_x>>94-|?Pbd}zgpGL%QrCR*s2KnoUXbH zfLH{KWfGN>5Wsv5W*<@hc@Dnkxgi1aYL{${7D;VGKR-d6bgy>`!c)iPuWoG;wwq`&P(iVKSm{Z&4|I_T= za_7Jh+oX)ULuy1|%4VG(n_n1A1W9Ep()mwP08Yv0yk4@(+j1wzRM zy)t?9vjY*>l{4J;cHxSRK3dWQ1CFWx5t=sTP{#5Q`!G(3NnK2gkG{rS!rji8eU3Yw zfs3SHuXj`2e+UgF-SDZNplwBa4dP|@5lNm6quiJK3j3^${M`<4;PSsq{|p1@mQ3#< z`bN3q&%xvgw5SX5%+eT1BlA>q7pZ37rB4N5AET^){SMZx^_&=f1T{1H_okifK1+LX z!q<@f?W%kGu#NTGRc`cZ9ioZUc@9H9q~3})7*szXIk8e(6wLw}J%gJu; zelpr)50wa#S$CjdsKA!fUBfHZ*Of0K&KitDbI;aOKCPP<#IwhtuP81iY{?U?51!Rv zE5xl66e}YFj|KH7ed&5SlD-I^xT$hJ!P99azAw8^`5-RiLSOsaouXMnu+~?I_jT5 zScSyf5lN36EP0?h`0a#hGA46;8&=!CaQPkzp04|~umc`mCuQPvW#_yl-LQpE*s&r^ zW4;r46QQ#d{KdiU+0a`&R5}rFGRI<@FGc^noWI;Ff$w=1SJZd6IR6)OZypcTAHRPq zNrfb`WEo3R$zCMOD5MgKWC>%JvhRdb*|SW@mSrjxWtV*$vW$I_WM2oPtTSVo^_<^) zzTf-4|GfYHJ^VRl=FEHMyqD|sx}F!|f820UG1&X`5yA@knFM+d<9)TE9h_XOI%J+-)kLt+HZV9oQ3Q@X_N3eFn}(@_4j@0k%IOtzQr zI1YvUV#v&_HL~?&Y#X4~IzWjMiJ+}61e>bE?6pY8lGbYIT(1d9yutb;Wu_zK>fSHE`G|vOu;%SH*}3-)C=si(bTbp}8~kV0 z?@q;WkK%d%?zoiI{&|DMCG*ZqKyxAfmRdqZrS!nx^fbdy2THXC%7CijP@2j!1*R+_4(EiNR#`v8glyvZtnZnGq zlUoFd>&Yg0ON_mW;7h@12KO&Nt&{)ivC#ddzdgczH$vk7+=W!-*R3>u3{#7H?zQzv z#9W~DJwBhmhGK5axI(V~c`>Ra^T(JS?gp_|rBF>0GqJz_HY)E+C$OcWvQW z8Vz}0k!xp}{VGLB#oCzOS7l#B5x5XJx6wS$;Gc6GKh z>fx$f?~e`3X#>lp{=*qsgoG=xm5-Ga8vimg;iH{W((XLoQGv}&ITz&zy)CO~vzBjN zx;toi^6l|#;4N6u0#$W{t@kTn;ReR-xixJxM&UBe0KBc*o?A~oo<2(m&H9{aJvHUR z+Vrv=*x$RrkKPDgz&qb*5nUP_HOcHI^DDlcpp2`mT&)3I8$#+@Y`c9g!1l-$MKpyJ z=;(Td?6XB9QFPl{J#hpXF6jNLy=Ca+9r>=JJLerg{Fes*)&Ii2vnR(~tg3Zn&{huh zQRP>&cbD5H9K*yiJ7v7uXq!;#^ZhoH z;cmQnU8mdQvv|}P`K!!z-m$`kRG(ri$OiR`T=qL96Vp-VW#0c|co*xt{tS{q1)bN9 z@vj$41WkX@Ooryb@n%IA3_TcVBb*a`=m2a4k0z`2X#O1)6i87_*sX>9=-S$wO`i!C zjK|H5Jf5bZ_Ms@}>!Jjd8#C-K-B^zv93EDO{t2&#D$z%u`yrTiCfdvn`ai4wROzKv z7#H}fNHo;=GzpmvWSb6hlN9Wrou&kk3n>!dRb~;P8^AcEZ8e|5F2(soMEU4U-|r{a z>*ZTyYPbYa!flH$R{!`A_)x4mNNGEB`{H`F=yC?z{gL;z9=1>1nRLb0GnwgJb4_39n+3F8*Pw%aZhC!t=swOB|WLbVK>7PnQ!+N?M4-l2AI-^;n+&>{aVO-SuOLFp3MkpD3L} z|HYR>{AF0Z`RPvSMdSh(pU4(}`dqB{F!G1YZR0Tu7gG9p%|hNxYg2x^kd}f6Kz6(K zeCxXWy#RlD{ajdDz*37g67iEbIQXMN5!L`n6vd9#C@giLXp#i-;92t=Z z-OrYLELT+WF!5}Vc4Wyl8Ajn^EXgE)vFB44TJvKjRp!ELPBX7B+hs}f|B6`0k|y)g z)(o0|9NSoR>u1`{8yzqa3l(RNv??0;l_^Ev-TbB~`_)u(7yHw!_5{uB>fcF75v&bk zY!c!^n0~`0UVd^C+uS3?lBg@0zXP)&SsXWR?jsH}-M1*lP(V`pmJ;P8OUi1LwLWM1 zqKBEm#IhAFhfmUV2|J;QLsdQ!T1L#g8C8CVQbpg;#sdWRCx5SG(!_-UOTX<1OYsEU zjFUi*_vmRDbY9p~T=ufYgRNG4q?{AW*aaJ$vr=#8i++ad{DfELZS@1lM{{qq>UP1w;RS3J=Wf7Yh9kiqta0e-!C+8fZDds z9~}i9)j^CHV51$QpPZ=B5}y}Az0)N48-rz5A`{VHr9J<&yu}r12`0Lo;Iclh$2&xV z7P(tYCZNq=`qg=;k=t9BqOq#=9PQ10*}8k8DFL#HuckgNxd3{%fU!)7%r=<;13z)d z6?Eqg(CM=F6LQJjG9{(e%GWWmtcz7i7`LZ&lTSGsw>#K(>A`S_t!H)@>w1adPw$hf zPeY{4Q{6Z;v&!ZH9gY}KrZTWM85cabw|0X5`0u>Iio5#Kb=$iEa4;J>rSzS%)!DFS z@z|_kcdT>T_nWeY0S9*$5R0d&Zd0s!It}Mz7!Pz$pM59j8OHlxapkuZ-P=LNRfGpP zeNsz?2axrU1Xp_x|9E0E))d8dUkNI#bAJ0aC}fTg(h$cYt{qLfX>vZrnp>m^(G1o; z$UJ~sx(!#rxjwh;!&{nyYF;;pVNz>xVYzDjyKfpsxiH8oAMsNi8hp0C6ybp&8wvjx z*DwFUnM-%*Ni8@3Ejbdh?2`zdIl94#Y)9SwfA}zM`)JOS4S%sA@3rTS;_P5s!%{l4 zP|gee#AO~^ogy~7L{Dq_NPN`Oje)1mrdBPr|7vZ1T$1_wJMy3B$tBd)=sF?x$&+4Z zncWXbC|*`3!6vowD*qv7jA%L$3uqOOdEVOr1nsX&KI4DhpkIb@;L@YJ(8fM}iX-@6 zj!vweS!9`nb{wQ~Bm_EZ$Vkpe&Zk5dI({kcO3u%-2Xg?7|MIXJkzSM@rfO~P&J4hPMlwLj^2F`K1EadI?R zLfoZGaX5P#w-(WxJ{26Bm%KfIO0KuCWIH!uIm}=R`-U5;n%9~TcDdAV9^Xfs@4u?r zf#a|I&8r?OLPZ{)FGg?$TE#z_xE~7kVQ$r-fK}R6XoMOR9q|j|r^3nav1^29^uj%pS=lpPVR;Ab1?RrqDQU+54m;2WhlKuPe zl8Y@dft9gR-qe)Od>m@i0Zy#n7O6mnxy6s0J+$3xS3vj2f8a>m98K)!{v$-hncgc% zpw`?X6>yw;PvU-<-7s^adL&l3*L<9Qqiu&0wFh=)pwUM;1o%5X_LeG+JPWw&XwO5;BXJAMfubwTBty(;2M97Q0-bm>aLV9 zzx+8V`1Sp3m(OYGXIeTcO|EP0_gr@DBfxxi1XgOt5m_!-IWN>AVG6$(x+)Rh%sZ+^f%Fa>xh%D8DUe;=|v6Z?QKXB#JWF^IOz;qODc5FgTjd{ zErCZv^89=Htjj<0;r~Q2+#kSbGG3b9Gae1vYlFAhT9F9hJNVADc<9W#u^g3xmH(|p zkwg{&dyYO1S;;NUD_qcH{pU+vTmf^B7P~l#OTwg^R&G-gBBP z7G_#vcH%?_wpY#qZaHT+iw7uAQWtZTEHj_3&mI!K+t#k95uL6(x+pP_D@Z*QCRZ|* z2oBxjOJ)B4nC+)bO=lN<_Soe#$xD{u_P>1~lXtm2XL|(Zb@Y<&;Ff(X!wb!ec1DOx)6}^Xc`i zI(c;8u#-qJIk9ICI3QqdSo8+0>b|IShaOpA3&e`gT#W3cop@LPW3rQ%AhHt9MI_Rl z#_)!v-mPI1tvIN^F4jwT}1^f?X;U4%y_X)Tc3=e9G-2w|RHR*g$=lox&u?bTZzqC#a zi4c?}qA$1ZM%%11R91Q?KdI*S%oFKF|88Kit!wK>?etC2taHF9rJf)g7SWEV`o}z6 z=;jIRd%N|gh*RUL+xkGY9Seojd)Ial_xDT$E(|Wxb!AR6lHf^?=%eHeD%OGR+hjOn zo2U3S8VeqvAE70{HX>2BdhG{CAt#puSjhOK)x11i+R*wcCS^7m__tp1L7T~woa&fX z-m$9ojyQ-TVxMoNnp@c20sU}BA$FrT%Q>E0AchTz990O2W>cirC%0$!*5Tv-}$0& zS#{QjvB&HsGJXT2uNe@_I=Q=*9vvVA?alyb_Ck_^*fE0_PS0MW7lu~pkFhhsi(F2H zE^6UoD?ga2PWXc7cDInC$xbcSbB(xZ7lJgu8}3fNIO-&&5f912X1CLo1OGj14>xI{ zctlwF#;mxEnLo;8)eb(kTALo)9u}af4Kfa_{vEv_FE*n5Y{R7`M~{H$T6MXcJ4!Q| zavFg*)jQ!|UdWb?(x1fwk6R&^2%k3X7bH0RKy&wss&u1SbVhN%wIJ~H&=78B$A;2l zwt8iB4JCd-a%E2N%R$P69=R&FBET9Y#}2YcNEO`|P(Q5qmUGL|+j+tJh0sYMwKxw| z(1g$_zsqeO12$gIX2O^*9jnz-XFmVyTd?LXzf(SMY<(7cyz;QAJYm3_PcbZAHsjOvWLtz#-lxx<-9s*$IczT`gp zwkM_DxV^{qdHj0MLxF7-gV4G5_MxE8Qff+r@6(P7 zh`dfdBeIhsw~+eSeP&HE$Gz~w} z3V}UvQ8`(atw3RDB1AZH@tXBq72^-FzocRS1%`8M?Md}4y|>J0ot{4P@Zt1`@|C$5 z#Cc-J%jB8;M{%{*31|<#GpV~GKnwi!BP|Wk1$xxK-y{tGXU4*NI5KScmuz`jvg*_Y zt+$I3KJ-7<*FR0sS+`GRdDhKY1GCf>U2TOpF@jvWwWVf?m=D%c@hn{e2#e|u`I>2vM(KhgMs zhjJtJH25AR5A&Yu0808b*vv4?%>8pCY~3@s_Xd0T7gcCcxKgYOe=%sn>lu?}qTwid z>g!A9S){y<)E>p%|Cq=7YEV^ng}F|C9LP}Qckn7we$;9-x0fQi5@#SPYq6+u?!R7u z!8|3iIntJ`5ZMn}|5(P{DBrB>lpOwDR@V`=5MHv+7_E z#MG$G{j9est?KzlTs%Od|ALtK2U5-n-2xaHBfDQ!>pw@y=s>cX^GG*;Iy)@vexyHP z+j%zdPX`{pKZu`yzjRE5LVaSYqN<7l23J82HWl#6YYeS~*(xQ#*f+Hj^yUe5nz|AS z*EU+BX06<)#OHdoncIc?R*L`|BTYJ)jOTQG(XtH`(qt7yg3t^SjCgd8*ue(d$kp$H zI^tlN2?GmeYKijwQOeqw7bz{!EynjkMXUYxx(T#r>9KDW1lUI{=Kb6Zea7J(&1R{t z0HYBMmb)!F;iJ{5@*73aZSlZ+ftSy;B(voULZ=7_*je}VuXV55>(We5Rj$FF907m-$De`!dpXx- zI{#*k*_KiPjq$zSk6f(S|&oAw;)bDrE?djl0owTq0RuyYS zcJDoFH-L$YI6)|Cxm)V*(-FPq%U7O$+NfbXq8?9z+X3l=@2(os1~D8u@2KM%16{8K zAF^wnB4iC!joINU?wn!Zf~v~DjdYqAbUS_TSSM%5W4sN}gj>RQHQHn&sX1;A_lixs zAYNl_b7sOhKp5!%dgOk2!@sAKcwST*9`!uM;48!brmDl=&pSpm;~ZrnHQJ=!)U?sY z93GbI)s*_5l1nY6a{?fV(G;LkqL^S{KqSoXP zyDP$NLk8#I7`W@MVCYu2fX*2_f^F3vEOa_3Fnhfk@g2-japeX5r{Kdexb29&b^1Xe z;Ei(15n%3?n;)?;r$aBU8Ys3*04i*|(D(bFvb>|2UF-WL*;@O52s66Otr*DsN$)>7 z@U$Bpr7=D~i-FJ(2^BwvouLwlKJKRVl8YaVN7o|=%z3T$BDb&v5sqjOb5e&5%CCr)V-<0DC5z_Jh9WCH?ox8g9pIL z^uWsOWp)EmDTs`LUj_fpRC41{g(U6gNJvjNuDsct@K+!O`jH68qN!*O!WZP2GjI4( z3}`|#7!ZRz=|P~uj=z+L%qEmlpQ_1Bnyw{nQ`P)kpUHAJOCS%1T?ThJ%DK^i%fFUZpQ9bA80?f`XCfUDAF z-h-2I7Hwg~mFcNZ_rJ0c?oxbP@b`be>Z9QC_y0i#eO0~?|FIca#)9fPAO2$u-Z51u zDyiN9P9!sFg-qb6{w2nLe(RV!|M3s`!c&azXET|DQT^f%-Zqr_qaJ1c?tASLLvnA9Wpx;HHJ@|7%{7SOUfx zN72DQ87fCV$(j6V zU+UZ1gGH@`7$kKWmi)?I)dat=cHet-3RhoX{~HJNq%awCw+`K;w$n5ZnX@TF-N*jmUbta{I@tKyOZb<^7g#SQ56tgrV>JEwE0zTKuT z+*V&6i0h~APw8e_``u~84QniCh1{;3di*GZ+0@v1lE;=zFPsKPMWQEsQ~bId=HEoX z3GS4IHq;xFNDo9PhuAgE-5seA7T>CS1u(1Q_!8<{fQtMFXUri47-{YJkoD3TjfUl&eheO{RK!X8Df zw}?G(1D(i)5}OHMYrDVyXlpdTqJ9Qk#=1kQ%=oA8MO$IlA%9f}SLHX#^GRZ|q5t&V zUDZg>sG%qN8#7y4E>aVhXxJYMx1iTAZ&qkTDM=JI9FiQp>-bq@BWd{gX{OJ5Aq;;} zqLZJBC#cDC+?Pm3v)za2qb~jdjIy=bK32GZL)a}`7P`;GnRPd6a&5qb3@*oxb8ArJD!1zB&rZ|7}^qs3&|(dWf-- z-e{XH&&$si#z<5{T0zrn9E#-`ctpqza9)BPciTs=n~sZ$Zw;pJ$y53*xYLo5%eurm zzUGP+Dz~4o(?ONNDvkSDiwhTJDvLW+3Q4J=A5ieiwi>Ov_!za>u8VF2ivu)xtHf(sWKhr=HNaO=yeHMAMaX`>5to= zRZ8S4__isX;0OTxHn>-!V2tSS;~4rn3MFHBxmK`J8oaJCU|vEt*qAIBg5OqFYi8zA zS3WyNWn^(L$q6Dfb`{IQ$!cw}{AafzOXy)?*?FThhwMbLz`-iw?@?GD=bKkoa_1>+!@lEo{&J1$Tg}T$h6?E}mD&40E9me|ljp^MQR_}o zAYr082bk(j)OsoIGt)S~_twKh8{Aq&%gsYKo1111M^Naf_Y>-@3i;4!W>FN2yY;*V z4l7G}kQ$tFLhyMI4m%DPD*b()JNwgKhnCu1UnW39e*y92NX?HLsqH((OZNC6{Kbr8 zj|Txq(CGkCU9BT~8)q^!;Bjy4LWcNph&AK~l8~aa75?Fq+ex>2Ub3c*+a}6HBYQa8(Ah*aj9NU)Ta+%@NJuvlb99 za}M9XClEV3yXOYb#*5U?7KWsLhI~iLxMkbfkjAmDJ$ftI-|~WDF1=w#s@)2ay46id zHd#%OfVDjNCOs9$@WIHd?FFv$F{MD^Zk3(5TRMpU+B$xFMne*u^eDA_IFrfN)}68p zB_@icyv!}IHX`+2j(vOhl5?7TMTUO_&N_n+=%@0@PubTsW*=Twj;$4gm=e$N&{a5j zxb|cof!nisk`L-Z3d6LFO2)(WyNCAplJ`;dFtwi zpthf4G#998?*pVyc()mmEVe4g5ak~4=p3fI;Uo~R;iVkSenuIPhzrPt4 zebONVJtw{AAE8w$Fqu7AsYkU7jV>ly|2emMGHw+O`M8Bn%Wi!I*mS3tPfFi`EaP1K zl_5v(F^b9lVLSCmt>CrdS_*8ZvSWfJFl6Pn`myP|UcopM7U}o*-d!D7%?UO-{~2G& z7Z0A1=uDtJ<)A0Td^kmQv%GNjGs8KaQ^qwn(dw4h1dM^Cn0)ey-hvYD-#UKZsx&7V zis9@OhyNw8Xg^E6fh7m`#V@iima&JI9Cs)&Qs=i9A_ zNS|dT+Y6eX+}o zXWgyeZ$K`QEonQeBAjrF!>PDKcaPw$?rz8^#W;uWFU>Y@Id)!N0yOMg!j`#tnvq(@ z<@y`o*Pg8)D{j;cjit(pK8>J>L&-dDo_wSKr#PGa3X;A^ItE=Ia=Gam9PWri zrPE9_ty65(w1V!tb0$n1RsDr=@L{#r(CY{sWxV_}nBRp;KDj_o)HTa@>|Y(7i}k>N z%{}C*{zuWOqVKJo`@n>h#R-X%4PuOA!09iGGR^;LuG!C(y6vhN z&zk@3wTfm}3j5%(H8^2+tUxuhT{2lxb@I64^oYFl<_T_A1C18th#7T#xfQzA#EXKkmrvZk$^8w)A*90v3e-gA=nJZ~rV18YPZIt?5DGzwI?Q zK48v5KLu=oedyAIKd$~`36)X2f7r$u_taX{QDHjKB;{*_;4jMzA zsgEfR+=T}X-h7gErqMTG=F)5(;cOOdG8t}oqgY|zo9M#*sZk&DdMtfiuZNg0ZF&2U z*8)4}^2zu8KY~ou8LIdF;-FNU%8l*X@uEFV@WyqhmGWnrDV;{%hz#H5UJ=#Ue4+Mo z=xq^1N-C}Sae^9@2=<`W=0a@Gn-oa$%E(*5Auil@`{4xJplP#5wk~JXH$UP0*X)ms zxF5M!mITKxjEdVF(>R&P7S;^y^vhQC3deu2@p!{P=t8c2eFd|noAE0yEtI9dMW~LY zM1AKUe!Q_845Zu6WS#q{TW`N?`GxQS;ri21&d->QrAzHBEQ(`y^lm&%KJO_F>kw8w z*?pMHPDqomJX$?yYS`Z!b1SzVcP@LJOVdhg5c}q1ltAY6k-1i_= z!Bmqal^~Ix#v+vIDEwQzviL+UnDfg$<~&1KrKhKFzbO~`{%9^t2#z(cwMxt!FJ!yp z@$?SAS`c@Pe9Tfjq_vJ%*na)v%HOo=+x{F3f6Kc8@kuMZ^;K^U&$KgdAKLmRXz_@D zD%b1b1tzb_0l0ege%ZpXjj^BR#l9Hh7cgdbI+HT|t0IjVGWglN<;6OKM=G|ja-Sze zs!mH_1Xe)w6x?zQYXnI@Kd+tb@?W7Q#&r&z9N<2g6!4lh1^J*uM#mLDh@MTj>b3f@ zVuz(LU!Z5|Qbv#JrtIEZje9Pa(!Z^};gG*HFNx52O;xFZ)2GF?fuR>cfO!VDV_AG{ z@P@xiN%fXRMxgiVb@NckAiccSZ7Ufun&ws`)qb&eXT{RoT(DLGb?e1Rj%3p;k2|Is za$65Fh2*2p-;@wn(~Vi4N72c_L2%k%WH3OnfTV~)Cr{p_7rJo{QL_*U zi9tz64+VS!dJkrj6A!nGrhlq`9?63)=##{{AH_3dCB{uWWj z;Pf7Yfu+VJ4#N`@>DJnlAp0tu@z=x;`~? zF#a5c*oMcE z6uO$RsnAPYAqHGX?5G~~@s z(!ZNmUO4oHl3ul=vb zp#(Q7_>?x$zpf)wAb@THg&xd?`E>yz61?${YNrVR*&8&WL&t`E&#u$g z=`>4<0Oe9vk_CP0D0Ws6N>2Zp18HOT+)iS+Ul39S9EbeJu`1e;p9^HZoY3wP$KBUR z;pWvau_|qUiA7q}RXPNUr@Q5kD7EdK#@E<1h&n$}nr7>6ORaQzQe2dbkx_2Pkf$MK zuT!uTxcwr%bMOs?PyJ>e-{o>@47He8@${HB?0aO|Oya`dg`Z=l6qFnv;_?k=)jGqW zrr2uG87U`&c+7s$D}YmYDk70e!Vunfp_t#>45vRs~|x--K7@-pG_5rU3;W@g|7=DCWAkhix@Tzci56he?()j%pj^a^0#tr<;D zgxY9$XaltccA}wR9_g2S9VhmAdJZTCuZ$2bzrx@knjkzsy_jrY1I>&BJ0t(RBimj} zL_GRQ?WOFJxGCnMCsv1CnlZW*EZho;Ac7$lf zblu$}rSAjiY^PJuyUBhj;(qBjZ_QPkGVjrfa2@b4HYYsPYU z7rxDkT{&Z#==FQdW2&b==#07H%?Cor5+X!YR~ay5Z-@% zV;xXf?mDXasIH{g`tF(vNVZw2NQ%LxGC9qS!H@bNO5&={&*Pw;m;G$e0=dWb3Irpg*^DE+q zHO#Nbm#Ll;rJ*kkxb9yJ)IM?T_W^R7KD~ov<$>}6q%5=80Na3gScD#(`1Vmo3F;3q z=REimbpTg80s8|QDoz_@0F~cKg@n#9Xg&Mtm2mo@Ti5#kiLhRv)coFuJns+HResY8 z)guU?Qsc-C|FG^@_<93Mk3k~9e?wGh_y(&q_ZaPoY{B>$sf z_@e$Wx*V=@CHv7^U1dxIcsLHL3SFxuRghaqbIvSg!$c)4ysP5ZxZamE?zX^m4ux8l zEfx{(d$&b&W95$asPOUm+h2Y23&(A6XL>FD0q^v`KF*1bW79b9QjEaQL+mV9z$B~V-MW6#6dcK16sMdQ}ov?KcbXOqDvt=e8&(dKm?+U z-==q8XuSAQjRIq03Yzo#=`fh()69L$SS<>RzJUwo%ka^D?&$b>aI&p`FdS&RfO!fzF z_|#aYCSHRc{n63RQUCmX82w_6S1 zB)H;B(gYN**T5E~@S}&271~L`Um+?y)pNa?$@zt5`5>=pcc+xqU7aEXxo1SUYy5PV zHC`2q$JI5nOeRFe}Hn6BUhjwo6A@@Nv*}k_L z=8qDS0(r~TT`|9L+-{f7GcE}S)8;X&2psp34Pcb}JU{j#I6QnmxN5r`y&I!o14MwK zXYuoHA#R(!D5ctpbraY-2vh&2C#Xmf!sGyvO9xm;tO*J{-K8xPDJX?G6k$wJWAfW* z%#F_(j+Fn>mxBU8qle+(xW7wie-s{8j(#mAuIBHB&jk%&WSzc{a7j!ufP(XwM)1(~VM;p>+^elf-Cu49SQ%`%D*UNhlF zd#U0uqGtXh_bf03n_Rh|5#=tG>M4ulR*XjZ2r!GMFm5gAixXG9FlYA|FQu^>I>bRk zP;N9P;S{90D9m$9!A|=+%00KO(4V_D4~Wq#Du?TI?qT-gB6mF373$zGNeUa!!n9Io z_03R!#YEEbS%Hw9%SX`)rQVBrwAd^_a!~R6;>P*tiDSSqIEw&XUFiO8iO#e0ZO$AM zrm4TCL|orE5y(BLHeNADNLObEjpG*jLBanfDU|4x-cH?qCPaZxh4a7RH+#2Vn=xEo z>wDko9~k}QU00AxyO^W7R3qyBm`Fl9Uy5E5~IePq;B0PE%2tJ5PEakAE~6z>nM!DWis6?192&0M zMh|}>{Im+T?)Wrje)bVCN}m`e^PZ3tEV=0?et|At3Gu{O8a+H+adT5@82ZUOQU@JB z{=)&15^=h!HAKMES?hJ8?^XDKJm+7y=4zcc{YLvZ@!Ik_bmI7rDt(_Y>*C2u$f!cn z`L97hkzClQX{5~EpF9IaOMverD+W@Z1=`P0tMa;KFB2`h&sJ0&yWsOH(YtF&Bb)q% zAvZrBAC$e5aKd^~UZ0J7s`gD^F3X#CC30LD4mg^dq`OQ=knLbn!UUOja)$6>!ZF(l?k+_-Ok1v`vQCMFFa0Ti9LU(tLnb7(egS ziw<>?&>}g};og_?hUw1|&^%bA0JIzI8r?pHv+%kC7M>TEs{1}|Vhd~lL*oU$blaY& z_XR-vAx-F7ECus553pel^u=#}(l_>poquSGl?>uX*NDpX8cZK=*53PsbrUgNvDpU- ztG-5CofyDtk(yq`v;NYp^V|y}-OZI7gSeG~<9@01y|-T-Iwo?^T}(uvbjb+D76 zfDNn)^&s8bLNBulo=H7+btyiGzA}2j*X+_O`Ocl7V6mh3aR<^oiIsS4!r(ex;x9+t zCpOhf`kJ^`k^c^z3=%xc+~)t4_}_6I9Y9;zTCxh_FR>TeJOp){pBg^Ob$D;e^U4Z% zBmd6vEVnUZ_|Ucpye%+{BIy-*4LS#E9xaCuexN^PiH+&XbfI1shV294TLQjjOMI&c z1;OB}CSBmj!RtkE^`Fx|;I-dQM&2-p-66+x>xFBbh3&&}3P+YLI-5^VPhQrY)E@;8 zJQ&DO+UL2R6)H`7U7`F=Ncgh!@0Yk%khC-=o$~hdR_vvU;b_D1cj_juLlQSH-c;*F zlXWW$0vPM&nt3r;lGQL!1L+`XcAU561l6;2zIa^7Jt1_38c8quuyNBgB0cBmsqrO3 zGW_FvP3P;eBA*^m%-O=9^bWaB_RGK9M|TzLi|D*l)Br`8=N~)_k67)>5iH3J_}HY( zk+N>Gpt5+OGTQ8lEr7CRv?lY)Hyv?2jzMYr*f7^kDkUzQMOQ=Y`A|efdoSW-<51iK z^6SLI(i^@HU_5W(YBxXKI;B+oljnJ-s%El_aD_le7u$Vg(O1pL>vr!Q>78mDK$0{D zX72+ocD^t<2UV*+lrARfAjWNyT%yt*XkAk4ek z#uOoI;@AftvFg8uq7{Bug$7-H+_R>293YNU!XY>76E=WdaSVbQt9edGBDd-{BgW^9 zYr2X2rl0U_5#W#`tI3*bDh^qWBOA`Yi92$^StYXxeX9J22eI$7tru(GgC0b$ zE$HwTib~fQcJ#eM`f!XGCV2D0k#l4_N4xf-i##_ib>1n|_G<4e`W=t;u76*28Q zXEQeK>D30;pr&{fBXm!Bj+ok^x)h*19|=v48*~AYeA1_okS7C625cBr(4j*Z7!mGu zq8ZeVWQbb4{HQDh;kqGBvAgZ`LzJ6BxStd_wWIU>2EMl(Si~>;X$v-6r+nX!lK zuLze%-Ky1sQnB&p6`vk2I%H{|)8wFZlQ4>brHqoAAR%shzDmkD{VTpYyY_4W1g<=B z){P*Mo$Dz!;z=B5JjN4zCKq>RyI@|GI5)##EUqs}Fp4qK#;Fx~!~H)U0)3CYmunbq zA_ckHUMu0qEez1}?EQT161#La0ad4a+to&D=+9Jo6PM^SqnaEmn4-2Ff%b2ug!-tB zvF%Pqh(OFO%);Z89eC?g>SG!}uNht+uLvTSk(}NEt?}dT=*YdW4YwGjz8O3Q352sC zv7I{S&hw{9Gi)dVtyrZ9x(t60T59JvgPQ{Fm4qB)g&TaFG|C+((;v^V+z)3gHrfIm zSl^b&&!K7Ze2+Gt_!5}|joaKa9Ht@v(s>Ec(~Y9=f~*l&DLSHgFzmxEOU(di(#liq zS<3o$*T)Xg4YLY(W4$)04VKK8xIhA@-w&fNsh7h8;ub~9(ThR^SfuZXalm)MbvT%j zYsr~)9!H-i_<+zp1s3|sO0s;6_3+^crB6r(7Q@CT&?vZCZ4yV<+I6P1%{_XuXGIr=_)VKC;o~FlbkMUc1 z{^_OlV2o(N7F7!4+e<|a!;Xk06-Ui+beHJD_9Az?M6`U!IMlufQcPI7)E?$xrp0&e zABMGvfBE~VuP$&@y2s%_2a1CuY;Cv8D=IosxaBkesRCU?V&fw^>zfFQ#}0VyPSetv z7?35H!Fvf5y}ljB@D$iaBhd_)?dmz!&o4J8FKG1AKSoyUWz@=M(5h1W@ar;u0`th+WA#M*qUNsIE^BqT%e7isg64D;$k zs)v5J-z8?uWdQ#j^Y}dLQ{3Q@|i=PQ8J?|0-S)Mm`E|uyf5aWYoAK5 z(xT-sd>EaG5T9&UvjN1c0T4TmAQhccz@5ETeg>x*?|TLz9{vpr+d0JfM|$JgO4`CJ zjDEo><>t34T<1upLfbbMi>}pn?O%J)?e?>EM!aZ3lUET)_{O^yE5WYyAPCIY;-+IDaTHGD(G3hN7fe%kYJgPBIwwUjO zM-%{|Ad_9?%z;JU>c7`RsxZq3BKGza$ZTDSi2}`rnmVW83JC_Mh>2mez3q#dz>6YD z!Oo_|oMs_ok$2O*K0hT#MlSul-$I(?d=Xu7q^t96Wk((5B$o%rg7_Vl5dltdgNijJ zjR9?~NPDsOhm}f`;o;q2+?vjs;$0CSM>*TfafR-GouYP9fPWdfOShkTH5fN_pJQE* zz~`syKFTfr(aS4WNH?Omez4?CSor%&$Kwfa1RNEGG;;!l>ncCW%T=j@>Zn5>L*uds z+kb&sA@;5Eqj4ANAuiON9>O@Inu3_dd3#TrToyvVcU(j66mvQ5E66XWLA?0VsVjLs~ zZGe}(uZ134@_b6jU$0o--r*ZZd^tTnKU$0~PVRLk*I-w`gURV&Z$e%7g!_h8j-U0p z;2M=YUcuh@pEW!JYaLhK8%9;hO;xejwy0*r=&6pp&Mg*$6$=71M3;W7ux<~5UGwyt ze!*clYkWXzmG(z^;&0$NFMOH@wne3$2g{daZZB~OqA9I!7DE0QmpaGewPQM>kN}g^ zSK@uu|Mv0mm(pXHchk{5INp@!N~bM5-yaFClfK+^8U}QzD001IU_pUnoa2H?d7suE zMY=uy@ln=2su-)tJg;y{sBuRV5T^uL`hs7{`&-qxJPeTDhj&QXW7GqpT%W0UZ2x%N z|9-|;Sits<@TWQwc^DXGCW+{$kbw3PQrPF$S~f@R`=uw0e_SQpZkjiEvH={~u}ew< zBo$dM>1=rzS_Vj$1Fzi3508ledQLhjf-iTqfoH1!^NI-!lbvI#Shy&2u1t#MIUDuh z4c~14_WwL-82-xO=XFT)DMBNY$YPQ{=+RUr>KaHM( zDn~jzmQGy4pnQD6IJnOwIf3N;f7-k9N2tE{Z(p-#30a1$NtQBYn6hLoOA(q3DI}t- z33syZ!z2+ItrCe+CcN!C8B0<2j7%Ean8b`R_j|oRpFiOH2Yl}H<9Y5q_w29d^*raC z(s^dX$rK84pX4}xcVeC%IX0I5XzB3fIR=GpwarO+LsnGLM}RVp7%J0Fi7o54FcHai z=0$eT!J-1{rZgV>m~MvtPRq@yXJsqdW>(L1;Ap{K^R(Fwk#R+iTZx;I$w46}!MvlE zDg=BRjd%M=Zde~}%C5036{b);(MfolQn%}4bEDEpo|cSeIt$XK-Q9kiY4iCt_7WOCkj8@VJdw*#RUc8Db5pi8$FX_lNu%;a&M zkdjKh>LRYurPX%1DBm)7faiQXaN_J_ytYu;Aa%U-23#_ZLn~7rIAx2$ z1jTUXj`$k|wI72LS$m1!!Cxx3^VoW+ybfYYd!EomL7g;5*_D?;cMd^1^t#jIA-7pC zusu#3s!)7sI@>L1;tXmU!tuQrzB!S9aU|q-F(4Nvq)tXNQsobws@DZg>vq8Uw~AXk zoCn!COXHF4mkx+Vmp`@7?QxWw>>#E>SA@qnvQ7H*?zDe8*z7G{J!(!$_71#z-eIog z3rW2v%zn=AU*FGF9x~ousO&M2J2a17!dq=-_j+iQTd~@4o$=jB?xM}8r*$7Q-h9y1 zIVPj&s({X!%RNZt{3`0GCYlA9sUUb9JHLTEuqC_f=1!OWUeDbiqgQ;-prV^O!16Rx zQGmGY4?T=l*QTEmu4#eso{-LwxnFx+@$@`{#85&1vPsnE&ZRi~=TijMK8uluC_w6> z6DIe{Plk#$3P1r1f+lH>FO_cZ+W04Sv%QuX$KSJN;3&j-ErFxXl_-F#mpX>uPK2i_ z5$9Os??rXN?b<$Aq7ODa>^4aLd>Em@F2{DBc$M#YPUedcA7eUV;FoKVP}^Tei++-z z?7X_axIGjukRfw=2+k7cRTDka?H2B|F^GhJb~@6#bim^QHU&1sNThE`sS8shB^DML zzLDXv9~$cGCw3P_nvmI&g?e$Zr7s_7S-$!D%{NY_qRHZtZpEt)vXcWMK3^aDQ?5yV zaP=vodsi~VgZ`;}Iu;OUfXsA3KYjDi<%>sF&|*r>Z@b#fcj+D#HJ_PBU1QTedb&E@ zIQFqnvFo+&@L0cAz?-h)w05`6Lle>N|I6fm)%E}BU6#;_Al)GS5OP{&^iR_*nI&f$ zAFS^Km`^eZR!K$C4$CaRCUzBt>Y;v(YHXCaSEFpgXEt`FujVqfr{(N={v1}ZM774t zeW|Y_xHRs<{Dq~ykmSHW8Dad;6sx;?@5X}6%lqruOgulgEg3J-*GwzO)@RTC-QpNr zvXI1XL}<^yt!a+@*1B&vFR!pma&tQgLq%V1V;RN!bHC&$w;c20H8}oQyj!tGH2*0C zhmjU7^hlvJf6Y6U0%XN`SV-F)9^(S#N8)7k6=k&ezkrf;P2Ala=3%Q)(MEqr;FC8q zy7H=k{qlR;!s`8Bd7fOh93JVaXxo+pSHngflH2WbQG7Sgcs8j9fZAO0K!u%!-QasI z+aoXJ1&^L9gbzP~MxkSf9!pzwSUd`PPeS^h#Gt`{f4O?U&RRl*q@|tQ+jN}W5|#Jd z{R1d-&1-GWU$&(=h~wahWC?M1mOzx7&AFL-t9`a@z)FkljH##CO6~K67F5w(Vdf>% zr_N30T+}x=0@J#}UecobFuSigA$Xpm+rfG6Z*%H_Zbel5op?7mh)7})2}uI@w7iSnax(%*hm`7jAZw9JrE%9alDIw}Y9zEb_MY!-8Rh&|Df(&GXC^ zF%nZ-6$SWm@k@*=j-!dl9P>~rQxHC|jFHmgA0AM+AGtd{cdS%`#MsmG+tWku$-)o{ zf|Bh6!v3FFYN`fzqgl#_5ujOOZPbBE8Deu{A{m|jXjQKS_(SgwZ2;wh>2U1D*bv!l z`*+6ecS)2B(!HC1m(@Hu#cfnQcZU^UwH$X7z2IjNlI$K(t-}yF;o^PZps@75WRE?w z1PK!w4q66Ulxsqm$|+E>)0q8*j5wB`i+Aj+ZFL({iuGA9a|?r?X&3S#+J`JHbjLV8l2&!lpN3TBjH|bCp{~^)WDKki=uNv1GH? zp_UW|n|03V!x5mi{Hl5tQbKNg*2z8IW`+h3k)p~!g;aa}rlMFs=vMV9WX1nzYCxJL zfPEl@a2^~gSTKRi7EUqenNzr!t}L=5F@QxKC_%!z-%I>t{E2KFfW^fgy5_ikBqWIg zq5O`!!)V3GVqWTcDwTBD?X~$;$@D*oM0suz-&0wukOht}5i|9MLeoxwh<})qGjSc6 z7wPyz3ECGD+^i?kYz1EZzVj4$tZ)Nm)NWfDU7PXfw@#o*oVnCD86Y}!7u$67SveE- zT(kcp2|Lr~GOsVDU0MwAL7oBtW9#ILYhtU^-*CtLTAkAYv-8!t;GYFeEan4>wjSh?L6GM{_mt36Z) zk)wWUwoq;~k%Klf`@>+z-O`HzS^YueMgiZm1MZ@(869*##(;(9weJzECn~)~@#i{t z>~d)i=3R6v1qp9M6~VSZle(Q|rnlH~QybS{8?P#?T@)9C$k<*#E4`r>Yw&oebBvj6 z5S`8~;?{EH;xcdSH3LGCOunF%_7UTkiIaC@NQzb7V80{3UkKAOqQ_9o>r0W|9IVF^ zYF>3W13-=Yu<-bq_>U>j7vRAQMmBk z7m@pjj@F-8`4xPk&|sTg5)2U@pOq9DM}Pfpd+@d1x@7v9e3S2t1eL+&Z${h5tzT)g zpvhg@qR!U-vU1yaHswfDwzP}!9cy_D8AqRnv+A3ttC}jmzR#mpB<%JMa{6=OP_#RY)@d` ze;Zr4CS32-MermTAsGa9tv+c92ZF{StV*FEMvzqmQ4hyRg~6Fs`|OJTRA_G5Na z2w4b*#VbMfGGug^&M|4KG+k`qn;g)m9Or13qgJ**Ji+x#+B1O)L%-19&OMJ@HhFo@ zV^=2N6<3xhD-CrG=vbQXmYRY>tGOzDy>qH%6B}}D0CU!h!T=W*k#$j`nhIvoTdM(E zM$HW0t-sU83ITUcWeB7Try)D!7)|MVRX&JJdI60%$>*P$K@C-$jT>fiVw$hAppG!h zDjUcrSM)^|F8JEH+NP&0AcH1%^jT)@+k$C7!tr-l6y_Qv&t8SH#KZ@YNf_E%NYauV ze>xe7;Tbm*f0`!Jq4_+}Whg5BUnnkAg*1by2;4FxM8p~;4I*3Asx>mDmWzR3@Q$n8 zzJ0wXz_>K#j_Mll;C-$V3p$G-R8`CKmv8A}Bs`e3$toP?5a_E(FLS(-6@19|cE(DZ zyvVhM_$unkTxm3cBRLAgDH708v@b`yBNSzjO_9wziz`Kh@0E?2L6<>H zJf)e8Of^QNQY#{ZVSJP437OMkcMc?pq#_%yEq*S_tn+ydrh-OPHmQwPpGEw@Jv~*N z^r)08_92KcJ-z3$gzxGD-T z+P_y}C_rCfk8}6)WfpI4oiG7H{w}UG5T1!vg)0C~!|TPDSuDr>_{N=-Pr=nS0y@P< zKaHfw2Tc{Ua+neahSaykU&>Cr%RBjTeXlXQq3tjb6%ftpG;xn9k`Lbe)a8&fg`y^} zh)~q6F9^74Ul4V>aT{0m?tW+DAyPuc3*w8K=A{zZ_~WKG>pjOEUWHJ$ORE65M-><* z2|HcpHOQk+3}=PjimSQ1`}=3XD0{{S_SX79(wbbyq|VII&|(@fqn|cSdH8nh@weLi zSPxmbcK_9KWq&%^j*P^jyE6wH0p=?1aCg}C@4*#gq6x0EEWeAzbN4rU{ci9n_HWC;&R{6^DzzLcWot?ODeyN1 zY-nhVscFc((I-tkKkgURY98Y*g|ytbu-423Bnz=fAa&844SH-W&skKNdBy)9Mo>;K literal 0 HcmV?d00001 diff --git a/hrms/__init__.py b/hrms/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/hrms/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/hrms/config/__init__.py b/hrms/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/config/desktop.py b/hrms/config/desktop.py new file mode 100644 index 0000000..c785f05 --- /dev/null +++ b/hrms/config/desktop.py @@ -0,0 +1,5 @@ +from frappe import _ + + +def get_data(): + return [{"module_name": "HRMS", "type": "module", "label": _("HRMS")}] diff --git a/hrms/config/docs.py b/hrms/config/docs.py new file mode 100644 index 0000000..7e56ec0 --- /dev/null +++ b/hrms/config/docs.py @@ -0,0 +1,11 @@ +""" +Configuration for docs +""" + +# source_link = "https://github.com/[org_name]/hrms" +# headline = "App that does everything" +# sub_heading = "Yes, you got that right the first time, everything" + + +def get_context(context): + context.brand_html = "HRMS" diff --git a/hrms/controllers/employee_boarding_controller.py b/hrms/controllers/employee_boarding_controller.py new file mode 100644 index 0000000..f3430e9 --- /dev/null +++ b/hrms/controllers/employee_boarding_controller.py @@ -0,0 +1,198 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe import _ +from frappe.desk.form import assign_to +from frappe.model.document import Document +from frappe.utils import add_days, flt, unique + +from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee +from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday + + +class EmployeeBoardingController(Document): + """ + Create the project and the task for the boarding process + Assign to the concerned person and roles as per the onboarding/separation template + """ + + def validate(self): + # remove the task if linked before submitting the form + if self.amended_from: + for activity in self.activities: + activity.task = "" + + def on_submit(self): + # create the project for the given employee onboarding + project_name = _(self.doctype) + " : " + if self.doctype == "Employee Onboarding": + project_name += self.job_applicant + else: + project_name += self.employee + + project = frappe.get_doc( + { + "doctype": "Project", + "project_name": project_name, + "expected_start_date": self.date_of_joining + if self.doctype == "Employee Onboarding" + else self.resignation_letter_date, + "department": self.department, + "company": self.company, + } + ).insert(ignore_permissions=True, ignore_mandatory=True) + + self.db_set("project", project.name) + self.db_set("boarding_status", "Pending") + self.reload() + self.create_task_and_notify_user() + + def create_task_and_notify_user(self): + # create the task for the given project and assign to the concerned person + holiday_list = self.get_holiday_list() + + for activity in self.activities: + if activity.task: + continue + + dates = self.get_task_dates(activity, holiday_list) + + task = frappe.get_doc( + { + "doctype": "Task", + "project": self.project, + "subject": activity.activity_name + " : " + self.employee_name, + "description": activity.description, + "department": self.department, + "company": self.company, + "task_weight": activity.task_weight, + "exp_start_date": dates[0], + "exp_end_date": dates[1], + } + ).insert(ignore_permissions=True) + activity.db_set("task", task.name) + + users = [activity.user] if activity.user else [] + if activity.role: + user_list = frappe.db.sql_list( + """ + SELECT + DISTINCT(has_role.parent) + FROM + `tabHas Role` has_role + LEFT JOIN `tabUser` user + ON has_role.parent = user.name + WHERE + has_role.parenttype = 'User' + AND user.enabled = 1 + AND has_role.role = %s + """, + activity.role, + ) + users = unique(users + user_list) + + if "Administrator" in users: + users.remove("Administrator") + + # assign the task the users + if users: + self.assign_task_to_users(task, users) + + def get_holiday_list(self): + if self.doctype == "Employee Separation": + return get_holiday_list_for_employee(self.employee) + else: + if self.employee: + return get_holiday_list_for_employee(self.employee) + else: + if not self.holiday_list: + frappe.throw(_("Please set the Holiday List."), frappe.MandatoryError) + else: + return self.holiday_list + + def get_task_dates(self, activity, holiday_list): + start_date = end_date = None + + if activity.begin_on is not None: + start_date = add_days(self.boarding_begins_on, activity.begin_on) + start_date = self.update_if_holiday(start_date, holiday_list) + + if activity.duration is not None: + end_date = add_days(self.boarding_begins_on, activity.begin_on + activity.duration) + end_date = self.update_if_holiday(end_date, holiday_list) + + return [start_date, end_date] + + def update_if_holiday(self, date, holiday_list): + while is_holiday(holiday_list, date): + date = add_days(date, 1) + return date + + def assign_task_to_users(self, task, users): + for user in users: + args = { + "assign_to": [user], + "doctype": task.doctype, + "name": task.name, + "description": task.description or task.subject, + "notify": self.notify_users_by_email, + } + assign_to.add(args) + + def on_cancel(self): + # delete task project + project = self.project + for task in frappe.get_all("Task", filters={"project": project}): + frappe.delete_doc("Task", task.name, force=1) + frappe.delete_doc("Project", project, force=1) + self.db_set("project", "") + for activity in self.activities: + activity.db_set("task", "") + + frappe.msgprint( + _("Linked Project {} and Tasks deleted.").format(project), alert=True, indicator="blue" + ) + + +@frappe.whitelist() +def get_onboarding_details(parent, parenttype): + return frappe.get_all( + "Employee Boarding Activity", + fields=[ + "activity_name", + "role", + "user", + "required_for_employee_creation", + "description", + "task_weight", + "begin_on", + "duration", + ], + filters={"parent": parent, "parenttype": parenttype}, + order_by="idx", + ) + + +def update_employee_boarding_status(project, event=None): + employee_onboarding = frappe.db.exists("Employee Onboarding", {"project": project.name}) + employee_separation = frappe.db.exists("Employee Separation", {"project": project.name}) + + if not (employee_onboarding or employee_separation): + return + + status = "Pending" + if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0: + status = "In Process" + elif flt(project.percent_complete) == 100.0: + status = "Completed" + + if employee_onboarding: + frappe.db.set_value("Employee Onboarding", employee_onboarding, "boarding_status", status) + elif employee_separation: + frappe.db.set_value("Employee Separation", employee_separation, "boarding_status", status) + + +def update_task(task, event=None): + if task.project and not task.flags.from_project: + update_employee_boarding_status(frappe.get_cached_doc("Project", task.project)) diff --git a/hrms/controllers/employee_reminders.py b/hrms/controllers/employee_reminders.py new file mode 100644 index 0000000..9d4d7f5 --- /dev/null +++ b/hrms/controllers/employee_reminders.py @@ -0,0 +1,275 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe import _ +from frappe.utils import add_days, add_months, comma_sep, getdate, today + +from erpnext.setup.doctype.employee.employee import get_all_employee_emails, get_employee_email + +from hrms.hr.utils import get_holidays_for_employee + + +# ----------------- +# HOLIDAY REMINDERS +# ----------------- +def send_reminders_in_advance_weekly(): + to_send_in_advance = int(frappe.db.get_single_value("HR Settings", "send_holiday_reminders")) + frequency = frappe.db.get_single_value("HR Settings", "frequency") + if not (to_send_in_advance and frequency == "Weekly"): + return + + send_advance_holiday_reminders("Weekly") + + +def send_reminders_in_advance_monthly(): + to_send_in_advance = int(frappe.db.get_single_value("HR Settings", "send_holiday_reminders")) + frequency = frappe.db.get_single_value("HR Settings", "frequency") + if not (to_send_in_advance and frequency == "Monthly"): + return + + send_advance_holiday_reminders("Monthly") + + +def send_advance_holiday_reminders(frequency): + """Send Holiday Reminders in Advance to Employees + `frequency` (str): 'Weekly' or 'Monthly' + """ + if frequency == "Weekly": + start_date = getdate() + end_date = add_days(getdate(), 7) + elif frequency == "Monthly": + # Sent on 1st of every month + start_date = getdate() + end_date = add_months(getdate(), 1) + else: + return + + employees = frappe.db.get_all("Employee", filters={"status": "Active"}, pluck="name") + for employee in employees: + holidays = get_holidays_for_employee( + employee, start_date, end_date, only_non_weekly=True, raise_exception=False + ) + + send_holidays_reminder_in_advance(employee, holidays) + + +def send_holidays_reminder_in_advance(employee, holidays): + if not holidays: + return + + employee_doc = frappe.get_doc("Employee", employee) + employee_email = get_employee_email(employee_doc) + frequency = frappe.db.get_single_value("HR Settings", "frequency") + + email_header = _("Holidays this Month.") if frequency == "Monthly" else _("Holidays this Week.") + frappe.sendmail( + recipients=[employee_email], + subject=_("Upcoming Holidays Reminder"), + template="holiday_reminder", + args=dict( + reminder_text=_("Hey {}! This email is to remind you about the upcoming holidays.").format( + employee_doc.get("first_name") + ), + message=_("Below is the list of upcoming holidays for you:"), + advance_holiday_reminder=True, + holidays=holidays, + frequency=frequency[:-2], + ), + header=email_header, + ) + + +# ------------------ +# BIRTHDAY REMINDERS +# ------------------ +def send_birthday_reminders(): + """Send Employee birthday reminders if no 'Stop Birthday Reminders' is not set.""" + to_send = int(frappe.db.get_single_value("HR Settings", "send_birthday_reminders")) + if not to_send: + return + + employees_born_today = get_employees_who_are_born_today() + + for company, birthday_persons in employees_born_today.items(): + employee_emails = get_all_employee_emails(company) + birthday_person_emails = [get_employee_email(doc) for doc in birthday_persons] + recipients = list(set(employee_emails) - set(birthday_person_emails)) + + reminder_text, message = get_birthday_reminder_text_and_message(birthday_persons) + send_birthday_reminder(recipients, reminder_text, birthday_persons, message) + + if len(birthday_persons) > 1: + # special email for people sharing birthdays + for person in birthday_persons: + person_email = person["user_id"] or person["personal_email"] or person["company_email"] + others = [d for d in birthday_persons if d != person] + reminder_text, message = get_birthday_reminder_text_and_message(others) + send_birthday_reminder(person_email, reminder_text, others, message) + + +def get_birthday_reminder_text_and_message(birthday_persons): + if len(birthday_persons) == 1: + birthday_person_text = birthday_persons[0]["name"] + else: + # converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim + person_names = [d["name"] for d in birthday_persons] + birthday_person_text = comma_sep(person_names, frappe._("{0} & {1}"), False) + + reminder_text = _("Today is {0}'s birthday 🎉").format(birthday_person_text) + message = _("A friendly reminder of an important date for our team.") + message += "
" + message += _("Everyone, let’s congratulate {0} on their birthday.").format(birthday_person_text) + + return reminder_text, message + + +def send_birthday_reminder(recipients, reminder_text, birthday_persons, message): + frappe.sendmail( + recipients=recipients, + subject=_("Birthday Reminder"), + template="birthday_reminder", + args=dict( + reminder_text=reminder_text, + birthday_persons=birthday_persons, + message=message, + ), + header=_("Birthday Reminder 🎂"), + ) + + +def get_employees_who_are_born_today(): + """Get all employee born today & group them based on their company""" + return get_employees_having_an_event_today("birthday") + + +def get_employees_having_an_event_today(event_type): + """Get all employee who have `event_type` today + & group them based on their company. `event_type` + can be `birthday` or `work_anniversary`""" + + from collections import defaultdict + + # Set column based on event type + if event_type == "birthday": + condition_column = "date_of_birth" + elif event_type == "work_anniversary": + condition_column = "date_of_joining" + else: + return + + employees_born_today = frappe.db.multisql( + { + "mariadb": f""" + SELECT `personal_email`, `company`, `company_email`, `user_id`, `employee_name` AS 'name', `image`, `date_of_joining` + FROM `tabEmployee` + WHERE + DAY({condition_column}) = DAY(%(today)s) + AND + MONTH({condition_column}) = MONTH(%(today)s) + AND + YEAR({condition_column}) < YEAR(%(today)s) + AND + `status` = 'Active' + """, + "postgres": f""" + SELECT "personal_email", "company", "company_email", "user_id", "employee_name" AS 'name', "image" + FROM "tabEmployee" + WHERE + DATE_PART('day', {condition_column}) = date_part('day', %(today)s) + AND + DATE_PART('month', {condition_column}) = date_part('month', %(today)s) + AND + DATE_PART('year', {condition_column}) < date_part('year', %(today)s) + AND + "status" = 'Active' + """, + }, + dict(today=today(), condition_column=condition_column), + as_dict=1, + ) + + grouped_employees = defaultdict(lambda: []) + + for employee_doc in employees_born_today: + grouped_employees[employee_doc.get("company")].append(employee_doc) + + return grouped_employees + + +# -------------------------- +# WORK ANNIVERSARY REMINDERS +# -------------------------- +def send_work_anniversary_reminders(): + """Send Employee Work Anniversary Reminders if 'Send Work Anniversary Reminders' is checked""" + to_send = int(frappe.db.get_single_value("HR Settings", "send_work_anniversary_reminders")) + if not to_send: + return + + employees_joined_today = get_employees_having_an_event_today("work_anniversary") + + for company, anniversary_persons in employees_joined_today.items(): + employee_emails = get_all_employee_emails(company) + anniversary_person_emails = [get_employee_email(doc) for doc in anniversary_persons] + recipients = list(set(employee_emails) - set(anniversary_person_emails)) + + reminder_text, message = get_work_anniversary_reminder_text_and_message(anniversary_persons) + send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message) + + if len(anniversary_persons) > 1: + # email for people sharing work anniversaries + for person in anniversary_persons: + person_email = person["user_id"] or person["personal_email"] or person["company_email"] + others = [d for d in anniversary_persons if d != person] + reminder_text, message = get_work_anniversary_reminder_text_and_message(others) + send_work_anniversary_reminder(person_email, reminder_text, others, message) + + +def get_work_anniversary_reminder_text_and_message(anniversary_persons): + if len(anniversary_persons) == 1: + anniversary_person = anniversary_persons[0]["name"] + persons_name = anniversary_person + # Number of years completed at the company + completed_years = getdate().year - anniversary_persons[0]["date_of_joining"].year + anniversary_person += f" completed {get_pluralized_years(completed_years)}" + else: + person_names_with_years = [] + names = [] + for person in anniversary_persons: + person_text = person["name"] + names.append(person_text) + # Number of years completed at the company + completed_years = getdate().year - person["date_of_joining"].year + person_text += f" completed {get_pluralized_years(completed_years)}" + person_names_with_years.append(person_text) + + # converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim + anniversary_person = comma_sep(person_names_with_years, frappe._("{0} & {1}"), False) + persons_name = comma_sep(names, frappe._("{0} & {1}"), False) + + reminder_text = _("Today {0} at our Company! 🎉").format(anniversary_person) + message = _("A friendly reminder of an important date for our team.") + message += "
" + message += _("Everyone, let’s congratulate {0} on their work anniversary!").format(persons_name) + + return reminder_text, message + + +def get_pluralized_years(years): + if years == 1: + return "1 year" + return f"{years} years" + + +def send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message): + frappe.sendmail( + recipients=recipients, + subject=_("Work Anniversary Reminder"), + template="anniversary_reminder", + args=dict( + reminder_text=reminder_text, + anniversary_persons=anniversary_persons, + message=message, + ), + header=_("Work Anniversary Reminder"), + ) diff --git a/hrms/controllers/tests/test_employee_reminders.py b/hrms/controllers/tests/test_employee_reminders.py new file mode 100644 index 0000000..d1e56be --- /dev/null +++ b/hrms/controllers/tests/test_employee_reminders.py @@ -0,0 +1,269 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import unittest +from datetime import timedelta + +import frappe +from frappe.utils import add_months, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.controllers.employee_reminders import send_holidays_reminder_in_advance +from hrms.hr.doctype.hr_settings.hr_settings import set_proceed_with_frequency_change +from hrms.hr.utils import get_holidays_for_employee + + +class TestEmployeeReminders(unittest.TestCase): + @classmethod + def setUpClass(cls): + from erpnext.setup.doctype.holiday_list.test_holiday_list import make_holiday_list + + # Create a test holiday list + test_holiday_dates = cls.get_test_holiday_dates() + test_holiday_list = make_holiday_list( + "TestHolidayRemindersList", + holiday_dates=[ + {"holiday_date": test_holiday_dates[0], "description": "test holiday1"}, + {"holiday_date": test_holiday_dates[1], "description": "test holiday2"}, + {"holiday_date": test_holiday_dates[2], "description": "test holiday3", "weekly_off": 1}, + {"holiday_date": test_holiday_dates[3], "description": "test holiday4"}, + {"holiday_date": test_holiday_dates[4], "description": "test holiday5"}, + {"holiday_date": test_holiday_dates[5], "description": "test holiday6"}, + ], + from_date=getdate() - timedelta(days=10), + to_date=getdate() + timedelta(weeks=5), + ) + + # Create a test employee + test_employee = frappe.get_doc( + "Employee", make_employee("test@gopher.io", company="_Test Company") + ) + + # Attach the holiday list to employee + test_employee.holiday_list = test_holiday_list.name + test_employee.save() + + # Attach to class + cls.test_employee = test_employee + cls.test_holiday_dates = test_holiday_dates + + # Employee without holidays in this month/week + test_employee_2 = make_employee("test@empwithoutholiday.io", company="_Test Company") + test_employee_2 = frappe.get_doc("Employee", test_employee_2) + + test_holiday_list = make_holiday_list( + "TestHolidayRemindersList2", + holiday_dates=[ + {"holiday_date": add_months(getdate(), 1), "description": "test holiday1"}, + ], + from_date=add_months(getdate(), -2), + to_date=add_months(getdate(), 2), + ) + test_employee_2.holiday_list = test_holiday_list.name + test_employee_2.save() + + cls.test_employee_2 = test_employee_2 + cls.holiday_list_2 = test_holiday_list + + @classmethod + def get_test_holiday_dates(cls): + today_date = getdate() + return [ + today_date, + today_date - timedelta(days=4), + today_date - timedelta(days=3), + today_date + timedelta(days=1), + today_date + timedelta(days=3), + today_date + timedelta(weeks=3), + ] + + def setUp(self): + # Clear Email Queue + frappe.db.sql("delete from `tabEmail Queue`") + frappe.db.sql("delete from `tabEmail Queue Recipient`") + + def test_is_holiday(self): + from erpnext.setup.doctype.employee.employee import is_holiday + + self.assertTrue(is_holiday(self.test_employee.name)) + self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[1])) + self.assertFalse(is_holiday(self.test_employee.name, date=getdate() - timedelta(days=1))) + + # Test weekly_off holidays + self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[2])) + self.assertFalse( + is_holiday(self.test_employee.name, date=self.test_holiday_dates[2], only_non_weekly=True) + ) + + # Test with descriptions + has_holiday, descriptions = is_holiday(self.test_employee.name, with_description=True) + self.assertTrue(has_holiday) + self.assertTrue("test holiday1" in descriptions) + + def test_birthday_reminders(self): + employee = frappe.get_doc( + "Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0] + ) + employee.date_of_birth = "1992" + frappe.utils.nowdate()[4:] + employee.company_email = "test@example.com" + employee.company = "_Test Company" + employee.save() + + from hrms.controllers.employee_reminders import ( + get_employees_who_are_born_today, + send_birthday_reminders, + ) + + employees_born_today = get_employees_who_are_born_today() + self.assertTrue(employees_born_today.get("_Test Company")) + + hr_settings = frappe.get_doc("HR Settings", "HR Settings") + hr_settings.send_birthday_reminders = 1 + hr_settings.save() + + send_birthday_reminders() + + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message) + + def test_work_anniversary_reminders(self): + from hrms.controllers.employee_reminders import ( + get_employees_having_an_event_today, + send_work_anniversary_reminders, + ) + + emp = make_employee( + "test_emp_work_anniversary@gmail.com", + company="_Test Company", + date_of_joining=frappe.utils.add_years(getdate(), -2), + ) + + employees_having_work_anniversary = get_employees_having_an_event_today("work_anniversary") + employees = employees_having_work_anniversary.get("_Test Company") or [] + user_ids = [] + for entry in employees: + user_ids.append(entry.user_id) + + self.assertTrue("test_emp_work_anniversary@gmail.com" in user_ids) + + hr_settings = frappe.get_doc("HR Settings", "HR Settings") + hr_settings.send_work_anniversary_reminders = 1 + hr_settings.save() + + send_work_anniversary_reminders() + + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertTrue("Subject: Work Anniversary Reminder" in email_queue[0].message) + + def test_work_anniversary_reminder_not_sent_for_0_years(self): + make_employee( + "test_work_anniversary_2@gmail.com", + date_of_joining=getdate(), + company="_Test Company", + ) + + from hrms.controllers.employee_reminders import get_employees_having_an_event_today + + employees_having_work_anniversary = get_employees_having_an_event_today("work_anniversary") + employees = employees_having_work_anniversary.get("_Test Company") or [] + user_ids = [] + for entry in employees: + user_ids.append(entry.user_id) + + self.assertTrue("test_work_anniversary_2@gmail.com" not in user_ids) + + def test_send_holidays_reminder_in_advance(self): + setup_hr_settings("Weekly") + + holidays = get_holidays_for_employee( + self.test_employee.get("name"), + getdate(), + getdate() + timedelta(days=3), + only_non_weekly=True, + raise_exception=False, + ) + + send_holidays_reminder_in_advance(self.test_employee.get("name"), holidays) + + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertEqual(len(email_queue), 1) + self.assertTrue("Holidays this Week." in email_queue[0].message) + + def test_advance_holiday_reminders_monthly(self): + from hrms.controllers.employee_reminders import send_reminders_in_advance_monthly + + setup_hr_settings("Monthly") + + # disable emp 2, set same holiday list + frappe.db.set_value( + "Employee", + self.test_employee_2.name, + {"status": "Left", "holiday_list": self.test_employee.holiday_list}, + ) + + send_reminders_in_advance_monthly() + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertTrue(len(email_queue) > 0) + + # even though emp 2 has holiday, non-active employees should not be recipients + recipients = frappe.db.get_all("Email Queue Recipient", pluck="recipient") + self.assertTrue(self.test_employee_2.user_id not in recipients) + + # teardown: enable emp 2 + frappe.db.set_value( + "Employee", + self.test_employee_2.name, + {"status": "Active", "holiday_list": self.holiday_list_2.name}, + ) + + def test_advance_holiday_reminders_weekly(self): + from hrms.controllers.employee_reminders import send_reminders_in_advance_weekly + + setup_hr_settings("Weekly") + + # disable emp 2, set same holiday list + frappe.db.set_value( + "Employee", + self.test_employee_2.name, + {"status": "Left", "holiday_list": self.test_employee.holiday_list}, + ) + + send_reminders_in_advance_weekly() + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertTrue(len(email_queue) > 0) + + # even though emp 2 has holiday, non-active employees should not be recipients + recipients = frappe.db.get_all("Email Queue Recipient", pluck="recipient") + self.assertTrue(self.test_employee_2.user_id not in recipients) + + # teardown: enable emp 2 + frappe.db.set_value( + "Employee", + self.test_employee_2.name, + {"status": "Active", "holiday_list": self.holiday_list_2.name}, + ) + + def test_reminder_not_sent_if_no_holdays(self): + setup_hr_settings("Monthly") + + # reminder not sent if there are no holidays + holidays = get_holidays_for_employee( + self.test_employee_2.get("name"), + getdate(), + getdate() + timedelta(days=3), + only_non_weekly=True, + raise_exception=False, + ) + send_holidays_reminder_in_advance(self.test_employee_2.get("name"), holidays) + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertEqual(len(email_queue), 0) + + +def setup_hr_settings(frequency=None): + # Get HR settings and enable advance holiday reminders + hr_settings = frappe.get_doc("HR Settings", "HR Settings") + hr_settings.send_holiday_reminders = 1 + set_proceed_with_frequency_change() + hr_settings.frequency = frequency or "Weekly" + hr_settings.save() diff --git a/hrms/hooks.py b/hrms/hooks.py new file mode 100644 index 0000000..1490eb3 --- /dev/null +++ b/hrms/hooks.py @@ -0,0 +1,301 @@ +app_name = "hrms" +app_title = "Frappe HR" +app_publisher = "Frappe Technologies Pvt. Ltd." +app_description = "Modern HR and Payroll Software" +app_email = "contact@frappe.io" +app_license = "GNU General Public License (v3)" +required_apps = ["erpnext"] + + +# Includes in +# ------------------ + +# include js, css files in header of desk.html +# app_include_css = "/assets/hrms/css/hrms.css" +# app_include_js = "/assets/hrms/js/hrms.js" + +# include js, css files in header of web template +# web_include_css = "/assets/hrms/css/hrms.css" +# web_include_js = "/assets/hrms/js/hrms.js" + +# include custom scss in every website theme (without file extension ".scss") +# website_theme_scss = "hrms/public/scss/website" + +# include js, css files in header of web form +# webform_include_js = {"doctype": "public/js/doctype.js"} +# webform_include_css = {"doctype": "public/css/doctype.css"} + +# include js in page +# page_js = {"page" : "public/js/file.js"} + +# include js in doctype views +doctype_js = { + "Employee": "public/js/employee.js", + "Company": "public/js/company.js", + "Department": "public/js/department.js", + "Timesheet": "public/js/timesheet.js", + "Payment Entry": "public/js/payment_entry.js", + "Journal Entry": "public/js/journal_entry.js", + "Delivery Trip": "public/js/deliver_trip.js", + "Bank Transaction": "public/js/bank_transaction.js", +} +# doctype_list_js = {"doctype" : "public/js/doctype_list.js"} +# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} +# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} + +# Home Pages +# ---------- + +# application home page (will override Website Settings) +# home_page = "login" + +# website user home page (by Role) +# role_home_page = { +# "Role": "home_page" +# } + +# Generators +# ---------- + +# automatically create page for each record of this doctype +website_generators = ["Job Opening"] + +website_route_rules = [ + {"from_route": "/jobs", "to_route": "Job Opening"}, +] +# Jinja +# ---------- + +# add methods and filters to jinja environment +jinja = { + "methods": [ + "hrms.utils.get_country", + ], +} + +# Installation +# ------------ + +# before_install = "hrms.install.before_install" +after_install = "hrms.setup.after_install" +after_migrate = ["hrms.setup.update_select_perm_after_install"] + +# Uninstallation +# ------------ + +# before_uninstall = "hrms.uninstall.before_uninstall" +# after_uninstall = "hrms.uninstall.after_uninstall" + +# Desk Notifications +# ------------------ +# See frappe.core.notifications.get_notification_config + +# notification_config = "hrms.notifications.get_notification_config" + +# Permissions +# ----------- +# Permissions evaluated in scripted ways + +# permission_query_conditions = { +# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", +# } +# +# has_permission = { +# "Event": "frappe.desk.doctype.event.event.has_permission", +# } + +has_upload_permission = { + "Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission" +} + +# DocType Class +# --------------- +# Override standard doctype classes + +override_doctype_class = { + "Employee": "hrms.overrides.employee_master.EmployeeMaster", + "Timesheet": "hrms.overrides.employee_timesheet.EmployeeTimesheet", + "Payment Entry": "hrms.overrides.employee_payment_entry.EmployeePaymentEntry", + "Project": "hrms.overrides.employee_project.EmployeeProject", +} + +# Document Events +# --------------- +# Hook on document methods and events + +doc_events = { + "User": { + "validate": "erpnext.setup.doctype.employee.employee.validate_employee_role", + "on_update": "erpnext.setup.doctype.employee.employee.update_user_permissions", + }, + "Company": { + "validate": "hrms.overrides.company.validate_default_accounts", + "on_update": [ + "hrms.overrides.company.make_company_fixtures", + "hrms.overrides.company.set_default_hr_accounts", + ], + }, + "Timesheet": {"validate": "hrms.hr.utils.validate_active_employee"}, + "Payment Entry": { + "on_submit": "hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim", + "on_cancel": "hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim", + }, + "Journal Entry": { + "validate": "hrms.hr.doctype.expense_claim.expense_claim.validate_expense_claim_in_jv", + "on_submit": [ + "hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim", + "hrms.hr.doctype.full_and_final_statement.full_and_final_statement.update_full_and_final_statement_status", + ], + "on_cancel": [ + "hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim", + "hrms.payroll.doctype.salary_slip.salary_slip.unlink_ref_doc_from_salary_slip", + "hrms.hr.doctype.full_and_final_statement.full_and_final_statement.update_full_and_final_statement_status", + ], + }, + "Loan": {"validate": "hrms.hr.utils.validate_loan_repay_from_salary"}, + "Employee": { + "validate": [ + "hrms.overrides.employee_master.validate_onboarding_process", + "hrms.overrides.employee_master.update_to_date_in_work_history", + ], + "on_update": "hrms.overrides.employee_master.update_approver_role", + "on_trash": "hrms.overrides.employee_master.update_employee_transfer", + }, + "Project": { + "validate": "hrms.controllers.employee_boarding_controller.update_employee_boarding_status" + }, + "Task": {"on_update": "hrms.controllers.employee_boarding_controller.update_task"}, +} + +# Scheduled Tasks +# --------------- + +scheduler_events = { + "all": [ + "hrms.hr.doctype.interview.interview.send_interview_reminder", + ], + "hourly": [ + "hrms.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails", + "hrms.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts", + ], + "daily": [ + "hrms.controllers.employee_reminders.send_birthday_reminders", + "hrms.controllers.employee_reminders.send_work_anniversary_reminders", + "hrms.hr.doctype.daily_work_summary_group.daily_work_summary_group.send_summary", + "hrms.hr.doctype.interview.interview.send_daily_feedback_reminder", + ], + "daily_long": [ + "hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", + "hrms.hr.utils.generate_leave_encashment", + "hrms.hr.utils.allocate_earned_leaves", + ], + "weekly": ["hrms.controllers.employee_reminders.send_reminders_in_advance_weekly"], + "monthly": ["hrms.controllers.employee_reminders.send_reminders_in_advance_monthly"], +} + +advance_payment_doctypes = ["Gratuity", "Employee Advance"] + +invoice_doctypes = ["Expense Claim"] + +period_closing_doctypes = ["Payroll Entry"] + +accounting_dimension_doctypes = [ + "Expense Claim", + "Expense Claim Detail", + "Expense Taxes and Charges", + "Payroll Entry", +] + +bank_reconciliation_doctypes = ["Expense Claim"] + +# Testing +# ------- + +before_tests = "hrms.utils.before_tests" + +# Overriding Methods +# ----------------------------- + +# get matching queries for Bank Reconciliation +get_matching_queries = "hrms.hr.utils.get_matching_queries" + +regional_overrides = { + "India": { + "hrms.hr.utils.calculate_annual_eligible_hra_exemption": "hrms.regional.india.utils.calculate_annual_eligible_hra_exemption", + "hrms.hr.utils.calculate_hra_exemption_for_period": "hrms.regional.india.utils.calculate_hra_exemption_for_period", + }, +} + +# ERPNext doctypes for Global Search +global_search_doctypes = { + "Default": [ + {"doctype": "Salary Slip", "index": 19}, + {"doctype": "Leave Application", "index": 20}, + {"doctype": "Expense Claim", "index": 21}, + {"doctype": "Employee Grade", "index": 37}, + {"doctype": "Job Opening", "index": 39}, + {"doctype": "Job Applicant", "index": 40}, + {"doctype": "Job Offer", "index": 41}, + {"doctype": "Salary Structure Assignment", "index": 42}, + {"doctype": "Appraisal", "index": 43}, + ], +} + +# override_whitelisted_methods = { +# "frappe.desk.doctype.event.event.get_events": "hrms.event.get_events" +# } +# +# each overriding function accepts a `data` argument; +# generated from the base implementation of the doctype dashboard, +# along with any modifications made in other Frappe apps +override_doctype_dashboards = { + "Employee": "hrms.overrides.dashboard_overrides.get_dashboard_for_employee", + "Holiday List": "hrms.overrides.dashboard_overrides.get_dashboard_for_holiday_list", + "Task": "hrms.overrides.dashboard_overrides.get_dashboard_for_project", + "Project": "hrms.overrides.dashboard_overrides.get_dashboard_for_project", + "Timesheet": "hrms.overrides.dashboard_overrides.get_dashboard_for_timesheet", +} + +# exempt linked doctypes from being automatically cancelled +# +# auto_cancel_exempted_doctypes = ["Auto Repeat"] + + +# User Data Protection +# -------------------- + +# user_data_fields = [ +# { +# "doctype": "{doctype_1}", +# "filter_by": "{filter_by}", +# "redact_fields": ["{field_1}", "{field_2}"], +# "partial": 1, +# }, +# { +# "doctype": "{doctype_2}", +# "filter_by": "{filter_by}", +# "partial": 1, +# }, +# { +# "doctype": "{doctype_3}", +# "strict": False, +# }, +# { +# "doctype": "{doctype_4}" +# } +# ] + +# Authentication and authorization +# -------------------------------- + +# auth_hooks = [ +# "hrms.auth.validate" +# ] + +# Translation +# -------------------------------- + +# Make link fields search translated document names for these DocTypes +# Recommended only for DocTypes which have limited documents with untranslated names +# For example: Role, Gender, etc. +# translated_search_doctypes = [] diff --git a/hrms/hr/README.md b/hrms/hr/README.md new file mode 100644 index 0000000..5d1ae6e --- /dev/null +++ b/hrms/hr/README.md @@ -0,0 +1,6 @@ +Key features: + +- Leave and Attendance +- Payroll +- Appraisal +- Expense Claim \ No newline at end of file diff --git a/hrms/hr/__init__.py b/hrms/hr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/dashboard_chart/attendance_count/attendance_count.json b/hrms/hr/dashboard_chart/attendance_count/attendance_count.json new file mode 100644 index 0000000..a2ef568 --- /dev/null +++ b/hrms/hr/dashboard_chart/attendance_count/attendance_count.json @@ -0,0 +1,28 @@ +{ + "chart_name": "Attendance Count", + "chart_type": "Report", + "creation": "2020-07-22 11:56:32.730068", + "custom_options": "{\n\t\t\"type\": \"line\",\n\t\t\"axisOptions\": {\n\t\t\t\"shortenYAxisNumbers\": 1\n\t\t},\n\t\t\"tooltipOptions\": {}\n\t}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"month\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1\",\"year\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getFullYear();\",\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}", + "filters_json": "{\"summarized_view\":0}", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2022-08-21 14:25:01.608941", + "modified_by": "Administrator", + "module": "HR", + "name": "Attendance Count", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Monthly Attendance Sheet", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/claims_by_type/claims_by_type.json b/hrms/hr/dashboard_chart/claims_by_type/claims_by_type.json new file mode 100644 index 0000000..0e815e5 --- /dev/null +++ b/hrms/hr/dashboard_chart/claims_by_type/claims_by_type.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Claims by Type", + "chart_type": "Group By", + "creation": "2022-08-31 23:04:43.377345", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Expense Claim Detail", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "expense_type", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-09-16 11:36:29.484579", + "modified": "2022-09-16 11:39:08.205987", + "modified_by": "Administrator", + "module": "HR", + "name": "Claims by Type", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "Expense Claim", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json b/hrms/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json new file mode 100644 index 0000000..57ddfd4 --- /dev/null +++ b/hrms/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json @@ -0,0 +1,30 @@ +{ + "chart_name": "Department Wise Employee Count", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:32.760730", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "group_by_based_on": "department", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 14:08:17.017113", + "modified": "2022-08-22 14:09:19.603767", + "modified_by": "Administrator", + "module": "HR", + "name": "Department Wise Employee Count", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/department_wise_expense_claims/department_wise_expense_claims.json b/hrms/hr/dashboard_chart/department_wise_expense_claims/department_wise_expense_claims.json new file mode 100644 index 0000000..291145e --- /dev/null +++ b/hrms/hr/dashboard_chart/department_wise_expense_claims/department_wise_expense_claims.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Department wise Expense Claims", + "chart_type": "Group By", + "creation": "2022-08-31 23:06:51.144716", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Expense Claim", + "dynamic_filters_json": "[[\"Expense Claim\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Expense Claim\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_based_on": "department", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-09-16 12:36:29.444007", + "modified": "2022-09-16 11:41:32.160907", + "modified_by": "Administrator", + "module": "HR", + "name": "Department wise Expense Claims", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/department_wise_openings/department_wise_openings.json b/hrms/hr/dashboard_chart/department_wise_openings/department_wise_openings.json new file mode 100644 index 0000000..1de29b7 --- /dev/null +++ b/hrms/hr/dashboard_chart/department_wise_openings/department_wise_openings.json @@ -0,0 +1,31 @@ +{ + "aggregate_function_based_on": "planned_vacancies", + "chart_name": "Department Wise Openings", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:32.849775", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Opening", + "dynamic_filters_json": "[[\"Job Opening\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Job Opening\",\"status\",\"=\",\"Open\",false]]", + "group_by_based_on": "department", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-21 23:19:31.637348", + "modified": "2022-08-20 23:22:24.707871", + "modified_by": "Administrator", + "module": "HR", + "name": "Department Wise Openings", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Monthly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/department_wise_timesheet_hours/department_wise_timesheet_hours.json b/hrms/hr/dashboard_chart/department_wise_timesheet_hours/department_wise_timesheet_hours.json new file mode 100644 index 0000000..a2b30e7 --- /dev/null +++ b/hrms/hr/dashboard_chart/department_wise_timesheet_hours/department_wise_timesheet_hours.json @@ -0,0 +1,34 @@ +{ + "aggregate_function_based_on": "total_hours", + "based_on": "", + "chart_name": "Department wise Timesheet Hours", + "chart_type": "Group By", + "creation": "2022-08-21 17:32:09.625319", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Timesheet", + "dynamic_filters_json": "[[\"Timesheet\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Timesheet\",\"start_date\",\"Timespan\",\"this month\",false],[\"Timesheet\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_based_on": "department", + "group_by_type": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-21 17:56:03.184928", + "modified": "2022-08-21 17:57:47.234034", + "modified_by": "Administrator", + "module": "HR", + "name": "Department wise Timesheet Hours", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json b/hrms/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json new file mode 100644 index 0000000..f3466cd --- /dev/null +++ b/hrms/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json @@ -0,0 +1,30 @@ +{ + "chart_name": "Designation Wise Employee Count", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:32.790337", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "group_by_based_on": "designation", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 12:33:54.631648", + "modified": "2022-08-22 12:45:29.009857", + "modified_by": "Administrator", + "module": "HR", + "name": "Designation Wise Employee Count", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json b/hrms/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json new file mode 100644 index 0000000..9b283df --- /dev/null +++ b/hrms/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json @@ -0,0 +1,31 @@ +{ + "aggregate_function_based_on": "planned_vacancies", + "chart_name": "Designation Wise Openings", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:32.820217", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Opening", + "dynamic_filters_json": "[[\"Job Opening\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Job Opening\",\"status\",\"=\",\"Open\",false]]", + "group_by_based_on": "designation", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-20 23:20:41.683553", + "modified": "2022-08-20 23:22:15.254254", + "modified_by": "Administrator", + "module": "HR", + "name": "Designation Wise Openings", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Monthly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/employee_advance_status/employee_advance_status.json b/hrms/hr/dashboard_chart/employee_advance_status/employee_advance_status.json new file mode 100644 index 0000000..054c89d --- /dev/null +++ b/hrms/hr/dashboard_chart/employee_advance_status/employee_advance_status.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Employee Advance Status", + "chart_type": "Group By", + "creation": "2022-08-31 23:06:16.063039", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee Advance", + "dynamic_filters_json": "[[\"Employee Advance\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Advance\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_based_on": "status", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-09-16 12:36:29.430589", + "modified": "2022-09-16 11:43:57.244256", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Advance Status", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/employees_by_age/employees_by_age.json b/hrms/hr/dashboard_chart/employees_by_age/employees_by_age.json new file mode 100644 index 0000000..d529e61 --- /dev/null +++ b/hrms/hr/dashboard_chart/employees_by_age/employees_by_age.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Employees by Age", + "chart_type": "Custom", + "creation": "2022-08-22 19:07:51.906347", + "custom_options": "{\n\t\"colors\": [\"#7cd6fd\"],\n\t\"barOptions\": {\"spaceRatio\": 0.5}\n}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}", + "filters_json": "{}", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 19:00:02.464180", + "modified": "2022-08-22 19:11:20.076166", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees by Age", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "Employees by Age", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/employees_by_branch/employees_by_branch.json b/hrms/hr/dashboard_chart/employees_by_branch/employees_by_branch.json new file mode 100644 index 0000000..f427565 --- /dev/null +++ b/hrms/hr/dashboard_chart/employees_by_branch/employees_by_branch.json @@ -0,0 +1,30 @@ +{ + "chart_name": "Employees by Branch", + "chart_type": "Group By", + "creation": "2022-08-22 12:33:43.241006", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "group_by_based_on": "branch", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 12:25:47.733581", + "modified": "2022-08-22 12:33:49.094799", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees by Branch", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/employees_by_grade/employees_by_grade.json b/hrms/hr/dashboard_chart/employees_by_grade/employees_by_grade.json new file mode 100644 index 0000000..1f81a84 --- /dev/null +++ b/hrms/hr/dashboard_chart/employees_by_grade/employees_by_grade.json @@ -0,0 +1,30 @@ +{ + "chart_name": "Employees by Grade", + "chart_type": "Group By", + "creation": "2022-08-22 12:33:23.767559", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "group_by_based_on": "grade", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 12:25:47.733581", + "modified": "2022-08-22 12:33:29.562836", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees by Grade", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/employees_by_type/employees_by_type.json b/hrms/hr/dashboard_chart/employees_by_type/employees_by_type.json new file mode 100644 index 0000000..8cbd419 --- /dev/null +++ b/hrms/hr/dashboard_chart/employees_by_type/employees_by_type.json @@ -0,0 +1,30 @@ +{ + "chart_name": "Employees by Type", + "chart_type": "Group By", + "creation": "2022-08-22 13:49:59.343893", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "group_by_based_on": "employment_type", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 13:45:38.913766", + "modified": "2022-08-22 13:50:05.645535", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees by Type", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/expense_claims/expense_claims.json b/hrms/hr/dashboard_chart/expense_claims/expense_claims.json new file mode 100644 index 0000000..849868a --- /dev/null +++ b/hrms/hr/dashboard_chart/expense_claims/expense_claims.json @@ -0,0 +1,33 @@ +{ + "based_on": "posting_date", + "chart_name": "Expense Claims", + "chart_type": "Sum", + "color": "#449CF0", + "creation": "2022-08-21 14:07:24.120739", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Expense Claim", + "dynamic_filters_json": "[[\"Expense Claim\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Expense Claim\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-09-16 11:36:29.292891", + "modified": "2022-09-16 11:37:59.669291", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claims", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "total_sanctioned_amount", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json b/hrms/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json new file mode 100644 index 0000000..48578c9 --- /dev/null +++ b/hrms/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json @@ -0,0 +1,29 @@ +{ + "chart_name": "Gender Diversity Ratio", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:32.667291", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "group_by_based_on": "gender", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-22 14:27:40.143783", + "modified": "2020-07-22 14:32:50.962459", + "modified_by": "Administrator", + "module": "HR", + "name": "Gender Diversity Ratio", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/grievance_type/grievance_type.json b/hrms/hr/dashboard_chart/grievance_type/grievance_type.json new file mode 100644 index 0000000..fc5aba7 --- /dev/null +++ b/hrms/hr/dashboard_chart/grievance_type/grievance_type.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Grievance Type", + "chart_type": "Group By", + "creation": "2022-08-21 13:02:06.880100", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee Grievance", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Grievance\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_based_on": "grievance_type", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-21 13:08:57.019388", + "modified": "2022-08-21 13:40:40.415600", + "modified_by": "Administrator", + "module": "HR", + "name": "Grievance Type", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/hiring_vs_attrition_count/hiring_vs_attrition_count.json b/hrms/hr/dashboard_chart/hiring_vs_attrition_count/hiring_vs_attrition_count.json new file mode 100644 index 0000000..1e73fa2 --- /dev/null +++ b/hrms/hr/dashboard_chart/hiring_vs_attrition_count/hiring_vs_attrition_count.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Hiring vs Attrition Count", + "chart_type": "Custom", + "creation": "2022-08-21 22:58:12.740936", + "custom_options": "{\n\t\"type\": \"axis-mixed\",\n\t\"axisOptions\": {\n\t\t\"xIsSeries\": 1\n\t},\n\t\"lineOptions\": {\n\t \"regionFill\": 1\n\t},\n\t\"colors\": [\"#7cd6fd\", \"#5e64ff\"]\n}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\", \"from_date\":\"frappe.defaults.get_user_default(\\\"year_start_date\\\")\", \"to_date\":\"frappe.defaults.get_user_default(\\\"year_end_date\\\")\"}", + "filters_json": "{\"time_interval\":\"Monthly\"}", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 10:57:55.011020", + "modified": "2022-08-22 11:03:30.080835", + "modified_by": "Administrator", + "module": "HR", + "name": "Hiring vs Attrition Count", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "Hiring vs Attrition Count", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/interview_status/interview_status.json b/hrms/hr/dashboard_chart/interview_status/interview_status.json new file mode 100644 index 0000000..6813dc9 --- /dev/null +++ b/hrms/hr/dashboard_chart/interview_status/interview_status.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Interview Status", + "chart_type": "Group By", + "creation": "2022-08-20 23:10:33.131622", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Interview", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "status", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 12:13:19.640093", + "modified": "2022-08-22 12:16:33.674218", + "modified_by": "Administrator", + "module": "HR", + "name": "Interview Status", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/job_applicant_pipeline/job_applicant_pipeline.json b/hrms/hr/dashboard_chart/job_applicant_pipeline/job_applicant_pipeline.json new file mode 100644 index 0000000..8538e0a --- /dev/null +++ b/hrms/hr/dashboard_chart/job_applicant_pipeline/job_applicant_pipeline.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Job Applicant Pipeline", + "chart_type": "Group By", + "creation": "2022-08-20 21:18:45.283444", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Applicant", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "job_title", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-20 23:45:11.740188", + "modified": "2022-08-20 23:48:35.499218", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Applicant Pipeline", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Percentage", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/job_applicant_source/job_applicant_source.json b/hrms/hr/dashboard_chart/job_applicant_source/job_applicant_source.json new file mode 100644 index 0000000..1da6a12 --- /dev/null +++ b/hrms/hr/dashboard_chart/job_applicant_source/job_applicant_source.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Job Applicant Source", + "chart_type": "Group By", + "creation": "2022-08-20 22:59:15.210760", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Applicant", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "source", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-20 23:45:11.697841", + "modified": "2022-08-20 23:47:52.946872", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Applicant Source", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Percentage", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/job_applicants_by_country/job_applicants_by_country.json b/hrms/hr/dashboard_chart/job_applicants_by_country/job_applicants_by_country.json new file mode 100644 index 0000000..bd47a0f --- /dev/null +++ b/hrms/hr/dashboard_chart/job_applicants_by_country/job_applicants_by_country.json @@ -0,0 +1,32 @@ +{ + "based_on": "", + "chart_name": "Job Applicants by Country", + "chart_type": "Group By", + "creation": "2022-08-22 12:17:53.776473", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Applicant", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "country", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2022-08-22 12:18:01.288634", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Applicants by Country", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/job_application_frequency/job_application_frequency.json b/hrms/hr/dashboard_chart/job_application_frequency/job_application_frequency.json new file mode 100644 index 0000000..294cc52 --- /dev/null +++ b/hrms/hr/dashboard_chart/job_application_frequency/job_application_frequency.json @@ -0,0 +1,32 @@ +{ + "based_on": "creation", + "chart_name": "Job Application Frequency", + "chart_type": "Count", + "creation": "2022-08-20 22:00:12.227849", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Applicant", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-20 23:11:18.520971", + "modified": "2022-08-20 23:16:02.076184", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Application Frequency", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/job_application_status/job_application_status.json b/hrms/hr/dashboard_chart/job_application_status/job_application_status.json new file mode 100644 index 0000000..b77fe4c --- /dev/null +++ b/hrms/hr/dashboard_chart/job_application_status/job_application_status.json @@ -0,0 +1,30 @@ +{ + "chart_name": "Job Application Status", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:32.699696", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Applicant", + "dynamic_filters_json": "", + "filters_json": "[]", + "group_by_based_on": "status", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 12:07:53.129240", + "modified": "2022-08-22 12:10:29.144396", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Application Status", + "number_of_groups": 0, + "owner": "Administrator", + "roles": [], + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/job_offer_status/job_offer_status.json b/hrms/hr/dashboard_chart/job_offer_status/job_offer_status.json new file mode 100644 index 0000000..c170fb6 --- /dev/null +++ b/hrms/hr/dashboard_chart/job_offer_status/job_offer_status.json @@ -0,0 +1,33 @@ +{ + "based_on": "", + "chart_name": "Job Offer Status", + "chart_type": "Group By", + "creation": "2022-08-20 21:33:17.378147", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Job Offer", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "status", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-22 12:12:19.736710", + "modified": "2022-08-22 12:14:23.044346", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offer Status", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/shift_assignment_breakup/shift_assignment_breakup.json b/hrms/hr/dashboard_chart/shift_assignment_breakup/shift_assignment_breakup.json new file mode 100644 index 0000000..b871c7e --- /dev/null +++ b/hrms/hr/dashboard_chart/shift_assignment_breakup/shift_assignment_breakup.json @@ -0,0 +1,32 @@ +{ + "based_on": "", + "chart_name": "Shift Assignment Breakup", + "chart_type": "Group By", + "creation": "2022-08-21 18:11:42.510195", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Shift Assignment", + "dynamic_filters_json": "[[\"Shift Assignment\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Shift Assignment\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_based_on": "shift_type", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2022-08-21 18:12:47.352410", + "modified_by": "Administrator", + "module": "HR", + "name": "Shift Assignment Breakup", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/timesheet_activity_breakup/timesheet_activity_breakup.json b/hrms/hr/dashboard_chart/timesheet_activity_breakup/timesheet_activity_breakup.json new file mode 100644 index 0000000..dd042ad --- /dev/null +++ b/hrms/hr/dashboard_chart/timesheet_activity_breakup/timesheet_activity_breakup.json @@ -0,0 +1,34 @@ +{ + "aggregate_function_based_on": "hours", + "based_on": "", + "chart_name": "Timesheet Activity Breakup", + "chart_type": "Group By", + "creation": "2022-08-21 14:31:10.401241", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Timesheet Detail", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_based_on": "activity_type", + "group_by_type": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2022-08-21 17:55:44.318686", + "modified": "2022-08-21 17:59:38.576219", + "modified_by": "Administrator", + "module": "HR", + "name": "Timesheet Activity Breakup", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "Timesheet", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Percentage", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/training_type/training_type.json b/hrms/hr/dashboard_chart/training_type/training_type.json new file mode 100644 index 0000000..f28cf95 --- /dev/null +++ b/hrms/hr/dashboard_chart/training_type/training_type.json @@ -0,0 +1,32 @@ +{ + "based_on": "", + "chart_name": "Training Type", + "chart_type": "Group By", + "creation": "2022-08-21 13:29:27.202404", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Training Event", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Training Event\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_based_on": "type", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2022-08-21 13:38:05.390856", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Type", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Pie", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/y_o_y_promotions/y_o_y_promotions.json b/hrms/hr/dashboard_chart/y_o_y_promotions/y_o_y_promotions.json new file mode 100644 index 0000000..84d1211 --- /dev/null +++ b/hrms/hr/dashboard_chart/y_o_y_promotions/y_o_y_promotions.json @@ -0,0 +1,31 @@ +{ + "based_on": "promotion_date", + "chart_name": "Y-O-Y Promotions", + "chart_type": "Count", + "creation": "2022-08-21 13:34:12.830736", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee Promotion", + "dynamic_filters_json": "[[\"Employee Promotion\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Promotion\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2022-08-21 13:38:46.190352", + "modified_by": "Administrator", + "module": "HR", + "name": "Y-O-Y Promotions", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart/y_o_y_transfers/y_o_y_transfers.json b/hrms/hr/dashboard_chart/y_o_y_transfers/y_o_y_transfers.json new file mode 100644 index 0000000..cc9d163 --- /dev/null +++ b/hrms/hr/dashboard_chart/y_o_y_transfers/y_o_y_transfers.json @@ -0,0 +1,31 @@ +{ + "based_on": "transfer_date", + "chart_name": "Y-O-Y Transfers", + "chart_type": "Count", + "creation": "2022-08-21 13:28:05.162754", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Employee Transfer", + "dynamic_filters_json": "[[\"Employee Transfer\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Transfer\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2022-08-21 13:38:54.890663", + "modified_by": "Administrator", + "module": "HR", + "name": "Y-O-Y Transfers", + "number_of_groups": 0, + "owner": "Administrator", + "parent_document_type": "", + "roles": [], + "source": "", + "time_interval": "Yearly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart_source/__init__.py b/hrms/hr/dashboard_chart_source/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/dashboard_chart_source/employees_by_age/__init__.py b/hrms/hr/dashboard_chart_source/employees_by_age/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.js b/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.js new file mode 100644 index 0000000..35c10f9 --- /dev/null +++ b/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.js @@ -0,0 +1,14 @@ +frappe.provide("frappe.dashboards.chart_sources"); + +frappe.dashboards.chart_sources["Employees by Age"] = { + method: "hrms.hr.dashboard_chart_source.employees_by_age.employees_by_age.get_data", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + }, + ] +}; diff --git a/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.json b/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.json new file mode 100644 index 0000000..5d535d5 --- /dev/null +++ b/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.json @@ -0,0 +1,13 @@ +{ + "creation": "2022-08-22 12:49:07.303139", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2022-08-22 12:49:07.303139", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees by Age", + "owner": "Administrator", + "source_name": "Employees by Age", + "timeseries": 0 +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.py b/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.py new file mode 100644 index 0000000..8784fad --- /dev/null +++ b/hrms/hr/dashboard_chart_source/employees_by_age/employees_by_age.py @@ -0,0 +1,87 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from dateutil.relativedelta import relativedelta + +import frappe +from frappe import _ +from frappe.utils import getdate +from frappe.utils.dashboard import cache_source + + +@frappe.whitelist() +@cache_source +def get_data( + chart_name=None, + chart=None, + no_cache=None, + filters=None, + from_date=None, + to_date=None, + timespan=None, + time_interval=None, + heatmap_year=None, +) -> dict[str, list]: + if filters: + filters = frappe.parse_json(filters) + + employees = frappe.db.get_list( + "Employee", + filters={"company": filters.get("company"), "status": "Active"}, + pluck="date_of_birth", + ) + age_list = get_age_list(employees) + ranges = get_ranges() + + age_range, values = get_employees_by_age(age_list, ranges) + + return { + "labels": age_range, + "datasets": [ + {"name": _("Employees"), "values": values}, + ], + } + + +def get_ranges() -> list[tuple[int, int]]: + ranges = [] + + for i in range(15, 80, 5): + ranges.append((i, i + 4)) + + ranges.append(80) + + return ranges + + +def get_age_list(employees) -> list[int]: + age_list = [] + for dob in employees: + if not dob: + continue + age = relativedelta(getdate(), getdate(dob)).years + age_list.append(age) + + return age_list + + +def get_employees_by_age(age_list, ranges) -> tuple[list[str], list[int]]: + age_range = [] + values = [] + for bracket in ranges: + if isinstance(bracket, int): + age_range.append(f"{bracket}+") + else: + age_range.append(f"{bracket[0]}-{bracket[1]}") + + count = 0 + for age in age_list: + if (isinstance(bracket, int) and age >= bracket) or ( + isinstance(bracket, tuple) and bracket[0] <= age <= bracket[1] + ): + count += 1 + + values.append(count) + + return age_range, values diff --git a/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/__init__.py b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.js b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.js new file mode 100644 index 0000000..114a33c --- /dev/null +++ b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.js @@ -0,0 +1,35 @@ +frappe.provide("frappe.dashboards.chart_sources"); + +frappe.dashboards.chart_sources["Hiring vs Attrition Count"] = { + method: "hrms.hr.dashboard_chart_source.hiring_vs_attrition_count.hiring_vs_attrition_count.get_data", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + reqd: 1, + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + }, + { + fieldname: "time_interval", + label: __("Time Interval"), + fieldtype: "Select", + options: ["Monthly", "Quarterly", "Yearly"], + default: "Monthly", + reqd: 1 + }, + ] +}; diff --git a/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.json b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.json new file mode 100644 index 0000000..77b8c89 --- /dev/null +++ b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.json @@ -0,0 +1,13 @@ +{ + "creation": "2022-08-21 21:38:22.271985", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2022-08-21 21:38:22.271985", + "modified_by": "Administrator", + "module": "HR", + "name": "Hiring vs Attrition Count", + "owner": "Administrator", + "source_name": "Hiring vs Attrition Count", + "timeseries": 1 +} \ No newline at end of file diff --git a/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.py b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.py new file mode 100644 index 0000000..bddec58 --- /dev/null +++ b/hrms/hr/dashboard_chart_source/hiring_vs_attrition_count/hiring_vs_attrition_count.py @@ -0,0 +1,69 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_result +from frappe.utils import getdate +from frappe.utils.dashboard import cache_source +from frappe.utils.dateutils import get_period + + +@frappe.whitelist() +@cache_source +def get_data( + chart_name=None, + chart=None, + no_cache=None, + filters=None, + from_date=None, + to_date=None, + timespan=None, + time_interval=None, + heatmap_year=None, +) -> dict[str, list]: + if filters: + filters = frappe.parse_json(filters) + + from_date = filters.get("from_date") + to_date = filters.get("to_date") + + if not to_date: + to_date = getdate() + + hiring = get_records(from_date, to_date, "date_of_joining", filters.get("company")) + attrition = get_records(from_date, to_date, "relieving_date", filters.get("company")) + + hiring_data = get_result(hiring, filters.get("time_interval"), from_date, to_date, "Count") + attrition_data = get_result(attrition, filters.get("time_interval"), from_date, to_date, "Count") + + return { + "labels": [get_period(r[0], filters.get("time_interval")) for r in hiring_data], + "datasets": [ + {"name": _("Hiring Count"), "values": [r[1] for r in hiring_data]}, + {"name": _("Attrition Count"), "values": [r[1] for r in attrition_data]}, + ], + } + + +def get_records( + from_date: str, to_date: str, datefield: str, company: str +) -> tuple[tuple[str, float, int]]: + filters = [ + ["Employee", "company", "=", company], + ["Employee", datefield, ">=", from_date, False], + ["Employee", datefield, "<=", to_date, False], + ] + + data = frappe.db.get_list( + "Employee", + fields=[f"{datefield} as _unit", "SUM(1)", "COUNT(*)"], + filters=filters, + group_by="_unit", + order_by="_unit asc", + as_list=True, + ignore_ifnull=True, + ) + + return data diff --git a/hrms/hr/doctype/__init__.py b/hrms/hr/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appointment_letter/__init__.py b/hrms/hr/doctype/appointment_letter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appointment_letter/appointment_letter.js b/hrms/hr/doctype/appointment_letter/appointment_letter.js new file mode 100644 index 0000000..1642bcc --- /dev/null +++ b/hrms/hr/doctype/appointment_letter/appointment_letter.js @@ -0,0 +1,30 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appointment Letter', { + appointment_letter_template: function(frm){ + if (frm.doc.appointment_letter_template){ + frappe.call({ + method: 'hrms.hr.doctype.appointment_letter.appointment_letter.get_appointment_letter_details', + args : { + template : frm.doc.appointment_letter_template + }, + callback: function(r){ + if(r.message){ + let message_body = r.message; + frm.set_value("introduction", message_body[0].introduction); + frm.set_value("closing_notes", message_body[0].closing_notes); + frm.doc.terms = [] + for (var i in message_body[1].description){ + frm.add_child("terms"); + frm.fields_dict.terms.get_value()[i].title = message_body[1].description[i].title; + frm.fields_dict.terms.get_value()[i].description = message_body[1].description[i].description; + } + frm.refresh(); + } + } + + }); + } + }, +}); diff --git a/hrms/hr/doctype/appointment_letter/appointment_letter.json b/hrms/hr/doctype/appointment_letter/appointment_letter.json new file mode 100644 index 0000000..012f6b6 --- /dev/null +++ b/hrms/hr/doctype/appointment_letter/appointment_letter.json @@ -0,0 +1,128 @@ +{ + "actions": [], + "autoname": "HR-APP-LETTER-.#####", + "creation": "2019-12-26 12:35:49.574828", + "default_print_format": "Standard Appointment Letter", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "job_applicant", + "applicant_name", + "column_break_3", + "company", + "appointment_date", + "appointment_letter_template", + "body_section", + "introduction", + "terms", + "closing_notes" + ], + "fields": [ + { + "fetch_from": "job_applicant.applicant_name", + "fieldname": "applicant_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Applicant Name", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "appointment_date", + "fieldtype": "Date", + "label": "Appointment Date", + "reqd": 1 + }, + { + "fieldname": "appointment_letter_template", + "fieldtype": "Link", + "label": "Appointment Letter Template", + "options": "Appointment Letter Template", + "reqd": 1 + }, + { + "fetch_from": "appointment_letter_template.introduction", + "fieldname": "introduction", + "fieldtype": "Long Text", + "label": "Introduction", + "reqd": 1 + }, + { + "fieldname": "body_section", + "fieldtype": "Section Break", + "label": "Body" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "label": "Job Applicant", + "options": "Job Applicant", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "closing_notes", + "fieldtype": "Text", + "label": "Closing Notes" + }, + { + "fieldname": "terms", + "fieldtype": "Table", + "label": "Terms", + "options": "Appointment Letter content", + "reqd": 1 + } + ], + "links": [], + "modified": "2022-01-18 19:27:35.649424", + "modified_by": "Administrator", + "module": "HR", + "name": "Appointment Letter", + "name_case": "Title Case", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "applicant_name, company", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "applicant_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/appointment_letter/appointment_letter.py b/hrms/hr/doctype/appointment_letter/appointment_letter.py new file mode 100644 index 0000000..a58589a --- /dev/null +++ b/hrms/hr/doctype/appointment_letter/appointment_letter.py @@ -0,0 +1,29 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe.model.document import Document + + +class AppointmentLetter(Document): + pass + + +@frappe.whitelist() +def get_appointment_letter_details(template): + body = [] + intro = frappe.get_list( + "Appointment Letter Template", + fields=["introduction", "closing_notes"], + filters={"name": template}, + )[0] + content = frappe.get_all( + "Appointment Letter content", + fields=["title", "description"], + filters={"parent": template}, + order_by="idx", + ) + body.append(intro) + body.append({"description": content}) + return body diff --git a/hrms/hr/doctype/appointment_letter/test_appointment_letter.py b/hrms/hr/doctype/appointment_letter/test_appointment_letter.py new file mode 100644 index 0000000..e0f65b4 --- /dev/null +++ b/hrms/hr/doctype/appointment_letter/test_appointment_letter.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestAppointmentLetter(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/appointment_letter_content/__init__.py b/hrms/hr/doctype/appointment_letter_content/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appointment_letter_content/appointment_letter_content.json b/hrms/hr/doctype/appointment_letter_content/appointment_letter_content.json new file mode 100644 index 0000000..17a2b91 --- /dev/null +++ b/hrms/hr/doctype/appointment_letter_content/appointment_letter_content.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2019-12-26 12:22:16.575767", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "description" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "description", + "fieldtype": "Long Text", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2019-12-26 12:24:09.824084", + "modified_by": "Administrator", + "module": "HR", + "name": "Appointment Letter content", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/appointment_letter_content/appointment_letter_content.py b/hrms/hr/doctype/appointment_letter_content/appointment_letter_content.py new file mode 100644 index 0000000..d158013 --- /dev/null +++ b/hrms/hr/doctype/appointment_letter_content/appointment_letter_content.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class AppointmentLettercontent(Document): + pass diff --git a/hrms/hr/doctype/appointment_letter_template/__init__.py b/hrms/hr/doctype/appointment_letter_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.js b/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.js new file mode 100644 index 0000000..8270f7a --- /dev/null +++ b/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appointment Letter Template', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.json b/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.json new file mode 100644 index 0000000..5e50fe6 --- /dev/null +++ b/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "autoname": "field:template_name", + "creation": "2019-12-26 12:20:14.219578", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "template_name", + "introduction", + "terms", + "closing_notes" + ], + "fields": [ + { + "fieldname": "introduction", + "fieldtype": "Long Text", + "in_list_view": 1, + "label": "Introduction", + "reqd": 1 + }, + { + "fieldname": "closing_notes", + "fieldtype": "Text", + "label": "Closing Notes" + }, + { + "fieldname": "terms", + "fieldtype": "Table", + "label": "Terms", + "options": "Appointment Letter content", + "reqd": 1 + }, + { + "fieldname": "template_name", + "fieldtype": "Data", + "label": "Template Name", + "reqd": 1, + "unique": 1 + } + ], + "links": [], + "modified": "2022-01-18 19:25:14.614616", + "modified_by": "Administrator", + "module": "HR", + "name": "Appointment Letter Template", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "template_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "template_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.py b/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.py new file mode 100644 index 0000000..9ac726e --- /dev/null +++ b/hrms/hr/doctype/appointment_letter_template/appointment_letter_template.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class AppointmentLetterTemplate(Document): + pass diff --git a/hrms/hr/doctype/appointment_letter_template/test_appointment_letter_template.py b/hrms/hr/doctype/appointment_letter_template/test_appointment_letter_template.py new file mode 100644 index 0000000..aa87da3 --- /dev/null +++ b/hrms/hr/doctype/appointment_letter_template/test_appointment_letter_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestAppointmentLetterTemplate(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/appraisal/README.md b/hrms/hr/doctype/appraisal/README.md new file mode 100644 index 0000000..7798f18 --- /dev/null +++ b/hrms/hr/doctype/appraisal/README.md @@ -0,0 +1 @@ +Performance of an Employee in a Time Period against given goals. \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal/__init__.py b/hrms/hr/doctype/appraisal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appraisal/appraisal.js b/hrms/hr/doctype/appraisal/appraisal.js new file mode 100644 index 0000000..c0f80c6 --- /dev/null +++ b/hrms/hr/doctype/appraisal/appraisal.js @@ -0,0 +1,79 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on('Appraisal', { + setup: function(frm) { + frm.add_fetch('employee', 'company', 'company'); + frm.add_fetch('employee', 'employee_name', 'employee_name'); + frm.fields_dict.employee.get_query = function(doc,cdt,cdn) { + return{ query: "erpnext.controllers.queries.employee_query" } + }; + }, + + onload: function(frm) { + if(!frm.doc.status) { + frm.set_value('status', 'Draft'); + } + }, + + kra_template: function(frm) { + frm.doc.goals = []; + erpnext.utils.map_current_doc({ + method: "hrms.hr.doctype.appraisal.appraisal.fetch_appraisal_template", + source_name: frm.doc.kra_template, + frm: frm + }); + }, + + calculate_total: function(frm) { + let goals = frm.doc.goals || []; + let total = 0; + + if (goals == []) { + frm.set_value('total_score', 0); + return; + } + for (let i = 0; i 5) { + frappe.msgprint(__("Score must be less than or equal to 5")); + d.score = 0; + refresh_field('score', d.name, 'goals'); + } + else { + frm.trigger('set_score_earned'); + } + }, + per_weightage: function(frm) { + frm.trigger('set_score_earned'); + }, + goals_remove: function(frm) { + frm.trigger('set_score_earned'); + } +}); diff --git a/hrms/hr/doctype/appraisal/appraisal.json b/hrms/hr/doctype/appraisal/appraisal.json new file mode 100644 index 0000000..9ca7bcc --- /dev/null +++ b/hrms/hr/doctype/appraisal/appraisal.json @@ -0,0 +1,254 @@ +{ + "actions": [], + "autoname": "naming_series:", + "creation": "2013-01-10 16:34:12", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "employee_details", + "naming_series", + "kra_template", + "employee", + "employee_name", + "column_break0", + "status", + "start_date", + "end_date", + "department", + "section_break0", + "goals", + "total_score", + "section_break1", + "remarks", + "other_details", + "company", + "column_break_17", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee_details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-APR-.YY.-.MM.", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "kra_template", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Appraisal Template", + "oldfieldname": "kra_template", + "oldfieldtype": "Link", + "options": "Appraisal Template", + "reqd": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "For Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "For Employee Name", + "oldfieldname": "employee_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "default": "Draft", + "depends_on": "kra_template", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nDraft\nSubmitted\nCompleted\nCancelled", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "oldfieldname": "start_date", + "oldfieldtype": "Date", + "reqd": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "oldfieldname": "end_date", + "oldfieldtype": "Date", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "section_break0", + "fieldtype": "Section Break", + "label": "Goals", + "oldfieldtype": "Section Break", + "options": "Simple" + }, + { + "fieldname": "goals", + "fieldtype": "Table", + "label": "Goals", + "oldfieldname": "appraisal_details", + "oldfieldtype": "Table", + "options": "Appraisal Goal" + }, + { + "fieldname": "total_score", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Total Score (Out of 5)", + "no_copy": 1, + "oldfieldname": "total_score", + "oldfieldtype": "Currency", + "read_only": 1 + }, + { + "depends_on": "kra_template", + "fieldname": "section_break1", + "fieldtype": "Section Break" + }, + { + "description": "Any other remarks, noteworthy effort that should go in the records.", + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks" + }, + { + "depends_on": "kra_template", + "fieldname": "other_details", + "fieldtype": "Section Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Appraisal", + "print_hide": 1, + "read_only": 1, + "report_hide": 1, + "width": "150px" + } + ], + "icon": "fa fa-thumbs-up", + "idx": 1, + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2020-10-03 21:48:33.297065", + "modified_by": "Administrator", + "module": "HR", + "name": "Appraisal", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "status, employee, employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal/appraisal.py b/hrms/hr/doctype/appraisal/appraisal.py new file mode 100644 index 0000000..112ab75 --- /dev/null +++ b/hrms/hr/doctype/appraisal/appraisal.py @@ -0,0 +1,94 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +from frappe.utils import flt, getdate + +from hrms.hr.utils import set_employee_name, validate_active_employee + + +class Appraisal(Document): + def validate(self): + if not self.status: + self.status = "Draft" + + if not self.goals: + frappe.throw(_("Goals cannot be empty")) + + validate_active_employee(self.employee) + set_employee_name(self) + self.validate_dates() + self.validate_existing_appraisal() + self.calculate_total() + + def get_employee_name(self): + self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name") + return self.employee_name + + def validate_dates(self): + if getdate(self.start_date) > getdate(self.end_date): + frappe.throw(_("End Date can not be less than Start Date")) + + def validate_existing_appraisal(self): + chk = frappe.db.sql( + """select name from `tabAppraisal` where employee=%s + and (status='Submitted' or status='Completed') + and ((start_date>=%s and start_date<=%s) + or (end_date>=%s and end_date<=%s))""", + (self.employee, self.start_date, self.end_date, self.start_date, self.end_date), + ) + if chk: + frappe.throw( + _("Appraisal {0} created for Employee {1} in the given date range").format( + chk[0][0], self.employee_name + ) + ) + + def calculate_total(self): + total, total_w = 0, 0 + for d in self.get("goals"): + if d.score: + d.score_earned = flt(d.score) * flt(d.per_weightage) / 100 + total = total + d.score_earned + total_w += flt(d.per_weightage) + + if int(total_w) != 100: + frappe.throw( + _("Total weightage assigned should be 100%.
It is {0}").format(str(total_w) + "%") + ) + + if ( + frappe.db.get_value("Employee", self.employee, "user_id") != frappe.session.user and total == 0 + ): + frappe.throw(_("Total cannot be zero")) + + self.total_score = total + + def on_submit(self): + frappe.db.set(self, "status", "Submitted") + + def on_cancel(self): + frappe.db.set(self, "status", "Cancelled") + + +@frappe.whitelist() +def fetch_appraisal_template(source_name, target_doc=None): + target_doc = get_mapped_doc( + "Appraisal Template", + source_name, + { + "Appraisal Template": { + "doctype": "Appraisal", + }, + "Appraisal Template Goal": { + "doctype": "Appraisal Goal", + }, + }, + target_doc, + ) + + return target_doc diff --git a/hrms/hr/doctype/appraisal/test_appraisal.py b/hrms/hr/doctype/appraisal/test_appraisal.py new file mode 100644 index 0000000..13a39f3 --- /dev/null +++ b/hrms/hr/doctype/appraisal/test_appraisal.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +# test_records = frappe.get_test_records('Appraisal') + + +class TestAppraisal(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/appraisal_goal/README.md b/hrms/hr/doctype/appraisal_goal/README.md new file mode 100644 index 0000000..851a403 --- /dev/null +++ b/hrms/hr/doctype/appraisal_goal/README.md @@ -0,0 +1 @@ +Goal for the parent Appraisal. \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal_goal/__init__.py b/hrms/hr/doctype/appraisal_goal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appraisal_goal/appraisal_goal.json b/hrms/hr/doctype/appraisal_goal/appraisal_goal.json new file mode 100644 index 0000000..b8ec5a8 --- /dev/null +++ b/hrms/hr/doctype/appraisal_goal/appraisal_goal.json @@ -0,0 +1,220 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "beta": 0, + "creation": "2013-02-22 01:27:44", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "editable_grid": 1, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Key Responsibility Area", + "fieldname": "kra", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Goal", + "length": 0, + "no_copy": 0, + "oldfieldname": "kra", + "oldfieldtype": "Small Text", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "240px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "240px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "per_weightage", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Weightage (%)", + "length": 0, + "no_copy": 0, + "oldfieldname": "per_weightage", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "70px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "70px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "score", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Score (0-5)", + "length": 0, + "no_copy": 1, + "oldfieldname": "score", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "70px", + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "70px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "score_earned", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Score Earned", + "length": 0, + "no_copy": 1, + "oldfieldname": "score_earned", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "70px", + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "70px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "HR", + "name": "Appraisal Goal", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal_goal/appraisal_goal.py b/hrms/hr/doctype/appraisal_goal/appraisal_goal.py new file mode 100644 index 0000000..3cbc918 --- /dev/null +++ b/hrms/hr/doctype/appraisal_goal/appraisal_goal.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.document import Document + + +class AppraisalGoal(Document): + pass diff --git a/hrms/hr/doctype/appraisal_template/README.md b/hrms/hr/doctype/appraisal_template/README.md new file mode 100644 index 0000000..d58a744 --- /dev/null +++ b/hrms/hr/doctype/appraisal_template/README.md @@ -0,0 +1 @@ +Standard set of goals for an Employee / Designation / Job Profile. New Appraisal transactions can be created from the Template. \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal_template/__init__.py b/hrms/hr/doctype/appraisal_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appraisal_template/appraisal_template.js b/hrms/hr/doctype/appraisal_template/appraisal_template.js new file mode 100644 index 0000000..cc77292 --- /dev/null +++ b/hrms/hr/doctype/appraisal_template/appraisal_template.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appraisal Template', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/appraisal_template/appraisal_template.json b/hrms/hr/doctype/appraisal_template/appraisal_template.json new file mode 100644 index 0000000..7329c35 --- /dev/null +++ b/hrms/hr/doctype/appraisal_template/appraisal_template.json @@ -0,0 +1,170 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:kra_title", + "beta": 0, + "creation": "2012-07-03 13:30:39", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "kra_title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Appraisal Template Title", + "length": 0, + "no_copy": 0, + "oldfieldname": "kra_title", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "300px", + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "300px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "goals", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Goals", + "length": 0, + "no_copy": 0, + "oldfieldname": "kra_sheet", + "oldfieldtype": "Table", + "options": "Appraisal Template Goal", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-file-text", + "idx": 1, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "HR", + "name": "Appraisal Template", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "is_custom": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "is_custom": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Employee", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal_template/appraisal_template.py b/hrms/hr/doctype/appraisal_template/appraisal_template.py new file mode 100644 index 0000000..6b5921e --- /dev/null +++ b/hrms/hr/doctype/appraisal_template/appraisal_template.py @@ -0,0 +1,21 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import cint, flt + + +class AppraisalTemplate(Document): + def validate(self): + self.check_total_points() + + def check_total_points(self): + total_points = 0 + for d in self.get("goals"): + total_points += flt(d.per_weightage) + + if cint(total_points) != 100: + frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) diff --git a/hrms/hr/doctype/appraisal_template/appraisal_template_dashboard.py b/hrms/hr/doctype/appraisal_template/appraisal_template_dashboard.py new file mode 100644 index 0000000..476de4f --- /dev/null +++ b/hrms/hr/doctype/appraisal_template/appraisal_template_dashboard.py @@ -0,0 +1,7 @@ +def get_data(): + return { + "fieldname": "kra_template", + "transactions": [ + {"items": ["Appraisal"]}, + ], + } diff --git a/hrms/hr/doctype/appraisal_template/test_appraisal_template.py b/hrms/hr/doctype/appraisal_template/test_appraisal_template.py new file mode 100644 index 0000000..560e992 --- /dev/null +++ b/hrms/hr/doctype/appraisal_template/test_appraisal_template.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +# test_records = frappe.get_test_records('Appraisal Template') + + +class TestAppraisalTemplate(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/appraisal_template_goal/README.md b/hrms/hr/doctype/appraisal_template_goal/README.md new file mode 100644 index 0000000..1b81afb --- /dev/null +++ b/hrms/hr/doctype/appraisal_template_goal/README.md @@ -0,0 +1 @@ +Goal details for the parent Appraisal Template. \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal_template_goal/__init__.py b/hrms/hr/doctype/appraisal_template_goal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.json b/hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.json new file mode 100644 index 0000000..2858419 --- /dev/null +++ b/hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.json @@ -0,0 +1,91 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "beta": 0, + "creation": "2013-02-22 01:27:44", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "editable_grid": 1, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Key Performance Area", + "fieldname": "kra", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "KRA", + "length": 0, + "no_copy": 0, + "oldfieldname": "kra", + "oldfieldtype": "Small Text", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "200px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "200px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "per_weightage", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Weightage (%)", + "length": 0, + "no_copy": 0, + "oldfieldname": "per_weightage", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "100px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "100px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "HR", + "name": "Appraisal Template Goal", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.py b/hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.py new file mode 100644 index 0000000..e6c5f64 --- /dev/null +++ b/hrms/hr/doctype/appraisal_template_goal/appraisal_template_goal.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.document import Document + + +class AppraisalTemplateGoal(Document): + pass diff --git a/hrms/hr/doctype/attendance/README.md b/hrms/hr/doctype/attendance/README.md new file mode 100644 index 0000000..f420b07 --- /dev/null +++ b/hrms/hr/doctype/attendance/README.md @@ -0,0 +1 @@ +Attendance record of an Employee on a particular date. \ No newline at end of file diff --git a/hrms/hr/doctype/attendance/__init__.py b/hrms/hr/doctype/attendance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/attendance/attendance.js b/hrms/hr/doctype/attendance/attendance.js new file mode 100644 index 0000000..7964078 --- /dev/null +++ b/hrms/hr/doctype/attendance/attendance.js @@ -0,0 +1,15 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +cur_frm.add_fetch('employee', 'company', 'company'); +cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); + +cur_frm.cscript.onload = function(doc, cdt, cdn) { + if(doc.__islocal) cur_frm.set_value("attendance_date", frappe.datetime.get_today()); +} + +cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) { + return{ + query: "erpnext.controllers.queries.employee_query" + } +} diff --git a/hrms/hr/doctype/attendance/attendance.json b/hrms/hr/doctype/attendance/attendance.json new file mode 100644 index 0000000..134098f --- /dev/null +++ b/hrms/hr/doctype/attendance/attendance.json @@ -0,0 +1,260 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-01-10 16:34:13", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "attendance_details", + "naming_series", + "employee", + "employee_name", + "working_hours", + "status", + "leave_type", + "leave_application", + "column_break0", + "attendance_date", + "company", + "department", + "attendance_request", + "details_section", + "shift", + "in_time", + "out_time", + "column_break_18", + "late_entry", + "early_exit", + "amended_from" + ], + "fields": [ + { + "fieldname": "attendance_details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "Simple" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "HR-ATT-.YYYY.-", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Employee Name", + "oldfieldname": "employee_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "depends_on": "working_hours", + "fieldname": "working_hours", + "fieldtype": "Float", + "label": "Working Hours", + "precision": "1", + "read_only": 1 + }, + { + "default": "Present", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nPresent\nAbsent\nOn Leave\nHalf Day\nWork From Home", + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)", + "fieldname": "leave_type", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Leave Type", + "mandatory_depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)", + "oldfieldname": "leave_type", + "oldfieldtype": "Link", + "options": "Leave Type" + }, + { + "fieldname": "leave_application", + "fieldtype": "Link", + "label": "Leave Application", + "no_copy": 1, + "options": "Leave Application", + "read_only": 1 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "attendance_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Attendance Date", + "oldfieldname": "attendance_date", + "oldfieldtype": "Date", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "shift", + "fieldtype": "Link", + "label": "Shift", + "options": "Shift Type" + }, + { + "fieldname": "attendance_request", + "fieldtype": "Link", + "label": "Attendance Request", + "options": "Attendance Request", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "options": "Attendance", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "late_entry", + "fieldtype": "Check", + "label": "Late Entry" + }, + { + "default": "0", + "fieldname": "early_exit", + "fieldtype": "Check", + "label": "Early Exit" + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "depends_on": "shift", + "fieldname": "in_time", + "fieldtype": "Datetime", + "label": "In Time", + "read_only": 1 + }, + { + "depends_on": "shift", + "fieldname": "out_time", + "fieldtype": "Datetime", + "label": "Out Time", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + } + ], + "icon": "fa fa-ok", + "idx": 1, + "is_submittable": 1, + "links": [], + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "HR", + "name": "Attendance", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name,attendance_date,status", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/attendance/attendance.py b/hrms/hr/doctype/attendance/attendance.py new file mode 100644 index 0000000..4030de9 --- /dev/null +++ b/hrms/hr/doctype/attendance/attendance.py @@ -0,0 +1,390 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.query_builder import Criterion +from frappe.utils import cint, cstr, formatdate, get_datetime, get_link_to_form, getdate, nowdate + +from hrms.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings +from hrms.hr.utils import get_holiday_dates_for_employee, validate_active_employee + + +class DuplicateAttendanceError(frappe.ValidationError): + pass + + +class OverlappingShiftAttendanceError(frappe.ValidationError): + pass + + +class Attendance(Document): + def validate(self): + from erpnext.controllers.status_updater import validate_status + + validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"]) + validate_active_employee(self.employee) + self.validate_attendance_date() + self.validate_duplicate_record() + self.validate_overlapping_shift_attendance() + self.validate_employee_status() + self.check_leave_record() + + def on_cancel(self): + self.unlink_attendance_from_checkins() + + def validate_attendance_date(self): + date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") + + # leaves can be marked for future dates + if ( + self.status != "On Leave" + and not self.leave_application + and getdate(self.attendance_date) > getdate(nowdate()) + ): + frappe.throw(_("Attendance can not be marked for future dates")) + elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): + frappe.throw(_("Attendance date can not be less than employee's joining date")) + + def validate_duplicate_record(self): + duplicate = get_duplicate_attendance_record( + self.employee, self.attendance_date, self.shift, self.name + ) + + if duplicate: + frappe.throw( + _("Attendance for employee {0} is already marked for the date {1}: {2}").format( + frappe.bold(self.employee), + frappe.bold(self.attendance_date), + get_link_to_form("Attendance", duplicate[0].name), + ), + title=_("Duplicate Attendance"), + exc=DuplicateAttendanceError, + ) + + def validate_overlapping_shift_attendance(self): + attendance = get_overlapping_shift_attendance( + self.employee, self.attendance_date, self.shift, self.name + ) + + if attendance: + frappe.throw( + _("Attendance for employee {0} is already marked for an overlapping shift {1}: {2}").format( + frappe.bold(self.employee), + frappe.bold(attendance.shift), + get_link_to_form("Attendance", attendance.name), + ), + title=_("Overlapping Shift Attendance"), + exc=OverlappingShiftAttendanceError, + ) + + def validate_employee_status(self): + if frappe.db.get_value("Employee", self.employee, "status") == "Inactive": + frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee)) + + def check_leave_record(self): + leave_record = frappe.db.sql( + """ + select leave_type, half_day, half_day_date + from `tabLeave Application` + where employee = %s + and %s between from_date and to_date + and status = 'Approved' + and docstatus = 1 + """, + (self.employee, self.attendance_date), + as_dict=True, + ) + if leave_record: + for d in leave_record: + self.leave_type = d.leave_type + if d.half_day_date == getdate(self.attendance_date): + self.status = "Half Day" + frappe.msgprint( + _("Employee {0} on Half day on {1}").format(self.employee, formatdate(self.attendance_date)) + ) + else: + self.status = "On Leave" + frappe.msgprint( + _("Employee {0} is on Leave on {1}").format(self.employee, formatdate(self.attendance_date)) + ) + + if self.status in ("On Leave", "Half Day"): + if not leave_record: + frappe.msgprint( + _("No leave record found for employee {0} on {1}").format( + self.employee, formatdate(self.attendance_date) + ), + alert=1, + ) + elif self.leave_type: + self.leave_type = None + self.leave_application = None + + def validate_employee(self): + emp = frappe.db.sql( + "select name from `tabEmployee` where name = %s and status = 'Active'", self.employee + ) + if not emp: + frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee)) + + def unlink_attendance_from_checkins(self): + EmployeeCheckin = frappe.qb.DocType("Employee Checkin") + linked_logs = ( + frappe.qb.from_(EmployeeCheckin) + .select(EmployeeCheckin.name) + .where(EmployeeCheckin.attendance == self.name) + .for_update() + .run(as_dict=True) + ) + + if linked_logs: + ( + frappe.qb.update(EmployeeCheckin) + .set("attendance", "") + .where(EmployeeCheckin.attendance == self.name) + ).run() + + frappe.msgprint( + msg=_("Unlinked Attendance record from Employee Checkins: {}").format( + ", ".join(get_link_to_form("Employee Checkin", log.name) for log in linked_logs) + ), + title=_("Unlinked logs"), + indicator="blue", + is_minimizable=True, + wide=True, + ) + + +def get_duplicate_attendance_record(employee, attendance_date, shift, name=None): + attendance = frappe.qb.DocType("Attendance") + query = ( + frappe.qb.from_(attendance) + .select(attendance.name) + .where((attendance.employee == employee) & (attendance.docstatus < 2)) + ) + + if shift: + query = query.where( + Criterion.any( + [ + Criterion.all( + [ + ((attendance.shift.isnull()) | (attendance.shift == "")), + (attendance.attendance_date == attendance_date), + ] + ), + Criterion.all( + [ + ((attendance.shift.isnotnull()) | (attendance.shift != "")), + (attendance.attendance_date == attendance_date), + (attendance.shift == shift), + ] + ), + ] + ) + ) + else: + query = query.where((attendance.attendance_date == attendance_date)) + + if name: + query = query.where(attendance.name != name) + + return query.run(as_dict=True) + + +def get_overlapping_shift_attendance(employee, attendance_date, shift, name=None): + if not shift: + return {} + + attendance = frappe.qb.DocType("Attendance") + query = ( + frappe.qb.from_(attendance) + .select(attendance.name, attendance.shift) + .where( + (attendance.employee == employee) + & (attendance.docstatus < 2) + & (attendance.attendance_date == attendance_date) + & (attendance.shift != shift) + ) + ) + + if name: + query = query.where(attendance.name != name) + + overlapping_attendance = query.run(as_dict=True) + + if overlapping_attendance and has_overlapping_timings(shift, overlapping_attendance[0].shift): + return overlapping_attendance[0] + return {} + + +@frappe.whitelist() +def get_events(start, end, filters=None): + events = [] + + employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}) + + if not employee: + return events + + from frappe.desk.reportview import get_filters_cond + + conditions = get_filters_cond("Attendance", filters, []) + add_attendance(events, start, end, conditions=conditions) + return events + + +def add_attendance(events, start, end, conditions=None): + query = """select name, attendance_date, status + from `tabAttendance` where + attendance_date between %(from_date)s and %(to_date)s + and docstatus < 2""" + if conditions: + query += conditions + + for d in frappe.db.sql(query, {"from_date": start, "to_date": end}, as_dict=True): + e = { + "name": d.name, + "doctype": "Attendance", + "start": d.attendance_date, + "end": d.attendance_date, + "title": cstr(d.status), + "docstatus": d.docstatus, + } + if e not in events: + events.append(e) + + +def mark_attendance( + employee, + attendance_date, + status, + shift=None, + leave_type=None, + ignore_validate=False, + late_entry=False, + early_exit=False, +): + if get_duplicate_attendance_record(employee, attendance_date, shift): + return + + if get_overlapping_shift_attendance(employee, attendance_date, shift): + return + + company = frappe.db.get_value("Employee", employee, "company") + attendance = frappe.get_doc( + { + "doctype": "Attendance", + "employee": employee, + "attendance_date": attendance_date, + "status": status, + "company": company, + "shift": shift, + "leave_type": leave_type, + "late_entry": late_entry, + "early_exit": early_exit, + } + ) + attendance.flags.ignore_validate = ignore_validate + attendance.insert() + attendance.submit() + return attendance.name + + +@frappe.whitelist() +def mark_bulk_attendance(data): + import json + + if isinstance(data, str): + data = json.loads(data) + data = frappe._dict(data) + company = frappe.get_value("Employee", data.employee, "company") + if not data.unmarked_days: + frappe.throw(_("Please select a date.")) + return + + for date in data.unmarked_days: + doc_dict = { + "doctype": "Attendance", + "employee": data.employee, + "attendance_date": get_datetime(date), + "status": data.status, + "company": company, + } + attendance = frappe.get_doc(doc_dict).insert() + attendance.submit() + + +def get_month_map(): + return frappe._dict( + { + "January": 1, + "February": 2, + "March": 3, + "April": 4, + "May": 5, + "June": 6, + "July": 7, + "August": 8, + "September": 9, + "October": 10, + "November": 11, + "December": 12, + } + ) + + +@frappe.whitelist() +def get_unmarked_days(employee, month, exclude_holidays=0): + import calendar + + month_map = get_month_map() + today = get_datetime() + + joining_date, relieving_date = frappe.get_cached_value( + "Employee", employee, ["date_of_joining", "relieving_date"] + ) + start_day = 1 + end_day = calendar.monthrange(today.year, month_map[month])[1] + 1 + + if joining_date and joining_date.month == month_map[month]: + start_day = joining_date.day + + if relieving_date and relieving_date.month == month_map[month]: + end_day = relieving_date.day + 1 + + dates_of_month = [ + "{}-{}-{}".format(today.year, month_map[month], r) for r in range(start_day, end_day) + ] + month_start, month_end = dates_of_month[0], dates_of_month[-1] + + records = frappe.get_all( + "Attendance", + fields=["attendance_date", "employee"], + filters=[ + ["attendance_date", ">=", month_start], + ["attendance_date", "<=", month_end], + ["employee", "=", employee], + ["docstatus", "!=", 2], + ], + ) + + marked_days = [get_datetime(record.attendance_date) for record in records] + if cint(exclude_holidays): + holiday_dates = get_holiday_dates_for_employee(employee, month_start, month_end) + holidays = [get_datetime(record) for record in holiday_dates] + marked_days.extend(holidays) + + unmarked_days = [] + + for date in dates_of_month: + date_time = get_datetime(date) + if today.day <= date_time.day and today.month <= date_time.month: + break + if date_time not in marked_days: + unmarked_days.append(date) + + return unmarked_days diff --git a/hrms/hr/doctype/attendance/attendance_calendar.js b/hrms/hr/doctype/attendance/attendance_calendar.js new file mode 100644 index 0000000..6341c87 --- /dev/null +++ b/hrms/hr/doctype/attendance/attendance_calendar.js @@ -0,0 +1,12 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +frappe.views.calendar["Attendance"] = { + options: { + header: { + left: 'prev,next today', + center: 'title', + right: 'month' + } + }, + get_events_method: "hrms.hr.doctype.attendance.attendance.get_events" +}; diff --git a/hrms/hr/doctype/attendance/attendance_dashboard.py b/hrms/hr/doctype/attendance/attendance_dashboard.py new file mode 100644 index 0000000..abed78f --- /dev/null +++ b/hrms/hr/doctype/attendance/attendance_dashboard.py @@ -0,0 +1,2 @@ +def get_data(): + return {"fieldname": "attendance", "transactions": [{"label": "", "items": ["Employee Checkin"]}]} diff --git a/hrms/hr/doctype/attendance/attendance_list.js b/hrms/hr/doctype/attendance/attendance_list.js new file mode 100644 index 0000000..a869ce8 --- /dev/null +++ b/hrms/hr/doctype/attendance/attendance_list.js @@ -0,0 +1,164 @@ +frappe.listview_settings['Attendance'] = { + add_fields: ["status", "attendance_date"], + get_indicator: function (doc) { + if (["Present", "Work From Home"].includes(doc.status)) { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (["Absent", "On Leave"].includes(doc.status)) { + return [__(doc.status), "red", "status,=," + doc.status]; + } else if (doc.status == "Half Day") { + return [__(doc.status), "orange", "status,=," + doc.status]; + } + }, + + onload: function(list_view) { + let me = this; + const months = moment.months(); + list_view.page.add_inner_button(__("Mark Attendance"), function() { + let dialog = new frappe.ui.Dialog({ + title: __("Mark Attendance"), + fields: [{ + fieldname: 'employee', + label: __('For Employee'), + fieldtype: 'Link', + options: 'Employee', + get_query: () => { + return {query: "erpnext.controllers.queries.employee_query"}; + }, + reqd: 1, + onchange: function() { + dialog.set_df_property("unmarked_days", "hidden", 1); + dialog.set_df_property("status", "hidden", 1); + dialog.set_df_property("exclude_holidays", "hidden", 1); + dialog.set_df_property("month", "value", ''); + dialog.set_df_property("unmarked_days", "options", []); + dialog.no_unmarked_days_left = false; + } + }, + { + label: __("For Month"), + fieldtype: "Select", + fieldname: "month", + options: months, + reqd: 1, + onchange: function() { + if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) { + dialog.set_df_property("status", "hidden", 0); + dialog.set_df_property("exclude_holidays", "hidden", 0); + dialog.set_df_property("unmarked_days", "options", []); + dialog.no_unmarked_days_left = false; + me.get_multi_select_options( + dialog.fields_dict.employee.value, + dialog.fields_dict.month.value, + dialog.fields_dict.exclude_holidays.get_value() + ).then(options => { + if (options.length > 0) { + dialog.set_df_property("unmarked_days", "hidden", 0); + dialog.set_df_property("unmarked_days", "options", options); + } else { + dialog.no_unmarked_days_left = true; + } + }); + } + } + }, + { + label: __("Status"), + fieldtype: "Select", + fieldname: "status", + options: ["Present", "Absent", "Half Day", "Work From Home"], + hidden: 1, + reqd: 1, + + }, + { + label: __("Exclude Holidays"), + fieldtype: "Check", + fieldname: "exclude_holidays", + hidden: 1, + onchange: function() { + if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) { + dialog.set_df_property("status", "hidden", 0); + dialog.set_df_property("unmarked_days", "options", []); + dialog.no_unmarked_days_left = false; + me.get_multi_select_options( + dialog.fields_dict.employee.value, + dialog.fields_dict.month.value, + dialog.fields_dict.exclude_holidays.get_value() + ).then(options => { + if (options.length > 0) { + dialog.set_df_property("unmarked_days", "hidden", 0); + dialog.set_df_property("unmarked_days", "options", options); + } else { + dialog.no_unmarked_days_left = true; + } + }); + } + } + }, + { + label: __("Unmarked Attendance for days"), + fieldname: "unmarked_days", + fieldtype: "MultiCheck", + options: [], + columns: 2, + hidden: 1 + }], + primary_action(data) { + if (cur_dialog.no_unmarked_days_left) { + frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}", + [dialog.fields_dict.month.value, dialog.fields_dict.employee.value])); + } else { + frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status, data.month]), () => { + frappe.call({ + method: "hrms.hr.doctype.attendance.attendance.mark_bulk_attendance", + args: { + data: data + }, + callback: function (r) { + if (r.message === 1) { + frappe.show_alert({ + message: __("Attendance Marked"), + indicator: 'blue' + }); + cur_dialog.hide(); + } + } + }); + }); + } + dialog.hide(); + list_view.refresh(); + }, + primary_action_label: __('Mark Attendance') + + }); + dialog.show(); + }); + }, + + get_multi_select_options: function(employee, month, exclude_holidays) { + return new Promise(resolve => { + frappe.call({ + method: 'hrms.hr.doctype.attendance.attendance.get_unmarked_days', + async: false, + args: { + employee: employee, + month: month, + exclude_holidays: exclude_holidays + } + }).then(r => { + var options = []; + for (var d in r.message) { + var momentObj = moment(r.message[d], 'YYYY-MM-DD'); + var date = momentObj.format('DD-MM-YYYY'); + options.push({ + "label": date, + "value": r.message[d], + "checked": 1 + }); + } + resolve(options); + }); + }); + } +}; diff --git a/hrms/hr/doctype/attendance/test_attendance.py b/hrms/hr/doctype/attendance/test_attendance.py new file mode 100644 index 0000000..32cb9ab --- /dev/null +++ b/hrms/hr/doctype/attendance/test_attendance.py @@ -0,0 +1,233 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import ( + add_days, + add_months, + get_last_day, + get_year_ending, + get_year_start, + getdate, + nowdate, +) + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.attendance.attendance import ( + DuplicateAttendanceError, + OverlappingShiftAttendanceError, + get_month_map, + get_unmarked_days, + mark_attendance, +) +from hrms.tests.test_utils import get_first_sunday + +test_records = frappe.get_test_records("Attendance") + + +class TestAttendance(FrappeTestCase): + def setUp(self): + from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + from_date = get_year_start(getdate()) + to_date = get_year_ending(getdate()) + self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) + frappe.db.delete("Attendance") + + def test_duplicate_attendance(self): + employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company") + date = nowdate() + + mark_attendance(employee, date, "Present") + attendance = frappe.get_doc( + { + "doctype": "Attendance", + "employee": employee, + "attendance_date": date, + "status": "Absent", + "company": "_Test Company", + } + ) + + self.assertRaises(DuplicateAttendanceError, attendance.insert) + + def test_duplicate_attendance_with_shift(self): + from hrms.hr.doctype.shift_type.test_shift_type import setup_shift_type + + employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company") + date = nowdate() + + shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") + mark_attendance(employee, date, "Present", shift=shift_1.name) + + # attendance record with shift + attendance = frappe.get_doc( + { + "doctype": "Attendance", + "employee": employee, + "attendance_date": date, + "status": "Absent", + "company": "_Test Company", + "shift": shift_1.name, + } + ) + + self.assertRaises(DuplicateAttendanceError, attendance.insert) + + # attendance record without any shift + attendance = frappe.get_doc( + { + "doctype": "Attendance", + "employee": employee, + "attendance_date": date, + "status": "Absent", + "company": "_Test Company", + } + ) + + self.assertRaises(DuplicateAttendanceError, attendance.insert) + + def test_overlapping_shift_attendance_validation(self): + from hrms.hr.doctype.shift_type.test_shift_type import setup_shift_type + + employee = make_employee("test_overlap_attendance@example.com", company="_Test Company") + date = nowdate() + + shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") + shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00") + + mark_attendance(employee, date, "Present", shift=shift_1.name) + + # attendance record with overlapping shift + attendance = frappe.get_doc( + { + "doctype": "Attendance", + "employee": employee, + "attendance_date": date, + "status": "Absent", + "company": "_Test Company", + "shift": shift_2.name, + } + ) + + self.assertRaises(OverlappingShiftAttendanceError, attendance.insert) + + def test_allow_attendance_with_different_shifts(self): + # allows attendance with 2 different non-overlapping shifts + from hrms.hr.doctype.shift_type.test_shift_type import setup_shift_type + + employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company") + date = nowdate() + + shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") + shift_2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="12:00:00") + + mark_attendance(employee, date, "Present", shift_1.name) + frappe.get_doc( + { + "doctype": "Attendance", + "employee": employee, + "attendance_date": date, + "status": "Absent", + "company": "_Test Company", + "shift": shift_2.name, + } + ).insert() + + def test_mark_absent(self): + employee = make_employee("test_mark_absent@example.com") + date = nowdate() + + attendance = mark_attendance(employee, date, "Absent") + fetch_attendance = frappe.get_value( + "Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"} + ) + self.assertEqual(attendance, fetch_attendance) + + def test_unmarked_days(self): + first_sunday = get_first_sunday( + self.holiday_list, for_date=get_last_day(add_months(getdate(), -1)) + ) + attendance_date = add_days(first_sunday, 1) + + employee = make_employee( + "test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1) + ) + frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list) + + mark_attendance(employee, attendance_date, "Present") + month_name = get_month_name(attendance_date) + + unmarked_days = get_unmarked_days(employee, month_name) + unmarked_days = [getdate(date) for date in unmarked_days] + + # attendance already marked for the day + self.assertNotIn(attendance_date, unmarked_days) + # attendance unmarked + self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days) + # holiday considered in unmarked days + self.assertIn(first_sunday, unmarked_days) + + def test_unmarked_days_excluding_holidays(self): + first_sunday = get_first_sunday( + self.holiday_list, for_date=get_last_day(add_months(getdate(), -1)) + ) + attendance_date = add_days(first_sunday, 1) + + employee = make_employee( + "test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1) + ) + frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list) + + mark_attendance(employee, attendance_date, "Present") + month_name = get_month_name(attendance_date) + + unmarked_days = get_unmarked_days(employee, month_name, exclude_holidays=True) + unmarked_days = [getdate(date) for date in unmarked_days] + + # attendance already marked for the day + self.assertNotIn(attendance_date, unmarked_days) + # attendance unmarked + self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days) + # holidays not considered in unmarked days + self.assertNotIn(first_sunday, unmarked_days) + + def test_unmarked_days_as_per_joining_and_relieving_dates(self): + first_sunday = get_first_sunday( + self.holiday_list, for_date=get_last_day(add_months(getdate(), -1)) + ) + date = add_days(first_sunday, 1) + + doj = add_days(date, 1) + relieving_date = add_days(date, 5) + employee = make_employee( + "test_unmarked_days_as_per_doj@example.com", date_of_joining=doj, relieving_date=relieving_date + ) + + frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list) + + attendance_date = add_days(date, 2) + mark_attendance(employee, attendance_date, "Present") + month_name = get_month_name(attendance_date) + + unmarked_days = get_unmarked_days(employee, month_name) + unmarked_days = [getdate(date) for date in unmarked_days] + + # attendance already marked for the day + self.assertNotIn(attendance_date, unmarked_days) + # date before doj not in unmarked days + self.assertNotIn(add_days(doj, -1), unmarked_days) + # date after relieving not in unmarked days + self.assertNotIn(add_days(relieving_date, 1), unmarked_days) + + def tearDown(self): + frappe.db.rollback() + + +def get_month_name(date): + month_number = date.month + for month, number in get_month_map().items(): + if number == month_number: + return month diff --git a/hrms/hr/doctype/attendance/test_records.json b/hrms/hr/doctype/attendance/test_records.json new file mode 100644 index 0000000..096f95c --- /dev/null +++ b/hrms/hr/doctype/attendance/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doctype": "Attendance", + "name": "_Test Attendance 1", + "employee": "_T-Employee-00001", + "status": "Present", + "attendance_date": "2014-02-01", + "company": "_Test Company" + } +] diff --git a/hrms/hr/doctype/attendance_request/__init__.py b/hrms/hr/doctype/attendance_request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/attendance_request/attendance_request.js b/hrms/hr/doctype/attendance_request/attendance_request.js new file mode 100644 index 0000000..2d25d14 --- /dev/null +++ b/hrms/hr/doctype/attendance_request/attendance_request.js @@ -0,0 +1,14 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +cur_frm.add_fetch('employee', 'company', 'company'); + +frappe.ui.form.on('Attendance Request', { + half_day: function(frm) { + if(frm.doc.half_day == 1){ + frm.set_df_property('half_day_date', 'reqd', true); + } + else{ + frm.set_df_property('half_day_date', 'reqd', false); + } + } +}); diff --git a/hrms/hr/doctype/attendance_request/attendance_request.json b/hrms/hr/doctype/attendance_request/attendance_request.json new file mode 100644 index 0000000..837a878 --- /dev/null +++ b/hrms/hr/doctype/attendance_request/attendance_request.json @@ -0,0 +1,190 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-ARQ-.YY.-.MM.-.#####", + "creation": "2018-04-13 15:37:40.918990", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_5", + "company", + "from_date", + "to_date", + "half_day", + "half_day_date", + "reason_section", + "reason", + "column_break_4", + "explanation", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "To Date", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "half_day", + "fieldtype": "Check", + "label": "Half Day" + }, + { + "depends_on": "half_day", + "fieldname": "half_day_date", + "fieldtype": "Date", + "label": "Half Day Date" + }, + { + "fieldname": "reason_section", + "fieldtype": "Section Break", + "label": "Reason" + }, + { + "fieldname": "reason", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Reason", + "options": "Work From Home\nOn Duty", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "explanation", + "fieldtype": "Small Text", + "label": "Explanation" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Attendance Request", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-16 11:49:26.943173", + "modified_by": "Administrator", + "module": "HR", + "name": "Attendance Request", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/attendance_request/attendance_request.py b/hrms/hr/doctype/attendance_request/attendance_request.py new file mode 100644 index 0000000..d0b3f41 --- /dev/null +++ b/hrms/hr/doctype/attendance_request/attendance_request.py @@ -0,0 +1,79 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import add_days, date_diff, getdate + +from erpnext.setup.doctype.employee.employee import is_holiday + +from hrms.hr.utils import validate_active_employee, validate_dates + + +class AttendanceRequest(Document): + def validate(self): + validate_active_employee(self.employee) + validate_dates(self, self.from_date, self.to_date) + if self.half_day: + if not getdate(self.from_date) <= getdate(self.half_day_date) <= getdate(self.to_date): + frappe.throw(_("Half day date should be in between from date and to date")) + + def on_submit(self): + self.create_attendance() + + def on_cancel(self): + attendance_list = frappe.get_list( + "Attendance", {"employee": self.employee, "attendance_request": self.name} + ) + if attendance_list: + for attendance in attendance_list: + attendance_obj = frappe.get_doc("Attendance", attendance["name"]) + attendance_obj.cancel() + + def create_attendance(self): + request_days = date_diff(self.to_date, self.from_date) + 1 + for number in range(request_days): + attendance_date = add_days(self.from_date, number) + skip_attendance = self.validate_if_attendance_not_applicable(attendance_date) + if not skip_attendance: + attendance = frappe.new_doc("Attendance") + attendance.employee = self.employee + attendance.employee_name = self.employee_name + if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0: + attendance.status = "Half Day" + elif self.reason == "Work From Home": + attendance.status = "Work From Home" + else: + attendance.status = "Present" + attendance.attendance_date = attendance_date + attendance.company = self.company + attendance.attendance_request = self.name + attendance.save(ignore_permissions=True) + attendance.submit() + + def validate_if_attendance_not_applicable(self, attendance_date): + # Check if attendance_date is a Holiday + if is_holiday(self.employee, attendance_date): + frappe.msgprint( + _("Attendance not submitted for {0} as it is a Holiday.").format(attendance_date), alert=1 + ) + return True + + # Check if employee on Leave + leave_record = frappe.db.sql( + """select half_day from `tabLeave Application` + where employee = %s and %s between from_date and to_date + and docstatus = 1""", + (self.employee, attendance_date), + as_dict=True, + ) + if leave_record: + frappe.msgprint( + _("Attendance not submitted for {0} as {1} on leave.").format(attendance_date, self.employee), + alert=1, + ) + return True + + return False diff --git a/hrms/hr/doctype/attendance_request/attendance_request_dashboard.py b/hrms/hr/doctype/attendance_request/attendance_request_dashboard.py new file mode 100644 index 0000000..059725c --- /dev/null +++ b/hrms/hr/doctype/attendance_request/attendance_request_dashboard.py @@ -0,0 +1,2 @@ +def get_data(): + return {"fieldname": "attendance_request", "transactions": [{"items": ["Attendance"]}]} diff --git a/hrms/hr/doctype/attendance_request/test_attendance_request.py b/hrms/hr/doctype/attendance_request/test_attendance_request.py new file mode 100644 index 0000000..ee436f5 --- /dev/null +++ b/hrms/hr/doctype/attendance_request/test_attendance_request.py @@ -0,0 +1,100 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest +from datetime import date + +import frappe +from frappe.utils import nowdate + +test_dependencies = ["Employee"] + + +class TestAttendanceRequest(unittest.TestCase): + def setUp(self): + for doctype in ["Attendance Request", "Attendance"]: + frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) + + def tearDown(self): + frappe.db.rollback() + + def test_on_duty_attendance_request(self): + "Test creation/updation of Attendace from Attendance Request, on duty." + today = nowdate() + employee = get_employee() + attendance_request = frappe.new_doc("Attendance Request") + attendance_request.employee = employee.name + attendance_request.from_date = date(date.today().year, 1, 1) + attendance_request.to_date = date(date.today().year, 1, 2) + attendance_request.reason = "On Duty" + attendance_request.company = "_Test Company" + attendance_request.insert() + attendance_request.submit() + + attendance = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1), + }, + fieldname=["status", "docstatus"], + as_dict=True, + ) + self.assertEqual(attendance.status, "Present") + self.assertEqual(attendance.docstatus, 1) + + # cancelling attendance request cancels linked attendances + attendance_request.cancel() + + # cancellation alters docname + # fetch attendance value again to avoid stale docname + attendance_docstatus = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1), + }, + fieldname="docstatus", + ) + self.assertEqual(attendance_docstatus, 2) + + def test_work_from_home_attendance_request(self): + "Test creation/updation of Attendace from Attendance Request, work from home." + today = nowdate() + employee = get_employee() + attendance_request = frappe.new_doc("Attendance Request") + attendance_request.employee = employee.name + attendance_request.from_date = date(date.today().year, 1, 1) + attendance_request.to_date = date(date.today().year, 1, 2) + attendance_request.reason = "Work From Home" + attendance_request.company = "_Test Company" + attendance_request.insert() + attendance_request.submit() + + attendance_status = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1), + }, + fieldname="status", + ) + self.assertEqual(attendance_status, "Work From Home") + + attendance_request.cancel() + + # cancellation alters docname + # fetch attendance value again to avoid stale docname + attendance_docstatus = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1), + }, + fieldname="docstatus", + ) + self.assertEqual(attendance_docstatus, 2) + + +def get_employee(): + return frappe.get_doc("Employee", "_T-Employee-00001") diff --git a/hrms/hr/doctype/compensatory_leave_request/__init__.py b/hrms/hr/doctype/compensatory_leave_request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.js b/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.js new file mode 100644 index 0000000..1baa1e0 --- /dev/null +++ b/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.js @@ -0,0 +1,22 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Compensatory Leave Request', { + refresh: function(frm) { + frm.set_query("leave_type", function() { + return { + filters: { + "is_compensatory": true + } + }; + }); + }, + half_day: function(frm) { + if(frm.doc.half_day == 1){ + frm.set_df_property('half_day_date', 'reqd', true); + } + else{ + frm.set_df_property('half_day_date', 'reqd', false); + } + } +}); diff --git a/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.json b/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.json new file mode 100644 index 0000000..3c6374f --- /dev/null +++ b/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.json @@ -0,0 +1,575 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "HR-CMP-.YY.-.MM.-.#####", + "beta": 0, + "creation": "2018-04-13 14:51:39.326768", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee Name", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Leave Type", + "length": 0, + "no_copy": 0, + "options": "Leave Type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_allocation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Leave Allocation", + "length": 0, + "no_copy": 0, + "options": "Leave Allocation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "worked_on", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Worked On Holiday", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "work_from_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Work From Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "work_end_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Work End Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "half_day", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Half Day", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "half_day", + "fieldname": "half_day_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Half Day Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reason", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Reason", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Compensatory Leave Request", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-08-21 16:15:36.266924", + "modified_by": "Administrator", + "module": "HR", + "name": "Compensatory Leave Request", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.py new file mode 100644 index 0000000..689cecc --- /dev/null +++ b/hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.py @@ -0,0 +1,155 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import add_days, cint, date_diff, format_date, getdate + +from hrms.hr.utils import ( + create_additional_leave_ledger_entry, + get_holiday_dates_for_employee, + get_leave_period, + validate_active_employee, + validate_dates, + validate_overlap, +) + + +class CompensatoryLeaveRequest(Document): + def validate(self): + validate_active_employee(self.employee) + validate_dates(self, self.work_from_date, self.work_end_date) + if self.half_day: + if not self.half_day_date: + frappe.throw(_("Half Day Date is mandatory")) + if ( + not getdate(self.work_from_date) <= getdate(self.half_day_date) <= getdate(self.work_end_date) + ): + frappe.throw(_("Half Day Date should be in between Work From Date and Work End Date")) + validate_overlap(self, self.work_from_date, self.work_end_date) + self.validate_holidays() + self.validate_attendance() + if not self.leave_type: + frappe.throw(_("Leave Type is madatory")) + + def validate_attendance(self): + attendance = frappe.get_all( + "Attendance", + filters={ + "attendance_date": ["between", (self.work_from_date, self.work_end_date)], + "status": "Present", + "docstatus": 1, + "employee": self.employee, + }, + fields=["attendance_date", "status"], + ) + + if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1: + frappe.throw(_("You are not present all day(s) between compensatory leave request days")) + + def validate_holidays(self): + holidays = get_holiday_dates_for_employee(self.employee, self.work_from_date, self.work_end_date) + if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1: + if date_diff(self.work_end_date, self.work_from_date): + msg = _("The days between {0} to {1} are not valid holidays.").format( + frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date)) + ) + else: + msg = _("{0} is not a holiday.").format(frappe.bold(format_date(self.work_from_date))) + + frappe.throw(msg) + + def on_submit(self): + company = frappe.db.get_value("Employee", self.employee, "company") + date_difference = date_diff(self.work_end_date, self.work_from_date) + 1 + if self.half_day: + date_difference -= 0.5 + leave_period = get_leave_period(self.work_from_date, self.work_end_date, company) + if leave_period: + leave_allocation = self.get_existing_allocation_for_period(leave_period) + if leave_allocation: + leave_allocation.new_leaves_allocated += date_difference + leave_allocation.validate() + leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated) + leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated) + + # generate additional ledger entry for the new compensatory leaves off + create_additional_leave_ledger_entry( + leave_allocation, date_difference, add_days(self.work_end_date, 1) + ) + + else: + leave_allocation = self.create_leave_allocation(leave_period, date_difference) + self.db_set("leave_allocation", leave_allocation.name) + else: + frappe.throw( + _("There is no leave period in between {0} and {1}").format( + format_date(self.work_from_date), format_date(self.work_end_date) + ) + ) + + def on_cancel(self): + if self.leave_allocation: + date_difference = date_diff(self.work_end_date, self.work_from_date) + 1 + if self.half_day: + date_difference -= 0.5 + leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation) + if leave_allocation: + leave_allocation.new_leaves_allocated -= date_difference + if leave_allocation.new_leaves_allocated - date_difference <= 0: + leave_allocation.new_leaves_allocated = 0 + leave_allocation.validate() + leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated) + leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated) + + # create reverse entry on cancelation + create_additional_leave_ledger_entry( + leave_allocation, date_difference * -1, add_days(self.work_end_date, 1) + ) + + def get_existing_allocation_for_period(self, leave_period): + leave_allocation = frappe.db.sql( + """ + select name + from `tabLeave Allocation` + where employee=%(employee)s and leave_type=%(leave_type)s + and docstatus=1 + and (from_date between %(from_date)s and %(to_date)s + or to_date between %(from_date)s and %(to_date)s + or (from_date < %(from_date)s and to_date > %(to_date)s)) + """, + { + "from_date": leave_period[0].from_date, + "to_date": leave_period[0].to_date, + "employee": self.employee, + "leave_type": self.leave_type, + }, + as_dict=1, + ) + + if leave_allocation: + return frappe.get_doc("Leave Allocation", leave_allocation[0].name) + else: + return False + + def create_leave_allocation(self, leave_period, date_difference): + is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward") + allocation = frappe.get_doc( + dict( + doctype="Leave Allocation", + employee=self.employee, + employee_name=self.employee_name, + leave_type=self.leave_type, + from_date=add_days(self.work_end_date, 1), + to_date=leave_period[0].to_date, + carry_forward=cint(is_carry_forward), + new_leaves_allocated=date_difference, + total_leaves_allocated=date_difference, + description=self.reason, + ) + ) + allocation.insert(ignore_permissions=True) + allocation.submit() + return allocation diff --git a/hrms/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/hrms/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py new file mode 100644 index 0000000..2cdc77a --- /dev/null +++ b/hrms/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py @@ -0,0 +1,157 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, add_months, today + +from hrms.hr.doctype.attendance_request.test_attendance_request import get_employee +from hrms.hr.doctype.leave_application.leave_application import get_leave_balance_on +from hrms.hr.doctype.leave_period.test_leave_period import create_leave_period + +test_dependencies = ["Employee"] + + +class TestCompensatoryLeaveRequest(unittest.TestCase): + def setUp(self): + frappe.db.sql(""" delete from `tabCompensatory Leave Request`""") + frappe.db.sql(""" delete from `tabLeave Ledger Entry`""") + frappe.db.sql(""" delete from `tabLeave Allocation`""") + frappe.db.sql( + """ delete from `tabAttendance` where attendance_date in {0} """.format( + (today(), add_days(today(), -1)) + ) + ) # nosec + create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company") + create_holiday_list() + + employee = get_employee() + employee.holiday_list = "_Test Compensatory Leave" + employee.save() + + def test_leave_balance_on_submit(self): + """check creation of leave allocation on submission of compensatory leave request""" + employee = get_employee() + mark_attendance(employee) + compensatory_leave_request = get_compensatory_leave_request(employee.name) + + before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today()) + compensatory_leave_request.submit() + + self.assertEqual( + get_leave_balance_on( + employee.name, compensatory_leave_request.leave_type, add_days(today(), 1) + ), + before + 1, + ) + + def test_leave_allocation_update_on_submit(self): + employee = get_employee() + mark_attendance(employee, date=add_days(today(), -1)) + compensatory_leave_request = get_compensatory_leave_request( + employee.name, leave_date=add_days(today(), -1) + ) + compensatory_leave_request.submit() + + # leave allocation creation on submit + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"name": compensatory_leave_request.leave_allocation}, + ["total_leaves_allocated"], + ) + self.assertEqual(leaves_allocated, 1) + + mark_attendance(employee) + compensatory_leave_request = get_compensatory_leave_request(employee.name) + compensatory_leave_request.submit() + + # leave allocation updates on submission of second compensatory leave request + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"name": compensatory_leave_request.leave_allocation}, + ["total_leaves_allocated"], + ) + self.assertEqual(leaves_allocated, 2) + + def test_creation_of_leave_ledger_entry_on_submit(self): + """check creation of leave ledger entry on submission of leave request""" + employee = get_employee() + mark_attendance(employee) + compensatory_leave_request = get_compensatory_leave_request(employee.name) + compensatory_leave_request.submit() + + filters = dict(transaction_name=compensatory_leave_request.leave_allocation) + leave_ledger_entry = frappe.get_all("Leave Ledger Entry", fields="*", filters=filters) + + self.assertEqual(len(leave_ledger_entry), 1) + self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, 1) + + # check reverse leave ledger entry on cancellation + compensatory_leave_request.cancel() + leave_ledger_entry = frappe.get_all( + "Leave Ledger Entry", fields="*", filters=filters, order_by="creation desc" + ) + + self.assertEqual(len(leave_ledger_entry), 2) + self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, -1) + + +def get_compensatory_leave_request(employee, leave_date=today()): + prev_comp_leave_req = frappe.db.get_value( + "Compensatory Leave Request", + dict( + leave_type="Compensatory Off", + work_from_date=leave_date, + work_end_date=leave_date, + employee=employee, + ), + "name", + ) + if prev_comp_leave_req: + return frappe.get_doc("Compensatory Leave Request", prev_comp_leave_req) + + return frappe.get_doc( + dict( + doctype="Compensatory Leave Request", + employee=employee, + leave_type="Compensatory Off", + work_from_date=leave_date, + work_end_date=leave_date, + reason="test", + ) + ).insert() + + +def mark_attendance(employee, date=today(), status="Present"): + if not frappe.db.exists( + dict(doctype="Attendance", employee=employee.name, attendance_date=date, status="Present") + ): + attendance = frappe.get_doc( + {"doctype": "Attendance", "employee": employee.name, "attendance_date": date, "status": status} + ) + attendance.save() + attendance.submit() + + +def create_holiday_list(): + if frappe.db.exists("Holiday List", "_Test Compensatory Leave"): + return + + holiday_list = frappe.get_doc( + { + "doctype": "Holiday List", + "from_date": add_months(today(), -3), + "to_date": add_months(today(), 3), + "holidays": [ + {"description": "Test Holiday", "holiday_date": today()}, + {"description": "Test Holiday 1", "holiday_date": add_days(today(), -1)}, + ], + "holiday_list_name": "_Test Compensatory Leave", + } + ) + holiday_list.save() diff --git a/hrms/hr/doctype/daily_work_summary/__init__.py b/hrms/hr/doctype/daily_work_summary/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/daily_work_summary/daily_work_summary.js b/hrms/hr/doctype/daily_work_summary/daily_work_summary.js new file mode 100644 index 0000000..8236480 --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary/daily_work_summary.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Daily Work Summary', { + refresh: function (frm) { + + } +}); diff --git a/hrms/hr/doctype/daily_work_summary/daily_work_summary.json b/hrms/hr/doctype/daily_work_summary/daily_work_summary.json new file mode 100644 index 0000000..259416e --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary/daily_work_summary.json @@ -0,0 +1,175 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-11-08 04:58:20.001780", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "daily_work_summary_group", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Daily Work Summary Group", + "length": 0, + "no_copy": 0, + "options": "Daily Work Summary Group", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Status", + "length": 0, + "no_copy": 0, + "options": "Open\nSent", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "email_sent_to", + "fieldtype": "Code", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Email Sent To", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 1, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-02-16 11:02:06.727749", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Employee", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 1, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/daily_work_summary/daily_work_summary.py b/hrms/hr/doctype/daily_work_summary/daily_work_summary.py new file mode 100644 index 0000000..babbbeb --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary/daily_work_summary.py @@ -0,0 +1,120 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from email_reply_parser import EmailReplyParser + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import global_date_format + + +class DailyWorkSummary(Document): + def send_mails(self, dws_group, emails): + """Send emails to get daily work summary to all users \ + in selected daily work summary group""" + incoming_email_account = frappe.db.get_value( + "Email Account", dict(enable_incoming=1, default_incoming=1), "email_id" + ) + + self.db_set("email_sent_to", "\n".join(emails)) + frappe.sendmail( + recipients=emails, + message=dws_group.message, + subject=dws_group.subject, + reference_doctype=self.doctype, + reference_name=self.name, + reply_to=incoming_email_account, + ) + + def send_summary(self): + """Send summary of all replies. Called at midnight""" + args = self.get_message_details() + emails = get_user_emails_from_group(self.daily_work_summary_group) + frappe.sendmail( + recipients=emails, + template="daily_work_summary", + args=args, + subject=_(self.daily_work_summary_group), + reference_doctype=self.doctype, + reference_name=self.name, + ) + + self.db_set("status", "Sent") + + def get_message_details(self): + """Return args for template""" + dws_group = frappe.get_doc("Daily Work Summary Group", self.daily_work_summary_group) + + replies = frappe.get_all( + "Communication", + fields=["content", "text_content", "sender"], + filters=dict( + reference_doctype=self.doctype, + reference_name=self.name, + communication_type="Communication", + sent_or_received="Received", + ), + order_by="creation asc", + ) + + did_not_reply = self.email_sent_to.split() + + for d in replies: + user = frappe.db.get_values( + "User", {"email": d.sender}, ["full_name", "user_image"], as_dict=True + ) + + d.sender_name = user[0].full_name if user else d.sender + d.image = user[0].image if user and user[0].image else None + + original_image = d.image + # make thumbnail image + try: + if original_image: + file_name = frappe.get_list("File", {"file_url": original_image}) + + if file_name: + file_name = file_name[0].name + file_doc = frappe.get_doc("File", file_name) + thumbnail_image = file_doc.make_thumbnail( + set_as_thumbnail=False, width=100, height=100, crop=True + ) + d.image = thumbnail_image + except Exception: + d.image = original_image + + if d.sender in did_not_reply: + did_not_reply.remove(d.sender) + if d.text_content: + d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content)) + + did_not_reply = [ + (frappe.db.get_value("User", {"email": email}, "full_name") or email) for email in did_not_reply + ] + + return dict( + replies=replies, + original_message=dws_group.message, + title=_("Work Summary for {0}").format(global_date_format(self.creation)), + did_not_reply=", ".join(did_not_reply) or "", + did_not_reply_title=_("No replies from"), + ) + + +def get_user_emails_from_group(group): + """Returns list of email of enabled users from the given group + + :param group: Daily Work Summary Group `name`""" + group_doc = group + if isinstance(group_doc, str): + group_doc = frappe.get_doc("Daily Work Summary Group", group) + + emails = get_users_email(group_doc) + + return emails + + +def get_users_email(doc): + return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")] diff --git a/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py b/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py new file mode 100644 index 0000000..dba51cc --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py @@ -0,0 +1,105 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import os +import unittest + +import frappe +import frappe.utils + +# test_records = frappe.get_test_records('Daily Work Summary') + + +class TestDailyWorkSummary(unittest.TestCase): + def test_email_trigger(self): + self.setup_and_prepare_test() + for d in self.users: + # check that email is sent to users + if d.message: + self.assertTrue( + d.email in [d.recipient for d in self.emails if self.groups.subject in d.message] + ) + + def test_email_trigger_failed(self): + hour = "00:00" + if frappe.utils.nowtime().split(":")[0] == "00": + hour = "01:00" + + self.setup_and_prepare_test(hour) + + for d in self.users: + # check that email is not sent to users + self.assertFalse( + d.email in [d.recipient for d in self.emails if self.groups.subject in d.message] + ) + + def test_incoming(self): + # get test mail with message-id as in-reply-to + self.setup_and_prepare_test() + with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f: + if not self.emails: + return + test_mails = [ + f.read() + .replace("{{ sender }}", self.users[-1].email) + .replace("{{ message_id }}", self.emails[-1].message_id) + ] + + # pull the mail + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + email_account.receive(test_mails=test_mails) + + daily_work_summary = frappe.get_doc( + "Daily Work Summary", frappe.get_all("Daily Work Summary")[0].name + ) + + args = daily_work_summary.get_message_details() + + self.assertTrue("I built Daily Work Summary!" in args.get("replies")[0].content) + + def setup_and_prepare_test(self, hour=None): + frappe.db.sql("delete from `tabDaily Work Summary`") + frappe.db.sql("delete from `tabEmail Queue`") + frappe.db.sql("delete from `tabEmail Queue Recipient`") + frappe.db.sql("delete from `tabCommunication`") + frappe.db.sql("delete from `tabDaily Work Summary Group`") + + self.users = frappe.get_all( + "User", fields=["email"], filters=dict(email=("!=", "test@example.com")) + ) + self.setup_groups(hour) + + from hrms.hr.doctype.daily_work_summary_group.daily_work_summary_group import trigger_emails + + trigger_emails() + + # check if emails are created + + self.emails = frappe.db.sql( + """select r.recipient, q.message, q.message_id \ + from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \ + where q.name = r.parent""", + as_dict=1, + ) + + def setup_groups(self, hour=None): + # setup email to trigger at this hour + if not hour: + hour = frappe.utils.nowtime().split(":")[0] + hour = hour + ":00" + + groups = frappe.get_doc( + dict( + doctype="Daily Work Summary Group", + name="Daily Work Summary", + users=self.users, + send_emails_at=hour, + subject="this is a subject for testing summary emails", + message="this is a message for testing summary emails", + ) + ) + groups.insert() + + self.groups = groups + self.groups.save() diff --git a/hrms/hr/doctype/daily_work_summary/test_data/test-reply.raw b/hrms/hr/doctype/daily_work_summary/test_data/test-reply.raw new file mode 100644 index 0000000..ba01bc2 --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary/test_data/test-reply.raw @@ -0,0 +1,75 @@ +From: {{ sender }} +Content-Type: multipart/alternative; + boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361" +Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com> +Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) +X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F +Subject: Re: What did you work on today? +Date: Thu, 10 Nov 2016 16:04:43 +0530 +X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2 +References: <{{ message_id }}> +To: test_in@iwebnotes.com +In-Reply-To: <{{ message_id }}> + + +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; + charset=us-ascii + +I built Daily Work Summary! + +> On 10-Nov-2016, at 3:20 PM, Frappe wrote: +>=20 +> Please share what did you do today. If you reply by midnight, your = +response will be recorded! +>=20 +> This email was sent to rmehta@gmail.com +> Unsubscribe from this list = + +> Sent via ERPNext + + +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +I built Daily Work Summary!



+ + + + +What did you work on today? + +
+ +

Please share what did you do today. If you reply by midnight, your response will be recorded!

+ +
+ + + + + + +
+

+--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361-- diff --git a/hrms/hr/doctype/daily_work_summary_group/__init__.py b/hrms/hr/doctype/daily_work_summary_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.js b/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.js new file mode 100644 index 0000000..43206d5 --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.js @@ -0,0 +1,12 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Daily Work Summary Group', { + refresh: function (frm) { + if (!frm.is_new()) { + frm.add_custom_button(__('Daily Work Summary'), function () { + frappe.set_route('List', 'Daily Work Summary'); + }); + } + } +}); diff --git a/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.json b/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.json new file mode 100644 index 0000000..562183f --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.json @@ -0,0 +1,309 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "Prompt", + "beta": 0, + "creation": "2018-02-12 15:06:18.767239", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Enabled", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "select_users", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Select Users", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "users", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Users", + "length": 0, + "no_copy": 0, + "options": "Daily Work Summary Group User", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "send_emails_at", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Send Emails At", + "length": 0, + "no_copy": 0, + "options": "00:00\n01:00\n02:00\n03:00\n04:00\n05:00\n06:00\n07:00\n08:00\n09:00\n10:00\n11:00\n12:00\n13:00\n14:00\n15:00\n16:00\n17:00\n18:00\n19:00\n20:00\n21:00\n22:00\n23:00", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "holiday_list", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Holiday List", + "length": 0, + "no_copy": 0, + "options": "Holiday List", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mail_details", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Reminder", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "What did you work on today?", + "fieldname": "subject", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Subject", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "

Please share what did you do today. If you reply by midnight, your response will be recorded!

", + "fieldname": "message", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Message", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-02-16 10:56:03.998495", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary Group", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.py b/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.py new file mode 100644 index 0000000..3cef68d --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary_group/daily_work_summary_group.py @@ -0,0 +1,56 @@ +# # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# # For license information, please see license.txt + + +import frappe +import frappe.utils +from frappe import _ +from frappe.model.document import Document + +from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday + +from hrms.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group + + +class DailyWorkSummaryGroup(Document): + def validate(self): + if self.users: + if not frappe.flags.in_test and not is_incoming_account_enabled(): + frappe.throw( + _("Please enable default incoming account before creating Daily Work Summary Group") + ) + + +def trigger_emails(): + """Send emails to Employees at the given hour asking + them what did they work on today""" + groups = frappe.get_all("Daily Work Summary Group") + for d in groups: + group_doc = frappe.get_doc("Daily Work Summary Group", d) + if ( + is_current_hour(group_doc.send_emails_at) + and not is_holiday(group_doc.holiday_list) + and group_doc.enabled + ): + emails = get_user_emails_from_group(group_doc) + # find emails relating to a company + if emails: + daily_work_summary = frappe.get_doc( + dict(doctype="Daily Work Summary", daily_work_summary_group=group_doc.name) + ).insert() + daily_work_summary.send_mails(group_doc, emails) + + +def is_current_hour(hour): + return frappe.utils.nowtime().split(":")[0] == hour.split(":")[0] + + +def send_summary(): + """Send summary to everyone""" + for d in frappe.get_all("Daily Work Summary", dict(status="Open")): + daily_work_summary = frappe.get_doc("Daily Work Summary", d.name) + daily_work_summary.send_summary() + + +def is_incoming_account_enabled(): + return frappe.db.get_value("Email Account", dict(enable_incoming=1, default_incoming=1)) diff --git a/hrms/hr/doctype/daily_work_summary_group_user/__init__.py b/hrms/hr/doctype/daily_work_summary_group_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json b/hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json new file mode 100644 index 0000000..b22c051 --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json @@ -0,0 +1,106 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-02-12 14:57:38.332692", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "user", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "User", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "user.email", + "fieldname": "email", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "email", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-05-16 22:43:00.481019", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary Group User", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py b/hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py new file mode 100644 index 0000000..6e0809a --- /dev/null +++ b/hrms/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class DailyWorkSummaryGroupUser(Document): + pass diff --git a/hrms/hr/doctype/department_approver/__init__.py b/hrms/hr/doctype/department_approver/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/department_approver/department_approver.json b/hrms/hr/doctype/department_approver/department_approver.json new file mode 100644 index 0000000..317ecb3 --- /dev/null +++ b/hrms/hr/doctype/department_approver/department_approver.json @@ -0,0 +1,76 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "beta": 0, + "creation": "2018-04-08 16:31:02.433252", + "custom": 0, + "description": "", + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "approver", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Approver", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0, + "width": "200" + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-04-17 11:05:46.294340", + "modified_by": "Administrator", + "module": "HR", + "name": "Department Approver", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/department_approver/department_approver.py b/hrms/hr/doctype/department_approver/department_approver.py new file mode 100644 index 0000000..87bdddd --- /dev/null +++ b/hrms/hr/doctype/department_approver/department_approver.py @@ -0,0 +1,93 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class DepartmentApprover(Document): + pass + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_approvers(doctype, txt, searchfield, start, page_len, filters): + + if not filters.get("employee"): + frappe.throw(_("Please select Employee first.")) + + approvers = [] + department_details = {} + department_list = [] + employee = frappe.get_value( + "Employee", + filters.get("employee"), + ["employee_name", "department", "leave_approver", "expense_approver", "shift_request_approver"], + as_dict=True, + ) + + employee_department = filters.get("department") or employee.department + if employee_department: + department_details = frappe.db.get_value( + "Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True + ) + if department_details: + department_list = frappe.db.sql( + """select name from `tabDepartment` where lft <= %s + and rgt >= %s + and disabled=0 + order by lft desc""", + (department_details.lft, department_details.rgt), + as_list=True, + ) + + if filters.get("doctype") == "Leave Application" and employee.leave_approver: + approvers.append( + frappe.db.get_value("User", employee.leave_approver, ["name", "first_name", "last_name"]) + ) + + if filters.get("doctype") == "Expense Claim" and employee.expense_approver: + approvers.append( + frappe.db.get_value("User", employee.expense_approver, ["name", "first_name", "last_name"]) + ) + + if filters.get("doctype") == "Shift Request" and employee.shift_request_approver: + approvers.append( + frappe.db.get_value( + "User", employee.shift_request_approver, ["name", "first_name", "last_name"] + ) + ) + + if filters.get("doctype") == "Leave Application": + parentfield = "leave_approvers" + field_name = "Leave Approver" + elif filters.get("doctype") == "Expense Claim": + parentfield = "expense_approvers" + field_name = "Expense Approver" + elif filters.get("doctype") == "Shift Request": + parentfield = "shift_request_approver" + field_name = "Shift Request Approver" + if department_list: + for d in department_list: + approvers += frappe.db.sql( + """select user.name, user.first_name, user.last_name from + tabUser user, `tabDepartment Approver` approver where + approver.parent = %s + and user.name like %s + and approver.parentfield = %s + and approver.approver=user.name""", + (d, "%" + txt + "%", parentfield), + as_list=True, + ) + + if len(approvers) == 0: + error_msg = _("Please set {0} for the Employee: {1}").format( + field_name, frappe.bold(employee.employee_name) + ) + if department_list: + error_msg += " " + _("or for Department: {0}").format(frappe.bold(employee_department)) + frappe.throw(error_msg, title=_(field_name + " Missing")) + + return set(tuple(approver) for approver in approvers) diff --git a/hrms/hr/doctype/designation_skill/__init__.py b/hrms/hr/doctype/designation_skill/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/designation_skill/designation_skill.json b/hrms/hr/doctype/designation_skill/designation_skill.json new file mode 100644 index 0000000..ee9061b --- /dev/null +++ b/hrms/hr/doctype/designation_skill/designation_skill.json @@ -0,0 +1,74 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-04-16 10:01:05.259881", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "skill", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Skill", + "length": 0, + "no_copy": 0, + "options": "Skill", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2022-06-28 13:42:10.760449", + "modified_by": "Administrator", + "module": "HR", + "name": "Designation Skill", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/designation_skill/designation_skill.py b/hrms/hr/doctype/designation_skill/designation_skill.py new file mode 100644 index 0000000..c35223b --- /dev/null +++ b/hrms/hr/doctype/designation_skill/designation_skill.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class DesignationSkill(Document): + pass diff --git a/hrms/hr/doctype/employee_advance/__init__.py b/hrms/hr/doctype/employee_advance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_advance/employee_advance.js b/hrms/hr/doctype/employee_advance/employee_advance.js new file mode 100644 index 0000000..0a88833 --- /dev/null +++ b/hrms/hr/doctype/employee_advance/employee_advance.js @@ -0,0 +1,225 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Advance', { + setup: function(frm) { + frm.add_fetch("employee", "company", "company"); + frm.add_fetch("company", "default_employee_advance_account", "advance_account"); + + frm.set_query("employee", function() { + return { + filters: { + "status": "Active" + } + }; + }); + + frm.set_query("advance_account", function() { + if (!frm.doc.employee) { + frappe.msgprint(__("Please select employee first")); + } + let company_currency = erpnext.get_currency(frm.doc.company); + let currencies = [company_currency]; + if (frm.doc.currency && (frm.doc.currency != company_currency)) { + currencies.push(frm.doc.currency); + } + + return { + filters: { + "root_type": "Asset", + "is_group": 0, + "company": frm.doc.company, + "account_currency": ["in", currencies], + } + }; + }); + + frm.set_query('salary_component', function() { + return { + filters: { + "type": "Deduction" + } + }; + }); + }, + + refresh: function(frm) { + if (frm.doc.docstatus === 1 && + (flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) && + frappe.model.can_create("Payment Entry")) { + frm.add_custom_button(__('Payment'), + function () { + frm.events.make_payment_entry(frm); + }, __('Create')); + } else if ( + frm.doc.docstatus === 1 && + flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) && + frappe.model.can_create("Expense Claim") + ) { + frm.add_custom_button( + __("Expense Claim"), + function () { + frm.events.make_expense_claim(frm); + }, + __('Create') + ); + } + + if ( + frm.doc.docstatus === 1 + && (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount)) + ) { + if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")) { + frm.add_custom_button(__("Return"), function() { + frm.trigger('make_return_entry'); + }, __('Create')); + } else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) { + frm.add_custom_button(__("Deduction from Salary"), function() { + frm.events.make_deduction_via_additional_salary(frm); + }, __('Create')); + } + } + }, + + make_deduction_via_additional_salary: function(frm) { + frappe.call({ + method: "hrms.hr.doctype.employee_advance.employee_advance.create_return_through_additional_salary", + args: { + doc: frm.doc + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, + + make_payment_entry: function(frm) { + let method = "hrms.overrides.employee_payment_entry.get_payment_entry_for_employee"; + if (frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { + method = "hrms.hr.doctype.employee_advance.employee_advance.make_bank_entry"; + } + return frappe.call({ + method: method, + args: { + "dt": frm.doc.doctype, + "dn": frm.doc.name + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, + + make_expense_claim: function(frm) { + return frappe.call({ + method: "hrms.hr.doctype.expense_claim.expense_claim.get_expense_claim", + args: { + "employee_name": frm.doc.employee, + "company": frm.doc.company, + "employee_advance_name": frm.doc.name, + "posting_date": frm.doc.posting_date, + "paid_amount": frm.doc.paid_amount, + "claimed_amount": frm.doc.claimed_amount + }, + callback: function(r) { + const doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, + + make_return_entry: function(frm) { + frappe.call({ + method: 'hrms.hr.doctype.employee_advance.employee_advance.make_return_entry', + args: { + 'employee': frm.doc.employee, + 'company': frm.doc.company, + 'employee_advance_name': frm.doc.name, + 'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount), + 'advance_account': frm.doc.advance_account, + 'mode_of_payment': frm.doc.mode_of_payment, + 'currency': frm.doc.currency, + 'exchange_rate': frm.doc.exchange_rate + }, + callback: function(r) { + const doclist = frappe.model.sync(r.message); + frappe.set_route('Form', doclist[0].doctype, doclist[0].name); + } + }); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('get_pending_amount') + ]); + } + }, + + get_pending_amount: function(frm) { + frappe.call({ + method: "hrms.hr.doctype.employee_advance.employee_advance.get_pending_amount", + args: { + "employee": frm.doc.employee, + "posting_date": frm.doc.posting_date + }, + callback: function(r) { + frm.set_value("pending_amount", r.message); + } + }); + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, + + currency: function(frm) { + if (frm.doc.currency) { + var from_currency = frm.doc.currency; + var company_currency; + if (!frm.doc.company) { + company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } else { + company_currency = erpnext.get_currency(frm.doc.company); + } + if (from_currency != company_currency) { + frm.events.set_exchange_rate(frm, from_currency, company_currency); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", ""); + } + frm.refresh_fields(); + } + }, + + set_exchange_rate: function(frm, from_currency, company_currency) { + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: from_currency, + to_currency: company_currency, + }, + callback: function(r) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + } + }); + } +}); diff --git a/hrms/hr/doctype/employee_advance/employee_advance.json b/hrms/hr/doctype/employee_advance/employee_advance.json new file mode 100644 index 0000000..b050183 --- /dev/null +++ b/hrms/hr/doctype/employee_advance/employee_advance.json @@ -0,0 +1,277 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2022-01-17 18:36:51.450395", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "department", + "column_break_4", + "posting_date", + "currency", + "exchange_rate", + "repay_unclaimed_amount_from_salary", + "section_break_8", + "purpose", + "column_break_11", + "advance_amount", + "paid_amount", + "pending_amount", + "claimed_amount", + "return_amount", + "section_break_7", + "status", + "company", + "amended_from", + "column_break_18", + "advance_account", + "mode_of_payment" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HR-EAD-.YYYY.-" + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "label": "Employee Name" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "purpose", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Purpose", + "reqd": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "advance_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance Amount", + "options": "currency", + "reqd": 1 + }, + { + "fieldname": "paid_amount", + "fieldtype": "Currency", + "label": "Paid Amount", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "claimed_amount", + "fieldtype": "Currency", + "label": "Claimed Amount", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled", + "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Advance", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fieldname": "advance_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Advance Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, + { + "allow_on_submit": 1, + "fieldname": "return_amount", + "fieldtype": "Currency", + "label": "Returned Amount", + "options": "currency", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "repay_unclaimed_amount_from_salary", + "fieldtype": "Check", + "label": "Repay Unclaimed Amount from Salary" + }, + { + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "pending_amount", + "fieldtype": "Currency", + "label": "Pending Amount", + "options": "currency", + "read_only": 1 + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, + { + "depends_on": "currency", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9", + "print_hide": 1, + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-17 19:33:52.345823", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Advance", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Expense Approver", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [ + { + "color": "Red", + "custom": 1, + "title": "Draft" + }, + { + "color": "Green", + "custom": 1, + "title": "Paid" + }, + { + "color": "Orange", + "custom": 1, + "title": "Unpaid" + }, + { + "color": "Blue", + "custom": 1, + "title": "Claimed" + }, + { + "color": "Gray", + "title": "Returned" + }, + { + "color": "Yellow", + "title": "Partly Claimed and Returned" + }, + { + "color": "Red", + "custom": 1, + "title": "Cancelled" + } + ], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_advance/employee_advance.py b/hrms/hr/doctype/employee_advance/employee_advance.py new file mode 100644 index 0000000..8b4d313 --- /dev/null +++ b/hrms/hr/doctype/employee_advance/employee_advance.py @@ -0,0 +1,324 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.query_builder.functions import Sum +from frappe.utils import flt, nowdate + +import erpnext +from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account + +from hrms.hr.utils import validate_active_employee + + +class EmployeeAdvanceOverPayment(frappe.ValidationError): + pass + + +class EmployeeAdvance(Document): + def onload(self): + self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value( + "Accounts Settings", "make_payment_via_journal_entry" + ) + + def validate(self): + validate_active_employee(self.employee) + self.set_status() + + def on_cancel(self): + self.ignore_linked_doctypes = "GL Entry" + self.set_status(update=True) + + def set_status(self, update=False): + precision = self.precision("paid_amount") + total_amount = flt(flt(self.claimed_amount) + flt(self.return_amount), precision) + status = None + + if self.docstatus == 0: + status = "Draft" + elif self.docstatus == 1: + if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt( + self.paid_amount, precision + ): + status = "Claimed" + elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt( + self.paid_amount, precision + ): + status = "Returned" + elif ( + flt(self.claimed_amount) > 0 + and (flt(self.return_amount) > 0) + and total_amount == flt(self.paid_amount, precision) + ): + status = "Partly Claimed and Returned" + elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt( + self.paid_amount, precision + ): + status = "Paid" + else: + status = "Unpaid" + elif self.docstatus == 2: + status = "Cancelled" + + if update: + self.db_set("status", status) + else: + self.status = status + + def set_total_advance_paid(self): + gle = frappe.qb.DocType("GL Entry") + + paid_amount = ( + frappe.qb.from_(gle) + .select(Sum(gle.debit).as_("paid_amount")) + .where( + (gle.against_voucher_type == "Employee Advance") + & (gle.against_voucher == self.name) + & (gle.party_type == "Employee") + & (gle.party == self.employee) + & (gle.docstatus == 1) + & (gle.is_cancelled == 0) + ) + ).run(as_dict=True)[0].paid_amount or 0 + + return_amount = ( + frappe.qb.from_(gle) + .select(Sum(gle.credit).as_("return_amount")) + .where( + (gle.against_voucher_type == "Employee Advance") + & (gle.voucher_type != "Expense Claim") + & (gle.against_voucher == self.name) + & (gle.party_type == "Employee") + & (gle.party == self.employee) + & (gle.docstatus == 1) + & (gle.is_cancelled == 0) + ) + ).run(as_dict=True)[0].return_amount or 0 + + if paid_amount != 0: + paid_amount = flt(paid_amount) / flt(self.exchange_rate) + if return_amount != 0: + return_amount = flt(return_amount) / flt(self.exchange_rate) + + if flt(paid_amount) > self.advance_amount: + frappe.throw( + _("Row {0}# Paid Amount cannot be greater than requested advance amount"), + EmployeeAdvanceOverPayment, + ) + + if flt(return_amount) > self.paid_amount - self.claimed_amount: + frappe.throw(_("Return amount cannot be greater unclaimed amount")) + + self.db_set("paid_amount", paid_amount) + self.db_set("return_amount", return_amount) + self.set_status(update=True) + + def update_claimed_amount(self): + claimed_amount = ( + frappe.db.sql( + """ + SELECT sum(ifnull(allocated_amount, 0)) + FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec + WHERE + eca.employee_advance = %s + AND ec.approval_status="Approved" + AND ec.name = eca.parent + AND ec.docstatus=1 + AND eca.allocated_amount > 0 + """, + self.name, + )[0][0] + or 0 + ) + + frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount)) + self.reload() + self.set_status(update=True) + + +@frappe.whitelist() +def get_pending_amount(employee, posting_date): + employee_due_amount = frappe.get_all( + "Employee Advance", + filters={"employee": employee, "docstatus": 1, "posting_date": ("<=", posting_date)}, + fields=["advance_amount", "paid_amount"], + ) + return sum([(emp.advance_amount - emp.paid_amount) for emp in employee_due_amount]) + + +@frappe.whitelist() +def make_bank_entry(dt, dn): + doc = frappe.get_doc(dt, dn) + payment_account = get_default_bank_cash_account( + doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment + ) + if not payment_account: + frappe.throw(_("Please set a Default Cash Account in Company defaults")) + + advance_account_currency = frappe.db.get_value("Account", doc.advance_account, "account_currency") + + advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate( + advance_account_currency, doc + ) + + paying_amount, paying_exchange_rate = get_paying_amount_paying_exchange_rate(payment_account, doc) + + je = frappe.new_doc("Journal Entry") + je.posting_date = nowdate() + je.voucher_type = "Bank Entry" + je.company = doc.company + je.remark = "Payment against Employee Advance: " + dn + "\n" + doc.purpose + je.multi_currency = 1 if advance_account_currency != payment_account.account_currency else 0 + + je.append( + "accounts", + { + "account": doc.advance_account, + "account_currency": advance_account_currency, + "exchange_rate": flt(advance_exchange_rate), + "debit_in_account_currency": flt(advance_amount), + "reference_type": "Employee Advance", + "reference_name": doc.name, + "party_type": "Employee", + "cost_center": erpnext.get_default_cost_center(doc.company), + "party": doc.employee, + "is_advance": "Yes", + }, + ) + + je.append( + "accounts", + { + "account": payment_account.account, + "cost_center": erpnext.get_default_cost_center(doc.company), + "credit_in_account_currency": flt(paying_amount), + "account_currency": payment_account.account_currency, + "account_type": payment_account.account_type, + "exchange_rate": flt(paying_exchange_rate), + }, + ) + + return je.as_dict() + + +def get_advance_amount_advance_exchange_rate(advance_account_currency, doc): + if advance_account_currency != doc.currency: + advance_amount = flt(doc.advance_amount) * flt(doc.exchange_rate) + advance_exchange_rate = 1 + else: + advance_amount = doc.advance_amount + advance_exchange_rate = doc.exchange_rate + + return advance_amount, advance_exchange_rate + + +def get_paying_amount_paying_exchange_rate(payment_account, doc): + if payment_account.account_currency != doc.currency: + paying_amount = flt(doc.advance_amount) * flt(doc.exchange_rate) + paying_exchange_rate = 1 + else: + paying_amount = doc.advance_amount + paying_exchange_rate = doc.exchange_rate + + return paying_amount, paying_exchange_rate + + +@frappe.whitelist() +def create_return_through_additional_salary(doc): + import json + + if isinstance(doc, str): + doc = frappe._dict(json.loads(doc)) + + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.employee = doc.employee + additional_salary.currency = doc.currency + additional_salary.amount = doc.paid_amount - doc.claimed_amount + additional_salary.company = doc.company + additional_salary.ref_doctype = doc.doctype + additional_salary.ref_docname = doc.name + + return additional_salary + + +@frappe.whitelist() +def make_return_entry( + employee, + company, + employee_advance_name, + return_amount, + advance_account, + currency, + exchange_rate, + mode_of_payment=None, +): + bank_cash_account = get_default_bank_cash_account( + company, account_type="Cash", mode_of_payment=mode_of_payment + ) + if not bank_cash_account: + frappe.throw(_("Please set a Default Cash Account in Company defaults")) + + advance_account_currency = frappe.db.get_value("Account", advance_account, "account_currency") + + je = frappe.new_doc("Journal Entry") + je.posting_date = nowdate() + je.voucher_type = get_voucher_type(mode_of_payment) + je.company = company + je.remark = "Return against Employee Advance: " + employee_advance_name + je.multi_currency = 1 if advance_account_currency != bank_cash_account.account_currency else 0 + + advance_account_amount = ( + flt(return_amount) + if advance_account_currency == currency + else flt(return_amount) * flt(exchange_rate) + ) + + je.append( + "accounts", + { + "account": advance_account, + "credit_in_account_currency": advance_account_amount, + "account_currency": advance_account_currency, + "exchange_rate": flt(exchange_rate) if advance_account_currency == currency else 1, + "reference_type": "Employee Advance", + "reference_name": employee_advance_name, + "party_type": "Employee", + "party": employee, + "is_advance": "Yes", + "cost_center": erpnext.get_default_cost_center(company), + }, + ) + + bank_amount = ( + flt(return_amount) + if bank_cash_account.account_currency == currency + else flt(return_amount) * flt(exchange_rate) + ) + + je.append( + "accounts", + { + "account": bank_cash_account.account, + "debit_in_account_currency": bank_amount, + "account_currency": bank_cash_account.account_currency, + "account_type": bank_cash_account.account_type, + "exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1, + "cost_center": erpnext.get_default_cost_center(company), + }, + ) + + return je.as_dict() + + +def get_voucher_type(mode_of_payment=None): + voucher_type = "Cash Entry" + + if mode_of_payment: + mode_of_payment_type = frappe.get_cached_value("Mode of Payment", mode_of_payment, "type") + if mode_of_payment_type == "Bank": + voucher_type = "Bank Entry" + + return voucher_type diff --git a/hrms/hr/doctype/employee_advance/employee_advance_dashboard.py b/hrms/hr/doctype/employee_advance/employee_advance_dashboard.py new file mode 100644 index 0000000..73fac51 --- /dev/null +++ b/hrms/hr/doctype/employee_advance/employee_advance_dashboard.py @@ -0,0 +1,9 @@ +def get_data(): + return { + "fieldname": "employee_advance", + "non_standard_fieldnames": { + "Payment Entry": "reference_name", + "Journal Entry": "reference_name", + }, + "transactions": [{"items": ["Expense Claim"]}, {"items": ["Payment Entry", "Journal Entry"]}], + } diff --git a/hrms/hr/doctype/employee_advance/test_employee_advance.py b/hrms/hr/doctype/employee_advance/test_employee_advance.py new file mode 100644 index 0000000..d80ebbf --- /dev/null +++ b/hrms/hr/doctype/employee_advance/test_employee_advance.py @@ -0,0 +1,258 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import flt, nowdate + +import erpnext +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.employee_advance.employee_advance import ( + EmployeeAdvanceOverPayment, + create_return_through_additional_salary, + make_bank_entry, + make_return_entry, +) +from hrms.hr.doctype.expense_claim.expense_claim import get_advances +from hrms.hr.doctype.expense_claim.test_expense_claim import ( + get_payable_account, + make_expense_claim, +) +from hrms.payroll.doctype.salary_component.test_salary_component import create_salary_component +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + +class TestEmployeeAdvance(unittest.TestCase): + def setUp(self): + frappe.db.delete("Employee Advance") + + def test_paid_amount_and_status(self): + employee_name = make_employee("_T@employe.advance") + advance = make_employee_advance(employee_name) + + journal_entry = make_payment_entry(advance) + journal_entry.submit() + + advance.reload() + + self.assertEqual(advance.paid_amount, 1000) + self.assertEqual(advance.status, "Paid") + + # try making over payment + journal_entry1 = make_payment_entry(advance) + self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit) + + def test_paid_amount_on_pe_cancellation(self): + employee_name = make_employee("_T@employe.advance") + advance = make_employee_advance(employee_name) + + pe = make_payment_entry(advance) + pe.submit() + + advance.reload() + + self.assertEqual(advance.paid_amount, 1000) + self.assertEqual(advance.status, "Paid") + + pe.cancel() + advance.reload() + + self.assertEqual(advance.paid_amount, 0) + self.assertEqual(advance.status, "Unpaid") + + advance.cancel() + advance.reload() + self.assertEqual(advance.status, "Cancelled") + + def test_claimed_status(self): + # CLAIMED Status check, full amount claimed + payable_account = get_payable_account("_Test Company") + claim = make_expense_claim( + payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True + ) + + advance = make_employee_advance(claim.employee) + pe = make_payment_entry(advance) + pe.submit() + + claim = get_advances_for_claim(claim, advance.name) + claim.save() + claim.submit() + + advance.reload() + self.assertEqual(advance.claimed_amount, 1000) + self.assertEqual(advance.status, "Claimed") + + # advance should not be shown in claims + advances = get_advances(claim.employee) + advances = [entry.name for entry in advances] + self.assertTrue(advance.name not in advances) + + # cancel claim; status should be Paid + claim.cancel() + advance.reload() + self.assertEqual(advance.claimed_amount, 0) + self.assertEqual(advance.status, "Paid") + + def test_partly_claimed_and_returned_status(self): + payable_account = get_payable_account("_Test Company") + claim = make_expense_claim( + payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True + ) + + advance = make_employee_advance(claim.employee) + pe = make_payment_entry(advance) + pe.submit() + + # PARTLY CLAIMED AND RETURNED status check + # 500 Claimed, 500 Returned + claim = make_expense_claim( + payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True + ) + + advance = make_employee_advance(claim.employee) + pe = make_payment_entry(advance) + pe.submit() + + claim = get_advances_for_claim(claim, advance.name, amount=500) + claim.save() + claim.submit() + + advance.reload() + self.assertEqual(advance.claimed_amount, 500) + self.assertEqual(advance.status, "Paid") + + entry = make_return_entry( + employee=advance.employee, + company=advance.company, + employee_advance_name=advance.name, + return_amount=flt(advance.paid_amount - advance.claimed_amount), + advance_account=advance.advance_account, + mode_of_payment=advance.mode_of_payment, + currency=advance.currency, + exchange_rate=advance.exchange_rate, + ) + + entry = frappe.get_doc(entry) + entry.insert() + entry.submit() + + advance.reload() + self.assertEqual(advance.return_amount, 500) + self.assertEqual(advance.status, "Partly Claimed and Returned") + + # advance should not be shown in claims + advances = get_advances(claim.employee) + advances = [entry.name for entry in advances] + self.assertTrue(advance.name not in advances) + + # Cancel return entry; status should change to PAID + entry.cancel() + advance.reload() + self.assertEqual(advance.return_amount, 0) + self.assertEqual(advance.status, "Paid") + + # advance should be shown in claims + advances = get_advances(claim.employee) + advances = [entry.name for entry in advances] + self.assertTrue(advance.name in advances) + + def test_repay_unclaimed_amount_from_salary(self): + employee_name = make_employee("_T@employe.advance") + advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1}) + pe = make_payment_entry(advance) + pe.submit() + + args = {"type": "Deduction"} + create_salary_component("Advance Salary - Deduction", **args) + make_salary_structure( + "Test Additional Salary for Advance Return", "Monthly", employee=employee_name + ) + + # additional salary for 700 first + advance.reload() + additional_salary = create_return_through_additional_salary(advance) + additional_salary.salary_component = "Advance Salary - Deduction" + additional_salary.payroll_date = nowdate() + additional_salary.amount = 700 + additional_salary.insert() + additional_salary.submit() + + advance.reload() + self.assertEqual(advance.return_amount, 700) + + # additional salary for remaining 300 + additional_salary = create_return_through_additional_salary(advance) + additional_salary.salary_component = "Advance Salary - Deduction" + additional_salary.payroll_date = nowdate() + additional_salary.amount = 300 + additional_salary.insert() + additional_salary.submit() + + advance.reload() + self.assertEqual(advance.return_amount, 1000) + self.assertEqual(advance.status, "Returned") + + # update advance return amount on additional salary cancellation + additional_salary.cancel() + advance.reload() + self.assertEqual(advance.return_amount, 700) + self.assertEqual(advance.status, "Paid") + + def tearDown(self): + frappe.db.rollback() + + +def make_payment_entry(advance): + journal_entry = frappe.get_doc(make_bank_entry("Employee Advance", advance.name)) + journal_entry.cheque_no = "123123" + journal_entry.cheque_date = nowdate() + journal_entry.save() + + return journal_entry + + +def make_employee_advance(employee_name, args=None): + doc = frappe.new_doc("Employee Advance") + doc.employee = employee_name + doc.company = "_Test Company" + doc.purpose = "For site visit" + doc.currency = erpnext.get_company_currency("_Test company") + doc.exchange_rate = 1 + doc.advance_amount = 1000 + doc.posting_date = nowdate() + doc.advance_account = "_Test Employee Advance - _TC" + + if args: + doc.update(args) + + doc.insert() + doc.submit() + + return doc + + +def get_advances_for_claim(claim, advance_name, amount=None): + advances = get_advances(claim.employee, advance_name) + + for entry in advances: + if amount: + allocated_amount = amount + else: + allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount) + + claim.append( + "advances", + { + "employee_advance": entry.name, + "posting_date": entry.posting_date, + "advance_account": entry.advance_account, + "advance_paid": entry.paid_amount, + "unclaimed_amount": allocated_amount, + "allocated_amount": allocated_amount, + }, + ) + + return claim diff --git a/hrms/hr/doctype/employee_attendance_tool/__init__.py b/hrms/hr/doctype/employee_attendance_tool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.css b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.css new file mode 100644 index 0000000..c8d6644 --- /dev/null +++ b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.css @@ -0,0 +1,21 @@ +.top-toolbar{ + padding-bottom: 30px; + margin-left: -17px; +} + +.bottom-toolbar{ + margin-left: -17px; + margin-top: 20px; +} + +.btn{ + margin-right: 5px; +} + +.marked-employee-label{ + font-weight: normal; +} + +.checkbox{ + margin-top: -3px; +} diff --git a/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.js new file mode 100644 index 0000000..e2ad0cf --- /dev/null +++ b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.js @@ -0,0 +1,269 @@ +frappe.ui.form.on("Employee Attendance Tool", { + refresh: function(frm) { + frm.disable_save(); + }, + + onload: function(frm) { + frm.set_value("date", frappe.datetime.get_today()); + erpnext.employee_attendance_tool.load_employees(frm); + }, + + date: function(frm) { + erpnext.employee_attendance_tool.load_employees(frm); + }, + + department: function(frm) { + erpnext.employee_attendance_tool.load_employees(frm); + }, + + branch: function(frm) { + erpnext.employee_attendance_tool.load_employees(frm); + }, + + company: function(frm) { + erpnext.employee_attendance_tool.load_employees(frm); + } + +}); + + +erpnext.employee_attendance_tool = { + load_employees: function(frm) { + if(frm.doc.date) { + frappe.call({ + method: "hrms.hr.doctype.employee_attendance_tool.employee_attendance_tool.get_employees", + args: { + date: frm.doc.date, + department: frm.doc.department, + branch: frm.doc.branch, + company: frm.doc.company + }, + callback: function(r) { + if(r.message['unmarked'].length > 0) { + unhide_field('unmarked_attendance_section') + if(!frm.employee_area) { + frm.employee_area = $('
') + .appendTo(frm.fields_dict.employees_html.wrapper); + } + frm.EmployeeSelector = new erpnext.EmployeeSelector(frm, frm.employee_area, r.message['unmarked']) + } + else{ + hide_field('unmarked_attendance_section') + } + + if(r.message['marked'].length > 0) { + unhide_field('marked_attendance_section') + if(!frm.marked_employee_area) { + frm.marked_employee_area = $('
') + .appendTo(frm.fields_dict.marked_attendance_html.wrapper); + } + frm.marked_employee = new erpnext.MarkedEmployee(frm, frm.marked_employee_area, r.message['marked']) + } + else{ + hide_field('marked_attendance_section') + } + } + }); + } + } +} + +erpnext.MarkedEmployee = class MarkedEmployee { + constructor(frm, wrapper, employee) { + this.wrapper = wrapper; + this.frm = frm; + this.make(frm, employee); + } + make(frm, employee) { + var me = this; + $(this.wrapper).empty(); + + var row; + $.each(employee, function(i, m) { + var attendance_icon = "fa fa-check"; + var color_class = ""; + if(m.status == "Absent") { + attendance_icon = "fa fa-check-empty" + color_class = "text-muted"; + } + else if(m.status == "Half Day") { + attendance_icon = "fa fa-check-minus" + } + + if (i===0 || i % 4===0) { + row = $('
').appendTo(me.wrapper); + } + + $(repl('
\ + \ +
', { + employee: m.employee +' : '+ m.employee_name, + icon: attendance_icon, + color_class: color_class + })).appendTo(row); + }); + } +}; + + +erpnext.EmployeeSelector = class EmployeeSelector { + constructor(frm, wrapper, employee) { + this.wrapper = wrapper; + this.frm = frm; + this.make(frm, employee); + } + make(frm, employee) { + var me = this; + + $(this.wrapper).empty(); + var employee_toolbar = $('
\ + \ + \ +
').appendTo($(this.wrapper)); + + var mark_employee_toolbar = $('
\ + \ + \ + \ + \ +
'); + + employee_toolbar.find(".btn-add") + .html(__('Check all')) + .on("click", function() { + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if(!$(check).is(":checked")) { + check.checked = true; + } + }); + }); + + employee_toolbar.find(".btn-remove") + .html(__('Uncheck all')) + .on("click", function() { + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + check.checked = false; + } + }); + }); + + mark_employee_toolbar.find(".btn-mark-present") + .html(__('Mark Present')) + .on("click", function() { + var employee_present = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_present.push(employee[i]); + } + }); + frappe.call({ + method: "hrms.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_present, + "status":"Present", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + mark_employee_toolbar.find(".btn-mark-absent") + .html(__('Mark Absent')) + .on("click", function() { + var employee_absent = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_absent.push(employee[i]); + } + }); + frappe.call({ + method: "hrms.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_absent, + "status":"Absent", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + + mark_employee_toolbar.find(".btn-mark-half-day") + .html(__('Mark Half Day')) + .on("click", function() { + var employee_half_day = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_half_day.push(employee[i]); + } + }); + frappe.call({ + method: "hrms.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_half_day, + "status":"Half Day", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + + mark_employee_toolbar.find(".btn-mark-work-from-home") + .html(__('Mark Work From Home')) + .on("click", function() { + var employee_work_from_home = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_work_from_home.push(employee[i]); + } + }); + frappe.call({ + method: "hrms.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_work_from_home, + "status":"Work From Home", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + var row; + $.each(employee, function(i, m) { + if (i===0 || (i % 4) === 0) { + row = $('
').appendTo(me.wrapper); + } + + $(repl('
\ +
\ + \ +
', {employee: m.employee +' : '+ m.employee_name})).appendTo(row); + }); + + mark_employee_toolbar.appendTo($(this.wrapper)); + } +}; diff --git a/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.json b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.json new file mode 100644 index 0000000..256e056 --- /dev/null +++ b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.json @@ -0,0 +1,275 @@ +{ + "allow_copy": 1, + "allow_import": 0, + "allow_rename": 0, + "creation": "2016-01-27 14:59:47.849379", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "branch", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Branch", + "length": 0, + "no_copy": 0, + "options": "Branch", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "date", + "fieldname": "unmarked_attendance_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Unmarked Attendance", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "employees_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Employees HTML", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "date", + "fieldname": "marked_attendance_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Marked Attendance", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "marked_attendance_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Marked Attendance HTML", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 1, + "hide_toolbar": 1, + "idx": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2016-01-29 02:14:36.034952", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Attendance Tool", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.py b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.py new file mode 100644 index 0000000..43665cc --- /dev/null +++ b/hrms/hr/doctype/employee_attendance_tool/employee_attendance_tool.py @@ -0,0 +1,69 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import json + +import frappe +from frappe.model.document import Document +from frappe.utils import getdate + + +class EmployeeAttendanceTool(Document): + pass + + +@frappe.whitelist() +def get_employees(date, department=None, branch=None, company=None): + attendance_not_marked = [] + attendance_marked = [] + filters = {"status": "Active", "date_of_joining": ["<=", date]} + + for field, value in {"department": department, "branch": branch, "company": company}.items(): + if value: + filters[field] = value + + employee_list = frappe.get_list( + "Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name" + ) + marked_employee = {} + for emp in frappe.get_list( + "Attendance", fields=["employee", "status"], filters={"attendance_date": date} + ): + marked_employee[emp["employee"]] = emp["status"] + + for employee in employee_list: + employee["status"] = marked_employee.get(employee["employee"]) + if employee["employee"] not in marked_employee: + attendance_not_marked.append(employee) + else: + attendance_marked.append(employee) + return {"marked": attendance_marked, "unmarked": attendance_not_marked} + + +@frappe.whitelist() +def mark_employee_attendance(employee_list, status, date, leave_type=None, company=None): + + employee_list = json.loads(employee_list) + for employee in employee_list: + + if status == "On Leave" and leave_type: + leave_type = leave_type + else: + leave_type = None + + company = frappe.db.get_value("Employee", employee["employee"], "Company", cache=True) + + attendance = frappe.get_doc( + dict( + doctype="Attendance", + employee=employee.get("employee"), + employee_name=employee.get("employee_name"), + attendance_date=getdate(date), + status=status, + leave_type=leave_type, + company=company, + ) + ) + attendance.insert() + attendance.submit() diff --git a/hrms/hr/doctype/employee_boarding_activity/__init__.py b/hrms/hr/doctype/employee_boarding_activity/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.json b/hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.json new file mode 100644 index 0000000..8474bd0 --- /dev/null +++ b/hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.json @@ -0,0 +1,108 @@ +{ + "actions": [], + "creation": "2018-05-09 05:37:18.439763", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "activity_name", + "user", + "role", + "begin_on", + "duration", + "column_break_3", + "task", + "task_weight", + "required_for_employee_creation", + "section_break_6", + "description" + ], + "fields": [ + { + "columns": 3, + "fieldname": "activity_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Activity Name", + "reqd": 1 + }, + { + "columns": 2, + "depends_on": "eval:!doc.role", + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User" + }, + { + "columns": 1, + "depends_on": "eval:!doc.user", + "fieldname": "role", + "fieldtype": "Link", + "label": "Role", + "options": "Role" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "no_copy": 1, + "options": "Task", + "read_only": 1 + }, + { + "fieldname": "task_weight", + "fieldtype": "Float", + "label": "Task Weight" + }, + { + "default": "0", + "depends_on": "eval:['Employee Onboarding', 'Employee Onboarding Template'].includes(doc.parenttype)", + "description": "Applicable in the case of Employee Onboarding", + "fieldname": "required_for_employee_creation", + "fieldtype": "Check", + "label": "Required for Employee Creation" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Description" + }, + { + "columns": 2, + "fieldname": "duration", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Duration (Days)" + }, + { + "columns": 2, + "fieldname": "begin_on", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Begin On (Days)" + } + ], + "istable": 1, + "links": [], + "modified": "2022-01-29 14:05:00.543122", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Boarding Activity", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.py b/hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.py new file mode 100644 index 0000000..e824081 --- /dev/null +++ b/hrms/hr/doctype/employee_boarding_activity/employee_boarding_activity.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeeBoardingActivity(Document): + pass diff --git a/hrms/hr/doctype/employee_checkin/__init__.py b/hrms/hr/doctype/employee_checkin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.js b/hrms/hr/doctype/employee_checkin/employee_checkin.js new file mode 100644 index 0000000..c2403ca --- /dev/null +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.js @@ -0,0 +1,10 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Checkin', { + setup: (frm) => { + if(!frm.doc.time) { + frm.set_value("time", frappe.datetime.now_datetime()); + } + } +}); diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.json b/hrms/hr/doctype/employee_checkin/employee_checkin.json new file mode 100644 index 0000000..d34316d --- /dev/null +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.json @@ -0,0 +1,208 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "EMP-CKIN-.MM.-.YYYY.-.######", + "creation": "2019-06-10 11:56:34.536413", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "log_type", + "shift", + "column_break_4", + "time", + "device_id", + "skip_auto_attendance", + "attendance", + "shift_start", + "shift_end", + "shift_actual_start", + "shift_actual_end" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "log_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Log Type", + "options": "\nIN\nOUT" + }, + { + "fieldname": "shift", + "fieldtype": "Link", + "label": "Shift", + "options": "Shift Type", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "Now", + "fieldname": "time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Time", + "permlevel": 1, + "reqd": 1 + }, + { + "fieldname": "device_id", + "fieldtype": "Data", + "label": "Location / Device ID" + }, + { + "default": "0", + "fieldname": "skip_auto_attendance", + "fieldtype": "Check", + "label": "Skip Auto Attendance" + }, + { + "fieldname": "attendance", + "fieldtype": "Link", + "label": "Attendance Marked", + "options": "Attendance", + "read_only": 1 + }, + { + "fieldname": "shift_start", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Shift Start" + }, + { + "fieldname": "shift_end", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Shift End" + }, + { + "fieldname": "shift_actual_start", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Shift Actual Start" + }, + { + "fieldname": "shift_actual_end", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Shift Actual End" + } + ], + "links": [], + "modified": "2020-07-08 11:02:32.660986", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Checkin", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "read": 1, + "role": "Employee", + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "Employee" + } + ], + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.py b/hrms/hr/doctype/employee_checkin/employee_checkin.py new file mode 100644 index 0000000..a46a556 --- /dev/null +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.py @@ -0,0 +1,278 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import cint, get_datetime, get_link_to_form + +from hrms.hr.doctype.attendance.attendance import ( + get_duplicate_attendance_record, + get_overlapping_shift_attendance, +) +from hrms.hr.doctype.shift_assignment.shift_assignment import ( + get_actual_start_end_datetime_of_shift, +) +from hrms.hr.utils import validate_active_employee + + +class EmployeeCheckin(Document): + def validate(self): + validate_active_employee(self.employee) + self.validate_duplicate_log() + self.fetch_shift() + + def validate_duplicate_log(self): + doc = frappe.db.exists( + "Employee Checkin", {"employee": self.employee, "time": self.time, "name": ["!=", self.name]} + ) + if doc: + doc_link = frappe.get_desk_link("Employee Checkin", doc) + frappe.throw( + _("This employee already has a log with the same timestamp.{0}").format("
" + doc_link) + ) + + def fetch_shift(self): + shift_actual_timings = get_actual_start_end_datetime_of_shift( + self.employee, get_datetime(self.time), True + ) + if shift_actual_timings: + if ( + shift_actual_timings.shift_type.determine_check_in_and_check_out + == "Strictly based on Log Type in Employee Checkin" + and not self.log_type + and not self.skip_auto_attendance + ): + frappe.throw( + _("Log Type is required for check-ins falling in the shift: {0}.").format( + shift_actual_timings.shift_type.name + ) + ) + if not self.attendance: + self.shift = shift_actual_timings.shift_type.name + self.shift_actual_start = shift_actual_timings.actual_start + self.shift_actual_end = shift_actual_timings.actual_end + self.shift_start = shift_actual_timings.start_datetime + self.shift_end = shift_actual_timings.end_datetime + else: + self.shift = None + + +@frappe.whitelist() +def add_log_based_on_employee_field( + employee_field_value, + timestamp, + device_id=None, + log_type=None, + skip_auto_attendance=0, + employee_fieldname="attendance_device_id", +): + """Finds the relevant Employee using the employee field value and creates a Employee Checkin. + + :param employee_field_value: The value to look for in employee field. + :param timestamp: The timestamp of the Log. Currently expected in the following format as string: '2019-05-08 10:48:08.000000' + :param device_id: (optional)Location / Device ID. A short string is expected. + :param log_type: (optional)Direction of the Punch if available (IN/OUT). + :param skip_auto_attendance: (optional)Skip auto attendance field will be set for this log(0/1). + :param employee_fieldname: (Default: attendance_device_id)Name of the field in Employee DocType based on which employee lookup will happen. + """ + + if not employee_field_value or not timestamp: + frappe.throw(_("'employee_field_value' and 'timestamp' are required.")) + + employee = frappe.db.get_values( + "Employee", + {employee_fieldname: employee_field_value}, + ["name", "employee_name", employee_fieldname], + as_dict=True, + ) + if employee: + employee = employee[0] + else: + frappe.throw( + _("No Employee found for the given employee field value. '{}': {}").format( + employee_fieldname, employee_field_value + ) + ) + + doc = frappe.new_doc("Employee Checkin") + doc.employee = employee.name + doc.employee_name = employee.employee_name + doc.time = timestamp + doc.device_id = device_id + doc.log_type = log_type + if cint(skip_auto_attendance) == 1: + doc.skip_auto_attendance = "1" + doc.insert() + + return doc + + +def mark_attendance_and_link_log( + logs, + attendance_status, + attendance_date, + working_hours=None, + late_entry=False, + early_exit=False, + in_time=None, + out_time=None, + shift=None, +): + """Creates an attendance and links the attendance to the Employee Checkin. + Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown. + + :param logs: The List of 'Employee Checkin'. + :param attendance_status: Attendance status to be marked. One of: (Present, Absent, Half Day, Skip). Note: 'On Leave' is not supported by this function. + :param attendance_date: Date of the attendance to be created. + :param working_hours: (optional)Number of working hours for the given date. + """ + log_names = [x.name for x in logs] + employee = logs[0].employee + + if attendance_status == "Skip": + skip_attendance_in_checkins(log_names) + return None + + elif attendance_status in ("Present", "Absent", "Half Day"): + employee_doc = frappe.get_doc("Employee", employee) + duplicate = get_duplicate_attendance_record(employee, attendance_date, shift) + overlapping = get_overlapping_shift_attendance(employee, attendance_date, shift) + + if not duplicate and not overlapping: + doc_dict = { + "doctype": "Attendance", + "employee": employee, + "attendance_date": attendance_date, + "status": attendance_status, + "working_hours": working_hours, + "company": employee_doc.company, + "shift": shift, + "late_entry": late_entry, + "early_exit": early_exit, + "in_time": in_time, + "out_time": out_time, + } + attendance = frappe.get_doc(doc_dict).insert() + attendance.submit() + + if attendance_status == "Absent": + attendance.add_comment( + text=_("Employee was marked Absent for not meeting the working hours threshold.") + ) + + frappe.db.sql( + """update `tabEmployee Checkin` + set attendance = %s + where name in %s""", + (attendance.name, log_names), + ) + return attendance + else: + skip_attendance_in_checkins(log_names) + add_comment_in_checkins(log_names, duplicate, overlapping) + return None + + else: + frappe.throw(_("{} is an invalid Attendance Status.").format(attendance_status)) + + +def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): + """Given a set of logs in chronological order calculates the total working hours based on the parameters. + Zero is returned for all invalid cases. + + :param logs: The List of 'Employee Checkin'. + :param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin' + :param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out' + """ + total_hours = 0 + in_time = out_time = None + if check_in_out_type == "Alternating entries as IN and OUT during the same shift": + in_time = logs[0].time + if len(logs) >= 2: + out_time = logs[-1].time + if working_hours_calc_type == "First Check-in and Last Check-out": + # assumption in this case: First log always taken as IN, Last log always taken as OUT + total_hours = time_diff_in_hours(in_time, logs[-1].time) + elif working_hours_calc_type == "Every Valid Check-in and Check-out": + logs = logs[:] + while len(logs) >= 2: + total_hours += time_diff_in_hours(logs[0].time, logs[1].time) + del logs[:2] + + elif check_in_out_type == "Strictly based on Log Type in Employee Checkin": + if working_hours_calc_type == "First Check-in and Last Check-out": + first_in_log_index = find_index_in_dict(logs, "log_type", "IN") + first_in_log = ( + logs[first_in_log_index] if first_in_log_index or first_in_log_index == 0 else None + ) + last_out_log_index = find_index_in_dict(reversed(logs), "log_type", "OUT") + last_out_log = ( + logs[len(logs) - 1 - last_out_log_index] + if last_out_log_index or last_out_log_index == 0 + else None + ) + if first_in_log and last_out_log: + in_time, out_time = first_in_log.time, last_out_log.time + total_hours = time_diff_in_hours(in_time, out_time) + elif working_hours_calc_type == "Every Valid Check-in and Check-out": + in_log = out_log = None + for log in logs: + if in_log and out_log: + if not in_time: + in_time = in_log.time + out_time = out_log.time + total_hours += time_diff_in_hours(in_log.time, out_log.time) + in_log = out_log = None + if not in_log: + in_log = log if log.log_type == "IN" else None + if in_log and not in_time: + in_time = in_log.time + elif not out_log: + out_log = log if log.log_type == "OUT" else None + + if in_log and out_log: + out_time = out_log.time + total_hours += time_diff_in_hours(in_log.time, out_log.time) + + return total_hours, in_time, out_time + + +def time_diff_in_hours(start, end): + return round(float((end - start).total_seconds()) / 3600, 2) + + +def find_index_in_dict(dict_list, key, value): + return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None) + + +def add_comment_in_checkins(log_names, duplicate, overlapping): + if duplicate: + text = _("Auto Attendance skipped due to duplicate attendance record: {}").format( + get_link_to_form("Attendance", duplicate[0].name) + ) + else: + text = _("Auto Attendance skipped due to overlapping attendance record: {}").format( + get_link_to_form("Attendance", overlapping.name) + ) + + for name in log_names: + frappe.get_doc( + { + "doctype": "Comment", + "comment_type": "Comment", + "reference_doctype": "Employee Checkin", + "reference_name": name, + "content": text, + } + ).insert(ignore_permissions=True) + + +def skip_attendance_in_checkins(log_names): + EmployeeCheckin = frappe.qb.DocType("Employee Checkin") + ( + frappe.qb.update(EmployeeCheckin) + .set("skip_auto_attendance", 1) + .where(EmployeeCheckin.name.isin(log_names)) + ).run() diff --git a/hrms/hr/doctype/employee_checkin/test_employee_checkin.py b/hrms/hr/doctype/employee_checkin/test_employee_checkin.py new file mode 100644 index 0000000..8f4cb65 --- /dev/null +++ b/hrms/hr/doctype/employee_checkin/test_employee_checkin.py @@ -0,0 +1,347 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest +from datetime import datetime, timedelta + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import ( + add_days, + get_time, + get_year_ending, + get_year_start, + getdate, + now_datetime, + nowdate, +) + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.employee_checkin.employee_checkin import ( + add_log_based_on_employee_field, + calculate_working_hours, + mark_attendance_and_link_log, +) +from hrms.hr.doctype.leave_application.test_leave_application import get_first_sunday +from hrms.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type +from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + +class TestEmployeeCheckin(FrappeTestCase): + def setUp(self): + frappe.db.delete("Shift Type") + frappe.db.delete("Shift Assignment") + frappe.db.delete("Employee Checkin") + + from_date = get_year_start(getdate()) + to_date = get_year_ending(getdate()) + self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) + + def test_add_log_based_on_employee_field(self): + employee = make_employee("test_add_log_based_on_employee_field@example.com") + employee = frappe.get_doc("Employee", employee) + employee.attendance_device_id = "3344" + employee.save() + + time_now = now_datetime().__str__()[:-7] + employee_checkin = add_log_based_on_employee_field("3344", time_now, "mumbai_first_floor", "IN") + self.assertEqual(employee_checkin.employee, employee.name) + self.assertEqual(employee_checkin.time, time_now) + self.assertEqual(employee_checkin.device_id, "mumbai_first_floor") + self.assertEqual(employee_checkin.log_type, "IN") + + def test_mark_attendance_and_link_log(self): + employee = make_employee("test_mark_attendance_and_link_log@example.com") + logs = make_n_checkins(employee, 3) + mark_attendance_and_link_log(logs, "Skip", nowdate()) + log_names = [log.name for log in logs] + logs_count = frappe.db.count( + "Employee Checkin", {"name": ["in", log_names], "skip_auto_attendance": 1} + ) + self.assertEqual(logs_count, 3) + + logs = make_n_checkins(employee, 4, 2) + now_date = nowdate() + frappe.db.delete("Attendance", {"employee": employee}) + attendance = mark_attendance_and_link_log(logs, "Present", now_date, 8.2) + log_names = [log.name for log in logs] + logs_count = frappe.db.count( + "Employee Checkin", {"name": ["in", log_names], "attendance": attendance.name} + ) + self.assertEqual(logs_count, 4) + attendance_count = frappe.db.count( + "Attendance", + {"status": "Present", "working_hours": 8.2, "employee": employee, "attendance_date": now_date}, + ) + self.assertEqual(attendance_count, 1) + + def test_unlink_attendance_on_cancellation(self): + employee = make_employee("test_mark_attendance_and_link_log@example.com") + logs = make_n_checkins(employee, 3) + + frappe.db.delete("Attendance", {"employee": employee}) + attendance = mark_attendance_and_link_log(logs, "Present", nowdate(), 8.2) + attendance.cancel() + + linked_logs = frappe.db.get_all("Employee Checkin", {"attendance": attendance.name}) + self.assertEquals(len(linked_logs), 0) + + def test_calculate_working_hours(self): + check_in_out_type = [ + "Alternating entries as IN and OUT during the same shift", + "Strictly based on Log Type in Employee Checkin", + ] + working_hours_calc_type = [ + "First Check-in and Last Check-out", + "Every Valid Check-in and Check-out", + ] + logs_type_1 = [ + {"time": now_datetime() - timedelta(minutes=390)}, + {"time": now_datetime() - timedelta(minutes=300)}, + {"time": now_datetime() - timedelta(minutes=270)}, + {"time": now_datetime() - timedelta(minutes=90)}, + {"time": now_datetime() - timedelta(minutes=0)}, + ] + logs_type_2 = [ + {"time": now_datetime() - timedelta(minutes=390), "log_type": "OUT"}, + {"time": now_datetime() - timedelta(minutes=360), "log_type": "IN"}, + {"time": now_datetime() - timedelta(minutes=300), "log_type": "OUT"}, + {"time": now_datetime() - timedelta(minutes=290), "log_type": "IN"}, + {"time": now_datetime() - timedelta(minutes=260), "log_type": "OUT"}, + {"time": now_datetime() - timedelta(minutes=240), "log_type": "IN"}, + {"time": now_datetime() - timedelta(minutes=150), "log_type": "IN"}, + {"time": now_datetime() - timedelta(minutes=60), "log_type": "OUT"}, + ] + logs_type_1 = [frappe._dict(x) for x in logs_type_1] + logs_type_2 = [frappe._dict(x) for x in logs_type_2] + + working_hours = calculate_working_hours( + logs_type_1, check_in_out_type[0], working_hours_calc_type[0] + ) + self.assertEqual(working_hours, (6.5, logs_type_1[0].time, logs_type_1[-1].time)) + + working_hours = calculate_working_hours( + logs_type_1, check_in_out_type[0], working_hours_calc_type[1] + ) + self.assertEqual(working_hours, (4.5, logs_type_1[0].time, logs_type_1[-1].time)) + + working_hours = calculate_working_hours( + logs_type_2, check_in_out_type[1], working_hours_calc_type[0] + ) + self.assertEqual(working_hours, (5, logs_type_2[1].time, logs_type_2[-1].time)) + + working_hours = calculate_working_hours( + logs_type_2, check_in_out_type[1], working_hours_calc_type[1] + ) + self.assertEqual(working_hours, (4.5, logs_type_2[1].time, logs_type_2[-1].time)) + + working_hours = calculate_working_hours( + [logs_type_2[1], logs_type_2[-1]], check_in_out_type[1], working_hours_calc_type[1] + ) + self.assertEqual(working_hours, (5.0, logs_type_2[1].time, logs_type_2[-1].time)) + + def test_fetch_shift(self): + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + + # shift setup for 8-12 + shift_type = setup_shift_type() + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + # within shift time + timestamp = datetime.combine(date, get_time("08:45:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift_type.name) + + # "begin checkin before shift time" = 60 mins, so should work for 7:00:00 + timestamp = datetime.combine(date, get_time("07:00:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift_type.name) + + # "allow checkout after shift end time" = 60 mins, so should work for 13:00:00 + timestamp = datetime.combine(date, get_time("13:00:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift_type.name) + + # should not fetch this shift beyond allowed time + timestamp = datetime.combine(date, get_time("13:01:00")) + log = make_checkin(employee, timestamp) + self.assertIsNone(log.shift) + + def test_fetch_shift_for_assignment_with_end_date(self): + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + + # shift setup for 8-12 + shift1 = setup_shift_type() + # 12:30 - 16:30 + shift2 = setup_shift_type(shift_type="Shift 2", start_time="12:30:00", end_time="16:30:00") + + date = getdate() + make_shift_assignment(shift1.name, employee, date, add_days(date, 15)) + make_shift_assignment(shift2.name, employee, date, add_days(date, 15)) + + timestamp = datetime.combine(date, get_time("08:45:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift1.name) + + timestamp = datetime.combine(date, get_time("12:45:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift2.name) + + # log after end date + timestamp = datetime.combine(add_days(date, 16), get_time("12:45:00")) + log = make_checkin(employee, timestamp) + self.assertIsNone(log.shift) + + def test_shift_start_and_end_timings(self): + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + + # shift setup for 8-12 + shift_type = setup_shift_type() + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:45:00")) + log = make_checkin(employee, timestamp) + + self.assertEqual(log.shift, shift_type.name) + self.assertEqual(log.shift_start, datetime.combine(date, get_time("08:00:00"))) + self.assertEqual(log.shift_end, datetime.combine(date, get_time("12:00:00"))) + self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("07:00:00"))) + self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("13:00:00"))) + + def test_fetch_shift_based_on_default_shift(self): + employee = make_employee("test_default_shift@example.com", company="_Test Company") + default_shift = setup_shift_type( + shift_type="Default Shift", start_time="14:00:00", end_time="16:00:00" + ) + + date = getdate() + frappe.db.set_value("Employee", employee, "default_shift", default_shift.name) + + timestamp = datetime.combine(date, get_time("14:45:00")) + log = make_checkin(employee, timestamp) + + # should consider default shift + self.assertEqual(log.shift, default_shift.name) + + def test_fetch_shift_spanning_over_two_days(self): + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type( + shift_type="Midnight Shift", start_time="23:00:00", end_time="01:00:00" + ) + date = getdate() + next_day = add_days(date, 1) + make_shift_assignment(shift_type.name, employee, date) + + # log falls in the first day + timestamp = datetime.combine(date, get_time("23:00:00")) + log = make_checkin(employee, timestamp) + + self.assertEqual(log.shift, shift_type.name) + self.assertEqual(log.shift_start, datetime.combine(date, get_time("23:00:00"))) + self.assertEqual(log.shift_end, datetime.combine(next_day, get_time("01:00:00"))) + self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("22:00:00"))) + self.assertEqual(log.shift_actual_end, datetime.combine(next_day, get_time("02:00:00"))) + + log.delete() + + # log falls in the second day + prev_day = add_days(date, -1) + timestamp = datetime.combine(date, get_time("01:30:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift_type.name) + self.assertEqual(log.shift_start, datetime.combine(prev_day, get_time("23:00:00"))) + self.assertEqual(log.shift_end, datetime.combine(date, get_time("01:00:00"))) + self.assertEqual(log.shift_actual_start, datetime.combine(prev_day, get_time("22:00:00"))) + self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("02:00:00"))) + + def test_no_shift_fetched_on_holiday_as_per_shift_holiday_list(self): + date = getdate() + from_date = get_year_start(date) + to_date = get_year_ending(date) + holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) + + employee = make_employee("test_shift_with_holiday@example.com", company="_Test Company") + setup_shift_type(shift_type="Test Holiday Shift", holiday_list=holiday_list) + + first_sunday = get_first_sunday(holiday_list, for_date=date) + timestamp = datetime.combine(first_sunday, get_time("08:00:00")) + log = make_checkin(employee, timestamp) + + self.assertIsNone(log.shift) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_no_shift_fetched_on_holiday_as_per_employee_holiday_list(self): + employee = make_employee("test_shift_with_holiday@example.com", company="_Test Company") + shift_type = setup_shift_type(shift_type="Test Holiday Shift") + shift_type.holiday_list = None + shift_type.save() + + date = getdate() + + first_sunday = get_first_sunday(self.holiday_list, for_date=date) + timestamp = datetime.combine(first_sunday, get_time("08:00:00")) + log = make_checkin(employee, timestamp) + + self.assertIsNone(log.shift) + + def test_consecutive_shift_assignments_overlapping_within_grace_period(self): + # test adjustment for start and end times if they are overlapping + # within "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time" periods + employee = make_employee("test_shift@example.com", company="_Test Company") + + # 8 - 12 + shift1 = setup_shift_type() + # 12:30 - 16:30 + shift2 = setup_shift_type( + shift_type="Consecutive Shift", start_time="12:30:00", end_time="16:30:00" + ) + + # the actual start and end times (with grace) for these shifts are 7 - 13 and 11:30 - 17:30 + date = getdate() + make_shift_assignment(shift1.name, employee, date) + make_shift_assignment(shift2.name, employee, date) + + # log at 12:30 should set shift2 and actual start as 12 and not 11:30 + timestamp = datetime.combine(date, get_time("12:30:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift2.name) + self.assertEqual(log.shift_start, datetime.combine(date, get_time("12:30:00"))) + self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("12:00:00"))) + + # log at 12:00 should set shift1 and actual end as 12 and not 1 since the next shift's grace starts + timestamp = datetime.combine(date, get_time("12:00:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift1.name) + self.assertEqual(log.shift_end, datetime.combine(date, get_time("12:00:00"))) + self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("12:00:00"))) + + # log at 12:01 should set shift2 + timestamp = datetime.combine(date, get_time("12:01:00")) + log = make_checkin(employee, timestamp) + self.assertEqual(log.shift, shift2.name) + + +def make_n_checkins(employee, n, hours_to_reverse=1): + logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n + 1))] + for i in range(n - 1): + logs.append( + make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n - i)) + ) + return logs + + +def make_checkin(employee, time=now_datetime()): + log = frappe.get_doc( + { + "doctype": "Employee Checkin", + "employee": employee, + "time": time, + "device_id": "device1", + "log_type": "IN", + } + ).insert() + return log diff --git a/hrms/hr/doctype/employee_grade/__init__.py b/hrms/hr/doctype/employee_grade/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_grade/employee_grade.js b/hrms/hr/doctype/employee_grade/employee_grade.js new file mode 100644 index 0000000..6c67f54 --- /dev/null +++ b/hrms/hr/doctype/employee_grade/employee_grade.js @@ -0,0 +1,29 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Grade', { + refresh: function (frm) { + + }, + setup: function (frm) { + frm.set_query("default_salary_structure", function () { + return { + "filters": { + "docstatus": 1, + "is_active": "Yes" + } + }; + }); + + frm.set_query("default_leave_policy", function () { + return { + "filters": { + "docstatus": 1 + } + }; + }); + + + } + +}); diff --git a/hrms/hr/doctype/employee_grade/employee_grade.json b/hrms/hr/doctype/employee_grade/employee_grade.json new file mode 100644 index 0000000..4967137 --- /dev/null +++ b/hrms/hr/doctype/employee_grade/employee_grade.json @@ -0,0 +1,89 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 16:14:24.174138", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "default_salary_structure", + "currency", + "default_base_pay" + ], + "fields": [ + { + "fieldname": "default_salary_structure", + "fieldtype": "Link", + "label": "Default Salary Structure", + "options": "Salary Structure" + }, + { + "depends_on": "default_salary_structure", + "fieldname": "default_base_pay", + "fieldtype": "Currency", + "label": "Default Base Pay", + "options": "currency" + }, + { + "fetch_from": "default_salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-05-06 15:42:10.395508", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Grade", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_grade/employee_grade.py b/hrms/hr/doctype/employee_grade/employee_grade.py new file mode 100644 index 0000000..41b7915 --- /dev/null +++ b/hrms/hr/doctype/employee_grade/employee_grade.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeeGrade(Document): + pass diff --git a/hrms/hr/doctype/employee_grade/employee_grade_dashboard.py b/hrms/hr/doctype/employee_grade/employee_grade_dashboard.py new file mode 100644 index 0000000..efc68ce --- /dev/null +++ b/hrms/hr/doctype/employee_grade/employee_grade_dashboard.py @@ -0,0 +1,9 @@ +def get_data(): + return { + "transactions": [ + { + "items": ["Employee", "Leave Period"], + }, + {"items": ["Employee Onboarding Template", "Employee Separation Template"]}, + ] + } diff --git a/hrms/hr/doctype/employee_grade/test_employee_grade.py b/hrms/hr/doctype/employee_grade/test_employee_grade.py new file mode 100644 index 0000000..a70d685 --- /dev/null +++ b/hrms/hr/doctype/employee_grade/test_employee_grade.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeGrade(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/employee_grievance/__init__.py b/hrms/hr/doctype/employee_grievance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_grievance/employee_grievance.js b/hrms/hr/doctype/employee_grievance/employee_grievance.js new file mode 100644 index 0000000..25c5bad --- /dev/null +++ b/hrms/hr/doctype/employee_grievance/employee_grievance.js @@ -0,0 +1,39 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Grievance', { + setup: function(frm) { + frm.set_query('grievance_against_party', function() { + return { + filters: { + name: ['in', [ + 'Company', 'Department', 'Employee Group', 'Employee Grade', 'Employee'] + ] + } + }; + }); + frm.set_query('associated_document_type', function() { + let ignore_modules = ["Setup", "Core", "Integrations", "Automation", "Website", + "Utilities", "Event Streaming", "Social", "Chat", "Data Migration", "Printing", "Desk", "Custom"]; + return { + filters: { + istable: 0, + issingle: 0, + module: ["Not In", ignore_modules] + } + }; + }); + }, + + grievance_against_party: function(frm) { + let filters = {}; + if (frm.doc.grievance_against_party == 'Employee' && frm.doc.raised_by) { + filters.name = ["!=", frm.doc.raised_by]; + } + frm.set_query('grievance_against', function() { + return { + filters: filters + }; + }); + }, +}); diff --git a/hrms/hr/doctype/employee_grievance/employee_grievance.json b/hrms/hr/doctype/employee_grievance/employee_grievance.json new file mode 100644 index 0000000..5a91856 --- /dev/null +++ b/hrms/hr/doctype/employee_grievance/employee_grievance.json @@ -0,0 +1,261 @@ +{ + "actions": [], + "autoname": "HR-GRIEV-.YYYY.-.#####", + "creation": "2021-05-11 13:41:51.485295", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "subject", + "raised_by", + "employee_name", + "designation", + "column_break_3", + "date", + "status", + "reports_to", + "grievance_details_section", + "grievance_against_party", + "grievance_against", + "grievance_type", + "column_break_11", + "associated_document_type", + "associated_document", + "section_break_14", + "description", + "investigation_details_section", + "cause_of_grievance", + "resolution_details_section", + "resolved_by", + "resolution_date", + "employee_responsible", + "column_break_16", + "resolution_detail", + "amended_from" + ], + "fields": [ + { + "fieldname": "grievance_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Grievance Type", + "options": "Grievance Type", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date ", + "reqd": 1 + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Open\nInvestigated\nResolved\nInvalid", + "reqd": 1 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "reqd": 1 + }, + { + "fieldname": "cause_of_grievance", + "fieldtype": "Text", + "label": "Cause of Grievance", + "mandatory_depends_on": "eval: doc.status == \"Investigated\" || doc.status == \"Resolved\"" + }, + { + "fieldname": "resolution_details_section", + "fieldtype": "Section Break", + "label": "Resolution Details" + }, + { + "fieldname": "resolved_by", + "fieldtype": "Link", + "label": "Resolved By", + "mandatory_depends_on": "eval: doc.status == \"Resolved\"", + "options": "User" + }, + { + "fieldname": "employee_responsible", + "fieldtype": "Link", + "label": "Employee Responsible ", + "options": "Employee" + }, + { + "fieldname": "resolution_detail", + "fieldtype": "Small Text", + "label": "Resolution Details", + "mandatory_depends_on": "eval: doc.status == \"Resolved\"" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "fieldname": "resolution_date", + "fieldtype": "Date", + "label": "Resolution Date", + "mandatory_depends_on": "eval: doc.status == \"Resolved\"" + }, + { + "fieldname": "grievance_against", + "fieldtype": "Dynamic Link", + "label": "Grievance Against", + "options": "grievance_against_party", + "reqd": 1 + }, + { + "fieldname": "raised_by", + "fieldtype": "Link", + "label": "Raised By", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Grievance", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "raised_by.designation", + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, + { + "fetch_from": "raised_by.reports_to", + "fieldname": "reports_to", + "fieldtype": "Link", + "label": "Reports To", + "options": "Employee", + "read_only": 1 + }, + { + "fieldname": "grievance_details_section", + "fieldtype": "Section Break", + "label": "Grievance Details" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, + { + "fieldname": "grievance_against_party", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Grievance Against Party", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "associated_document_type", + "fieldtype": "Link", + "label": "Associated Document Type", + "options": "DocType" + }, + { + "fieldname": "associated_document", + "fieldtype": "Dynamic Link", + "label": "Associated Document", + "options": "associated_document_type" + }, + { + "fieldname": "investigation_details_section", + "fieldtype": "Section Break", + "label": "Investigation Details" + }, + { + "fetch_from": "raised_by.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "subject", + "fieldtype": "Data", + "label": "Subject", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-06-21 12:51:01.499486", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Grievance", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "select": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "select": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "search_fields": "subject,raised_by,grievance_against_party", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "subject", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_grievance/employee_grievance.py b/hrms/hr/doctype/employee_grievance/employee_grievance.py new file mode 100644 index 0000000..45de79f --- /dev/null +++ b/hrms/hr/doctype/employee_grievance/employee_grievance.py @@ -0,0 +1,16 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _, bold +from frappe.model.document import Document + + +class EmployeeGrievance(Document): + def on_submit(self): + if self.status not in ["Invalid", "Resolved"]: + frappe.throw( + _("Only Employee Grievance with status {0} or {1} can be submitted").format( + bold("Invalid"), bold("Resolved") + ) + ) diff --git a/hrms/hr/doctype/employee_grievance/employee_grievance_list.js b/hrms/hr/doctype/employee_grievance/employee_grievance_list.js new file mode 100644 index 0000000..11672ca --- /dev/null +++ b/hrms/hr/doctype/employee_grievance/employee_grievance_list.js @@ -0,0 +1,12 @@ +frappe.listview_settings["Employee Grievance"] = { + has_indicator_for_draft: 1, + get_indicator: function(doc) { + var colors = { + "Open": "red", + "Investigated": "orange", + "Resolved": "green", + "Invalid": "grey" + }; + return [__(doc.status), colors[doc.status], "status,=," + doc.status]; + } +}; diff --git a/hrms/hr/doctype/employee_grievance/test_employee_grievance.py b/hrms/hr/doctype/employee_grievance/test_employee_grievance.py new file mode 100644 index 0000000..cf6a915 --- /dev/null +++ b/hrms/hr/doctype/employee_grievance/test_employee_grievance.py @@ -0,0 +1,55 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import today + +from erpnext.setup.doctype.employee.test_employee import make_employee + + +class TestEmployeeGrievance(unittest.TestCase): + def test_create_employee_grievance(self): + create_employee_grievance() + + +def create_employee_grievance(): + grievance_type = create_grievance_type() + emp_1 = make_employee("test_emp_grievance_@example.com", company="_Test Company") + emp_2 = make_employee("testculprit@example.com", company="_Test Company") + + grievance = frappe.new_doc("Employee Grievance") + grievance.subject = "Test Employee Grievance" + grievance.raised_by = emp_1 + grievance.date = today() + grievance.grievance_type = grievance_type + grievance.grievance_against_party = "Employee" + grievance.grievance_against = emp_2 + grievance.description = "test descrip" + + # set cause + grievance.cause_of_grievance = "test cause" + + # resolution details + grievance.resolution_date = today() + grievance.resolution_detail = "test resolution detail" + grievance.resolved_by = "test_emp_grievance_@example.com" + grievance.employee_responsible = emp_2 + grievance.status = "Resolved" + + grievance.save() + grievance.submit() + + return grievance + + +def create_grievance_type(): + if frappe.db.exists("Grievance Type", "Employee Abuse"): + return frappe.get_doc("Grievance Type", "Employee Abuse") + grievance_type = frappe.new_doc("Grievance Type") + grievance_type.name = "Employee Abuse" + grievance_type.description = "Test" + grievance_type.save() + + return grievance_type.name diff --git a/hrms/hr/doctype/employee_health_insurance/__init__.py b/hrms/hr/doctype/employee_health_insurance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.js b/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.js new file mode 100644 index 0000000..69d46e2 --- /dev/null +++ b/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Health Insurance', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.json b/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.json new file mode 100644 index 0000000..e63da39 --- /dev/null +++ b/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.json @@ -0,0 +1,113 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "autoname": "field:health_insurance_name", + "beta": 0, + "creation": "2017-03-27 14:32:51.628588", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "health_insurance_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Health Insurance Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-04-15 14:56:46.924890", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Health Insurance", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "health_insurance_name", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.py b/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.py new file mode 100644 index 0000000..4a8c437 --- /dev/null +++ b/hrms/hr/doctype/employee_health_insurance/employee_health_insurance.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeeHealthInsurance(Document): + pass diff --git a/hrms/hr/doctype/employee_health_insurance/test_employee_health_insurance.py b/hrms/hr/doctype/employee_health_insurance/test_employee_health_insurance.py new file mode 100644 index 0000000..4f042b7 --- /dev/null +++ b/hrms/hr/doctype/employee_health_insurance/test_employee_health_insurance.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeHealthInsurance(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/employee_loan/employee_loan.js b/hrms/hr/doctype/employee_loan/employee_loan.js new file mode 100644 index 0000000..f1e3dd9 --- /dev/null +++ b/hrms/hr/doctype/employee_loan/employee_loan.js @@ -0,0 +1,121 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Loan', { + onload: function (frm) { + frm.set_query("employee_loan_application", function () { + return { + "filters": { + "employee": frm.doc.employee, + "docstatus": 1, + "status": "Approved" + } + }; + }); + + frm.set_query("interest_income_account", function () { + return { + "filters": { + "company": frm.doc.company, + "root_type": "Income", + "is_group": 0 + } + }; + }); + + frm.set_query("employee", function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); + + $.each(["payment_account", "employee_loan_account"], function (i, field) { + frm.set_query(field, function () { + return { + "filters": { + "company": frm.doc.company, + "root_type": "Asset", + "is_group": 0 + } + }; + }); + }) + }, + + refresh: function (frm) { + if (frm.doc.docstatus == 1 && (frm.doc.status == "Sanctioned" || frm.doc.status == "Partially Disbursed")) { + frm.add_custom_button(__('Create Disbursement Entry'), function () { + frm.trigger("make_jv"); + }) + } + frm.trigger("toggle_fields"); + }, + + make_jv: function (frm) { + frappe.call({ + args: { + "employee_loan": frm.doc.name, + "company": frm.doc.company, + "employee_loan_account": frm.doc.employee_loan_account, + "employee": frm.doc.employee, + "loan_amount": frm.doc.loan_amount, + "payment_account": frm.doc.payment_account + }, + method: "hrms.hr.doctype.employee_loan.employee_loan.make_jv_entry", + callback: function (r) { + if (r.message) + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }) + }, + + mode_of_payment: function (frm) { + if (frm.doc.mode_of_payment && frm.doc.company) { + frappe.call({ + method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account", + args: { + "mode_of_payment": frm.doc.mode_of_payment, + "company": frm.doc.company + }, + callback: function (r, rt) { + if (r.message) { + frm.set_value("payment_account", r.message.account); + } + } + }); + } + }, + + employee_loan_application: function (frm) { + if(frm.doc.employee_loan_application){ + return frappe.call({ + method: "hrms.hr.doctype.employee_loan.employee_loan.get_employee_loan_application", + args: { + "employee_loan_application": frm.doc.employee_loan_application + }, + callback: function (r) { + if (!r.exc && r.message) { + frm.set_value("loan_type", r.message.loan_type); + frm.set_value("loan_amount", r.message.loan_amount); + frm.set_value("repayment_method", r.message.repayment_method); + frm.set_value("monthly_repayment_amount", r.message.repayment_amount); + frm.set_value("repayment_periods", r.message.repayment_periods); + frm.set_value("rate_of_interest", r.message.rate_of_interest); + } + } + }); + } + }, + + repayment_method: function (frm) { + frm.trigger("toggle_fields") + }, + + toggle_fields: function (frm) { + frm.toggle_enable("monthly_repayment_amount", frm.doc.repayment_method == "Repay Fixed Amount per Period") + frm.toggle_enable("repayment_periods", frm.doc.repayment_method == "Repay Over Number of Periods") + } +}); diff --git a/hrms/hr/doctype/employee_onboarding/__init__.py b/hrms/hr/doctype/employee_onboarding/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_onboarding/employee_onboarding.js b/hrms/hr/doctype/employee_onboarding/employee_onboarding.js new file mode 100644 index 0000000..506e374 --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding/employee_onboarding.js @@ -0,0 +1,83 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Onboarding', { + setup: function(frm) { + frm.set_query("job_applicant", function () { + return { + filters:{ + "status": "Accepted", + } + }; + }); + + frm.set_query('job_offer', function () { + return { + filters: { + 'job_applicant': frm.doc.job_applicant, + 'docstatus': 1 + } + }; + }); + }, + + refresh: function(frm) { + if (frm.doc.employee) { + frm.add_custom_button(__('Employee'), function() { + frappe.set_route("Form", "Employee", frm.doc.employee); + },__("View")); + } + if (frm.doc.project) { + frm.add_custom_button(__('Project'), function() { + frappe.set_route("Form", "Project", frm.doc.project); + },__("View")); + frm.add_custom_button(__('Task'), function() { + frappe.set_route('List', 'Task', {project: frm.doc.project}); + },__("View")); + } + if ((!frm.doc.employee) && (frm.doc.docstatus === 1)) { + frm.add_custom_button(__('Employee'), function () { + frappe.model.open_mapped_doc({ + method: "hrms.hr.doctype.employee_onboarding.employee_onboarding.make_employee", + frm: frm + }); + }, __('Create')); + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + }, + + employee_onboarding_template: function(frm) { + frm.set_value("activities" ,""); + if (frm.doc.employee_onboarding_template) { + frappe.call({ + method: "hrms.controllers.employee_boarding_controller.get_onboarding_details", + args: { + "parent": frm.doc.employee_onboarding_template, + "parenttype": "Employee Onboarding Template" + }, + callback: function(r) { + if (r.message) { + r.message.forEach((d) => { + frm.add_child("activities", d); + }); + refresh_field("activities"); + } + } + }); + } + }, + + job_applicant: function(frm) { + if (frm.doc.job_applicant) { + frappe.db.get_value('Employee', {'job_applicant': frm.doc.job_applicant}, 'name', (r) => { + if (r.name) { + frm.set_value('employee', r.name); + } else { + frm.set_value('employee', ''); + } + }); + } else { + frm.set_value('employee', ''); + } + } +}); diff --git a/hrms/hr/doctype/employee_onboarding/employee_onboarding.json b/hrms/hr/doctype/employee_onboarding/employee_onboarding.json new file mode 100644 index 0000000..1d2ea0c --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding/employee_onboarding.json @@ -0,0 +1,205 @@ +{ + "actions": [], + "autoname": "HR-EMP-ONB-.YYYY.-.#####", + "creation": "2018-05-09 04:57:20.016220", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "job_applicant", + "job_offer", + "employee_onboarding_template", + "column_break_7", + "company", + "boarding_status", + "project", + "details_section", + "employee", + "employee_name", + "department", + "designation", + "employee_grade", + "holiday_list", + "column_break_13", + "date_of_joining", + "boarding_begins_on", + "table_for_activity", + "activities", + "notify_users_by_email", + "amended_from" + ], + "fields": [ + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "label": "Job Applicant", + "options": "Job Applicant", + "reqd": 1 + }, + { + "fieldname": "job_offer", + "fieldtype": "Link", + "label": "Job Offer", + "options": "Job Offer", + "reqd": 1 + }, + { + "fetch_from": "job_applicant.applicant_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee", + "read_only": 1 + }, + { + "fieldname": "date_of_joining", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date of Joining", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "Pending", + "fieldname": "boarding_status", + "fieldtype": "Select", + "label": "Status", + "options": "Pending\nIn Process\nCompleted", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "notify_users_by_email", + "fieldtype": "Check", + "label": "Notify users by email" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "employee_onboarding_template", + "fieldtype": "Link", + "label": "Employee Onboarding Template", + "options": "Employee Onboarding Template" + }, + { + "fetch_from": "employee_onboarding_template.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fetch_from": "employee_onboarding_template.department", + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department", + "options": "Department" + }, + { + "fetch_from": "employee_onboarding_template.designation", + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation", + "options": "Designation" + }, + { + "fetch_from": "employee_onboarding_template.employee_grade", + "fieldname": "employee_grade", + "fieldtype": "Link", + "label": "Employee Grade", + "options": "Employee Grade" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project", + "read_only": 1 + }, + { + "fieldname": "table_for_activity", + "fieldtype": "Section Break", + "label": "Onboarding Activities" + }, + { + "allow_on_submit": 1, + "fieldname": "activities", + "fieldtype": "Table", + "label": "Activities", + "options": "Employee Boarding Activity" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Onboarding", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Employee Details" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "boarding_begins_on", + "fieldtype": "Date", + "label": "Onboarding Begins On", + "reqd": 1 + }, + { + "fieldname": "holiday_list", + "fieldtype": "Link", + "label": "Holiday List", + "options": "Holiday List" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-29 12:33:57.120384", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Onboarding", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_onboarding/employee_onboarding.py b/hrms/hr/doctype/employee_onboarding/employee_onboarding.py new file mode 100644 index 0000000..3fb5a64 --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding/employee_onboarding.py @@ -0,0 +1,86 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.mapper import get_mapped_doc + +from hrms.controllers.employee_boarding_controller import EmployeeBoardingController + + +class IncompleteTaskError(frappe.ValidationError): + pass + + +class EmployeeOnboarding(EmployeeBoardingController): + def validate(self): + super(EmployeeOnboarding, self).validate() + self.set_employee() + self.validate_duplicate_employee_onboarding() + + def set_employee(self): + if not self.employee: + self.employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") + + def validate_duplicate_employee_onboarding(self): + emp_onboarding = frappe.db.exists( + "Employee Onboarding", {"job_applicant": self.job_applicant, "docstatus": ("!=", 2)} + ) + if emp_onboarding and emp_onboarding != self.name: + frappe.throw( + _("Employee Onboarding: {0} already exists for Job Applicant: {1}").format( + frappe.bold(emp_onboarding), frappe.bold(self.job_applicant) + ) + ) + + def validate_employee_creation(self): + if self.docstatus != 1: + frappe.throw(_("Submit this to create the Employee record")) + else: + for activity in self.activities: + if not activity.required_for_employee_creation: + continue + else: + task_status = frappe.db.get_value("Task", activity.task, "status") + if task_status not in ["Completed", "Cancelled"]: + frappe.throw( + _("All the mandatory tasks for employee creation are not completed yet."), + IncompleteTaskError, + ) + + def on_submit(self): + super(EmployeeOnboarding, self).on_submit() + + def on_update_after_submit(self): + self.create_task_and_notify_user() + + def on_cancel(self): + super(EmployeeOnboarding, self).on_cancel() + + +@frappe.whitelist() +def make_employee(source_name, target_doc=None): + doc = frappe.get_doc("Employee Onboarding", source_name) + doc.validate_employee_creation() + + def set_missing_values(source, target): + target.personal_email = frappe.db.get_value("Job Applicant", source.job_applicant, "email_id") + target.status = "Active" + + doc = get_mapped_doc( + "Employee Onboarding", + source_name, + { + "Employee Onboarding": { + "doctype": "Employee", + "field_map": { + "first_name": "employee_name", + "employee_grade": "grade", + }, + } + }, + target_doc, + set_missing_values, + ) + return doc diff --git a/hrms/hr/doctype/employee_onboarding/employee_onboarding_list.js b/hrms/hr/doctype/employee_onboarding/employee_onboarding_list.js new file mode 100644 index 0000000..a33619b --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding/employee_onboarding_list.js @@ -0,0 +1,7 @@ +frappe.listview_settings['Employee Onboarding'] = { + add_fields: ["boarding_status", "employee_name", "date_of_joining", "department"], + filters:[["boarding_status","=", "Pending"]], + get_indicator: function(doc) { + return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status]; + } +}; diff --git a/hrms/hr/doctype/employee_onboarding/test_employee_onboarding.py b/hrms/hr/doctype/employee_onboarding/test_employee_onboarding.py new file mode 100644 index 0000000..2aede16 --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -0,0 +1,135 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, getdate + +from hrms.hr.doctype.employee_onboarding.employee_onboarding import ( + IncompleteTaskError, + make_employee, +) +from hrms.hr.doctype.job_offer.test_job_offer import create_job_offer +from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + +class TestEmployeeOnboarding(unittest.TestCase): + def setUp(self): + if frappe.db.exists("Employee Onboarding", {"employee_name": "Test Researcher"}): + frappe.delete_doc("Employee Onboarding", {"employee_name": "Test Researcher"}) + + project = "Employee Onboarding : test@researcher.com" + frappe.db.sql("delete from tabProject where name=%s", project) + frappe.db.sql("delete from tabTask where project=%s", project) + + def test_employee_onboarding_incomplete_task(self): + onboarding = create_employee_onboarding() + + project_name = frappe.db.get_value("Project", onboarding.project, "project_name") + self.assertEqual(project_name, "Employee Onboarding : test@researcher.com") + + # don't allow making employee if onboarding is not complete + self.assertRaises(IncompleteTaskError, make_employee, onboarding.name) + + # boarding status + self.assertEqual(onboarding.boarding_status, "Pending") + + # start and end dates + start_date, end_date = frappe.db.get_value( + "Task", onboarding.activities[0].task, ["exp_start_date", "exp_end_date"] + ) + self.assertEqual(getdate(start_date), getdate(onboarding.boarding_begins_on)) + self.assertEqual(getdate(end_date), add_days(start_date, onboarding.activities[0].duration)) + + start_date, end_date = frappe.db.get_value( + "Task", onboarding.activities[1].task, ["exp_start_date", "exp_end_date"] + ) + self.assertEqual( + getdate(start_date), add_days(onboarding.boarding_begins_on, onboarding.activities[0].duration) + ) + self.assertEqual(getdate(end_date), add_days(start_date, onboarding.activities[1].duration)) + + # complete the task + project = frappe.get_doc("Project", onboarding.project) + for task in frappe.get_all("Task", dict(project=project.name)): + task = frappe.get_doc("Task", task.name) + task.status = "Completed" + task.save() + + # boarding status + onboarding.reload() + self.assertEqual(onboarding.boarding_status, "Completed") + + # make employee + onboarding.reload() + employee = make_employee(onboarding.name) + employee.first_name = employee.employee_name + employee.date_of_joining = getdate() + employee.date_of_birth = "1990-05-08" + employee.gender = "Female" + employee.insert() + self.assertEqual(employee.employee_name, "Test Researcher") + + def tearDown(self): + frappe.db.rollback() + + +def get_job_applicant(): + if frappe.db.exists("Job Applicant", "test@researcher.com"): + return frappe.get_doc("Job Applicant", "test@researcher.com") + applicant = frappe.new_doc("Job Applicant") + applicant.applicant_name = "Test Researcher" + applicant.email_id = "test@researcher.com" + applicant.designation = "Researcher" + applicant.status = "Open" + applicant.cover_letter = "I am a great Researcher." + applicant.insert() + return applicant + + +def get_job_offer(applicant_name): + job_offer = frappe.db.exists("Job Offer", {"job_applicant": applicant_name}) + if job_offer: + return frappe.get_doc("Job Offer", job_offer) + + job_offer = create_job_offer(job_applicant=applicant_name) + job_offer.submit() + return job_offer + + +def create_employee_onboarding(): + applicant = get_job_applicant() + job_offer = get_job_offer(applicant.name) + + holiday_list = make_holiday_list("_Test Employee Boarding") + holiday_list = frappe.get_doc("Holiday List", holiday_list) + holiday_list.holidays = [] + holiday_list.save() + + onboarding = frappe.new_doc("Employee Onboarding") + onboarding.job_applicant = applicant.name + onboarding.job_offer = job_offer.name + onboarding.date_of_joining = onboarding.boarding_begins_on = getdate() + onboarding.company = "_Test Company" + onboarding.holiday_list = holiday_list.name + onboarding.designation = "Researcher" + onboarding.append( + "activities", + { + "activity_name": "Assign ID Card", + "role": "HR User", + "required_for_employee_creation": 1, + "begin_on": 0, + "duration": 1, + }, + ) + onboarding.append( + "activities", + {"activity_name": "Assign a laptop", "role": "HR User", "begin_on": 1, "duration": 1}, + ) + onboarding.status = "Pending" + onboarding.insert() + onboarding.submit() + + return onboarding diff --git a/hrms/hr/doctype/employee_onboarding_template/__init__.py b/hrms/hr/doctype/employee_onboarding_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.js b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.js new file mode 100644 index 0000000..5e1b6e9 --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.js @@ -0,0 +1,14 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Onboarding Template', { + setup: function(frm) { + frm.set_query("department", function() { + return { + filters: { + company: frm.doc.company + } + }; + }); + } +}); diff --git a/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.json b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.json new file mode 100644 index 0000000..04de08e --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.json @@ -0,0 +1,292 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "HR-EMP-ONT-.#####", + "beta": 0, + "creation": "2018-05-09 05:27:02.393377", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "designation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Designation", + "length": 0, + "no_copy": 0, + "options": "Designation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_grade", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee Grade", + "length": 0, + "no_copy": 0, + "options": "Employee Grade", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Activities", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "activities", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Activities", + "length": 0, + "no_copy": 0, + "options": "Employee Boarding Activity", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-08-21 16:15:55.720946", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Onboarding Template", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "designation", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.py b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.py new file mode 100644 index 0000000..199013a --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeeOnboardingTemplate(Document): + pass diff --git a/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py new file mode 100644 index 0000000..93237ee --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py @@ -0,0 +1,7 @@ +def get_data(): + return { + "fieldname": "employee_onboarding_template", + "transactions": [ + {"items": ["Employee Onboarding"]}, + ], + } diff --git a/hrms/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py b/hrms/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py new file mode 100644 index 0000000..db09011 --- /dev/null +++ b/hrms/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeOnboardingTemplate(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/employee_promotion/__init__.py b/hrms/hr/doctype/employee_promotion/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_promotion/employee_promotion.js b/hrms/hr/doctype/employee_promotion/employee_promotion.js new file mode 100644 index 0000000..3b3b12e --- /dev/null +++ b/hrms/hr/doctype/employee_promotion/employee_promotion.js @@ -0,0 +1,10 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +{% include 'hrms/hr/employee_property_update.js' %} + +frappe.ui.form.on('Employee Promotion', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/employee_promotion/employee_promotion.json b/hrms/hr/doctype/employee_promotion/employee_promotion.json new file mode 100644 index 0000000..173573e --- /dev/null +++ b/hrms/hr/doctype/employee_promotion/employee_promotion.json @@ -0,0 +1,172 @@ +{ + "actions": [], + "autoname": "HR-EMP-PRO-.YYYY.-.#####", + "creation": "2018-04-13 18:33:59.476562", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "salary_currency", + "column_break_3", + "promotion_date", + "company", + "details_section", + "promotion_details", + "salary_details_section", + "current_ctc", + "column_break_12", + "revised_ctc", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "promotion_date", + "fieldtype": "Date", + "label": "Promotion Date", + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "description": "Set the properties that should be updated in the Employee master on promotion submission", + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Employee Promotion Details" + }, + { + "fieldname": "promotion_details", + "fieldtype": "Table", + "options": "Employee Property History", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Promotion", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "salary_details_section", + "fieldtype": "Section Break", + "label": "Salary Details" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.salary_currency", + "fieldname": "salary_currency", + "fieldtype": "Link", + "label": "Salary Currency", + "options": "Currency", + "read_only": 1 + }, + { + "fetch_from": "employee.ctc", + "fetch_if_empty": 1, + "fieldname": "current_ctc", + "fieldtype": "Currency", + "label": "Current CTC", + "mandatory_depends_on": "revised_ctc", + "options": "salary_currency" + }, + { + "depends_on": "current_ctc", + "fieldname": "revised_ctc", + "fieldtype": "Currency", + "label": "Revised CTC", + "options": "salary_currency" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-04-22 18:47:10.168744", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Promotion", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_promotion/employee_promotion.py b/hrms/hr/doctype/employee_promotion/employee_promotion.py new file mode 100644 index 0000000..e0cc316 --- /dev/null +++ b/hrms/hr/doctype/employee_promotion/employee_promotion.py @@ -0,0 +1,42 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate + +from hrms.hr.utils import update_employee_work_history, validate_active_employee + + +class EmployeePromotion(Document): + def validate(self): + validate_active_employee(self.employee) + + def before_submit(self): + if getdate(self.promotion_date) > getdate(): + frappe.throw( + _("Employee Promotion cannot be submitted before Promotion Date"), + frappe.DocstatusTransitionError, + ) + + def on_submit(self): + employee = frappe.get_doc("Employee", self.employee) + employee = update_employee_work_history( + employee, self.promotion_details, date=self.promotion_date + ) + + if self.revised_ctc: + employee.ctc = self.revised_ctc + + employee.save() + + def on_cancel(self): + employee = frappe.get_doc("Employee", self.employee) + employee = update_employee_work_history(employee, self.promotion_details, cancel=True) + + if self.revised_ctc: + employee.ctc = self.current_ctc + + employee.save() diff --git a/hrms/hr/doctype/employee_promotion/test_employee_promotion.py b/hrms/hr/doctype/employee_promotion/test_employee_promotion.py new file mode 100644 index 0000000..edd2134 --- /dev/null +++ b/hrms/hr/doctype/employee_promotion/test_employee_promotion.py @@ -0,0 +1,97 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, getdate + +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_employee + + +class TestEmployeePromotion(FrappeTestCase): + def setUp(self): + frappe.db.delete("Employee Promotion") + + def test_submit_before_promotion_date(self): + employee = make_employee("employee@promotions.com") + promotion = frappe.get_doc( + { + "doctype": "Employee Promotion", + "employee": employee, + "promotion_details": [ + { + "property": "Designation", + "current": "Software Developer", + "new": "Project Manager", + "fieldname": "designation", + } + ], + } + ) + promotion.promotion_date = add_days(getdate(), 1) + self.assertRaises(frappe.DocstatusTransitionError, promotion.submit) + + promotion.promotion_date = getdate() + promotion.submit() + self.assertEqual(promotion.docstatus, 1) + + def test_employee_history(self): + for grade in ["L1", "L2"]: + frappe.get_doc({"doctype": "Employee Grade", "__newname": grade}).insert() + + employee = make_employee( + "test_employee_promotion@example.com", + company="_Test Company", + date_of_birth=getdate("30-09-1980"), + date_of_joining=getdate("01-10-2021"), + designation="Software Developer", + grade="L1", + salary_currency="INR", + ctc="500000", + ) + + promotion = frappe.get_doc( + { + "doctype": "Employee Promotion", + "employee": employee, + "promotion_date": getdate(), + "revised_ctc": "1000000", + "promotion_details": [ + { + "property": "Designation", + "current": "Software Developer", + "new": "Project Manager", + "fieldname": "designation", + }, + {"property": "Grade", "current": "L1", "new": "L2", "fieldname": "grade"}, + ], + } + ).submit() + + # employee fields updated + employee = frappe.get_doc("Employee", employee) + self.assertEqual(employee.grade, "L2") + self.assertEqual(employee.designation, "Project Manager") + self.assertEqual(employee.ctc, 1000000) + + # internal work history updated + self.assertEqual(employee.internal_work_history[0].designation, "Software Developer") + self.assertEqual(employee.internal_work_history[0].from_date, getdate("01-10-2021")) + + self.assertEqual(employee.internal_work_history[1].designation, "Project Manager") + self.assertEqual(employee.internal_work_history[1].from_date, getdate()) + + promotion.cancel() + employee.reload() + + # fields restored + self.assertEqual(employee.grade, "L1") + self.assertEqual(employee.designation, "Software Developer") + self.assertEqual(employee.ctc, 500000) + + # internal work history updated on cancellation + self.assertEqual(len(employee.internal_work_history), 1) + self.assertEqual(employee.internal_work_history[0].designation, "Software Developer") + self.assertEqual(employee.internal_work_history[0].from_date, getdate("01-10-2021")) diff --git a/hrms/hr/doctype/employee_property_history/__init__.py b/hrms/hr/doctype/employee_property_history/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_property_history/employee_property_history.json b/hrms/hr/doctype/employee_property_history/employee_property_history.json new file mode 100644 index 0000000..0a51579 --- /dev/null +++ b/hrms/hr/doctype/employee_property_history/employee_property_history.json @@ -0,0 +1,161 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-04-13 18:24:30.579965", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 4, + "fieldname": "property", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Property", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 3, + "fieldname": "current", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Current", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 3, + "fieldname": "new", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "New", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fieldname", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Field Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-05-02 18:19:54.436391", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Property History", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_property_history/employee_property_history.py b/hrms/hr/doctype/employee_property_history/employee_property_history.py new file mode 100644 index 0000000..345899e --- /dev/null +++ b/hrms/hr/doctype/employee_property_history/employee_property_history.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeePropertyHistory(Document): + pass diff --git a/hrms/hr/doctype/employee_referral/__init__.py b/hrms/hr/doctype/employee_referral/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_referral/employee_referral.js b/hrms/hr/doctype/employee_referral/employee_referral.js new file mode 100644 index 0000000..9f822d0 --- /dev/null +++ b/hrms/hr/doctype/employee_referral/employee_referral.js @@ -0,0 +1,66 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Employee Referral", { + refresh: function(frm) { + if (frm.doc.docstatus === 1 && frm.doc.status === "Pending") { + frm.add_custom_button(__("Reject Employee Referral"), function() { + frappe.confirm( + __("Are you sure you want to reject the Employee Referral?"), + function() { + frm.doc.status = "Rejected"; + frm.dirty(); + frm.save_or_update(); + }, + function() { + window.close(); + } + ); + }); + + frm.add_custom_button(__("Create Job Applicant"), function() { + frm.events.create_job_applicant(frm); + }).addClass("btn-primary"); + } + + // To check whether Payment is done or not + if (frm.doc.docstatus === 1 && frm.doc.status === "Accepted") { + frappe.db.get_list("Additional Salary", { + filters: { + ref_docname: cur_frm.doc.name, + docstatus: 1 + }, + fields: ["count(name) as additional_salary_count"] + }).then((data) => { + + let additional_salary_count = data[0].additional_salary_count; + + if (frm.doc.is_applicable_for_referral_bonus && !additional_salary_count) { + frm.add_custom_button(__("Create Additional Salary"), function() { + frm.events.create_additional_salary(frm); + }).addClass("btn-primary"); + } + }); + } + + }, + create_job_applicant: function(frm) { + frappe.model.open_mapped_doc({ + method: "hrms.hr.doctype.employee_referral.employee_referral.create_job_applicant", + frm: frm + }); + }, + + create_additional_salary: function(frm) { + frappe.call({ + method: "hrms.hr.doctype.employee_referral.employee_referral.create_additional_salary", + args: { + doc: frm.doc + }, + callback: function (r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, +}); diff --git a/hrms/hr/doctype/employee_referral/employee_referral.json b/hrms/hr/doctype/employee_referral/employee_referral.json new file mode 100644 index 0000000..3ae73a9 --- /dev/null +++ b/hrms/hr/doctype/employee_referral/employee_referral.json @@ -0,0 +1,305 @@ +{ + "actions": [], + "autoname": "format:HR-REF-{####}", + "creation": "2021-03-23 14:54:45.047051", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "first_name", + "last_name", + "full_name", + "column_break_6", + "date", + "status", + "for_designation", + "referral_details_section", + "email", + "contact_no", + "resume_link", + "column_break_12", + "current_employer", + "current_job_title", + "resume", + "referrer_details_section", + "referrer", + "referrer_name", + "column_break_14", + "is_applicable_for_referral_bonus", + "referral_payment_status", + "department", + "additional_information_section", + "qualification_reason", + "work_references", + "amended_from" + ], + "fields": [ + { + "fieldname": "first_name", + "fieldtype": "Data", + "label": "First Name ", + "reqd": 1 + }, + { + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name", + "reqd": 1 + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name", + "read_only": 1 + }, + { + "fieldname": "contact_no", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Contact No.", + "options": "Phone" + }, + { + "fieldname": "current_employer", + "fieldtype": "Data", + "label": "Current Employer " + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "in_standard_filter": 1, + "label": "Date", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Pending\nIn Process\nAccepted\nRejected", + "permlevel": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "current_job_title", + "fieldtype": "Data", + "label": "Current Job Title" + }, + { + "fieldname": "resume", + "fieldtype": "Attach", + "label": "Resume" + }, + { + "fieldname": "referrer_details_section", + "fieldtype": "Section Break", + "label": "Referrer Details" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "additional_information_section", + "fieldtype": "Section Break", + "label": "Additional Information " + }, + { + "fieldname": "work_references", + "fieldtype": "Text Editor", + "label": "Work References" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Referral", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "for_designation", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "For Designation ", + "options": "Designation", + "reqd": 1 + }, + { + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Email", + "options": "Email", + "reqd": 1, + "unique": 1 + }, + { + "default": "1", + "fieldname": "is_applicable_for_referral_bonus", + "fieldtype": "Check", + "label": "Is Applicable for Referral Bonus" + }, + { + "fieldname": "qualification_reason", + "fieldtype": "Text Editor", + "label": "Why is this Candidate Qualified for this Position?" + }, + { + "fieldname": "referrer", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Referrer", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "referrer.employee_name", + "fieldname": "referrer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Referrer Name", + "read_only": 1 + }, + { + "fieldname": "resume_link", + "fieldtype": "Data", + "label": "Resume Link" + }, + { + "fieldname": "referral_payment_status", + "fieldtype": "Select", + "label": "Referral Bonus Payment Status", + "options": "\nUnpaid\nPaid", + "read_only": 1 + }, + { + "fieldname": "referral_details_section", + "fieldtype": "Section Break", + "label": "Referral Details" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-05-04 17:03:26.134560", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Referral", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "full_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_referral/employee_referral.py b/hrms/hr/doctype/employee_referral/employee_referral.py new file mode 100644 index 0000000..f813e1c --- /dev/null +++ b/hrms/hr/doctype/employee_referral/employee_referral.py @@ -0,0 +1,78 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import get_link_to_form + +from hrms.hr.utils import validate_active_employee + + +class EmployeeReferral(Document): + def validate(self): + validate_active_employee(self.referrer) + self.set_full_name() + self.set_referral_bonus_payment_status() + + def set_full_name(self): + self.full_name = " ".join(filter(None, [self.first_name, self.last_name])) + + def set_referral_bonus_payment_status(self): + if not self.is_applicable_for_referral_bonus: + self.referral_payment_status = "" + else: + if not self.referral_payment_status: + self.referral_payment_status = "Unpaid" + + +@frappe.whitelist() +def create_job_applicant(source_name, target_doc=None): + emp_ref = frappe.get_doc("Employee Referral", source_name) + # just for Api call if some set status apart from default Status + status = emp_ref.status + if emp_ref.status in ["Pending", "In process"]: + status = "Open" + + job_applicant = frappe.new_doc("Job Applicant") + job_applicant.source = "Employee Referral" + job_applicant.employee_referral = emp_ref.name + job_applicant.status = status + job_applicant.designation = emp_ref.for_designation + job_applicant.applicant_name = emp_ref.full_name + job_applicant.email_id = emp_ref.email + job_applicant.phone_number = emp_ref.contact_no + job_applicant.resume_attachment = emp_ref.resume + job_applicant.resume_link = emp_ref.resume_link + job_applicant.save() + + frappe.msgprint( + _("Job Applicant {0} created successfully.").format( + get_link_to_form("Job Applicant", job_applicant.name) + ), + title=_("Success"), + indicator="green", + ) + + emp_ref.db_set("status", "In Process") + + return job_applicant + + +@frappe.whitelist() +def create_additional_salary(doc): + import json + + if isinstance(doc, str): + doc = frappe._dict(json.loads(doc)) + + if not frappe.db.exists("Additional Salary", {"ref_docname": doc.name}): + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.employee = doc.referrer + additional_salary.company = frappe.db.get_value("Employee", doc.referrer, "company") + additional_salary.overwrite_salary_structure_amount = 0 + additional_salary.ref_doctype = doc.doctype + additional_salary.ref_docname = doc.name + + return additional_salary diff --git a/hrms/hr/doctype/employee_referral/employee_referral_dashboard.py b/hrms/hr/doctype/employee_referral/employee_referral_dashboard.py new file mode 100644 index 0000000..4d683fb --- /dev/null +++ b/hrms/hr/doctype/employee_referral/employee_referral_dashboard.py @@ -0,0 +1,8 @@ +def get_data(): + return { + "fieldname": "employee_referral", + "non_standard_fieldnames": {"Additional Salary": "ref_docname"}, + "transactions": [ + {"items": ["Job Applicant", "Additional Salary"]}, + ], + } diff --git a/hrms/hr/doctype/employee_referral/employee_referral_list.js b/hrms/hr/doctype/employee_referral/employee_referral_list.js new file mode 100644 index 0000000..38dfc4d --- /dev/null +++ b/hrms/hr/doctype/employee_referral/employee_referral_list.js @@ -0,0 +1,14 @@ +frappe.listview_settings['Employee Referral'] = { + add_fields: ["status"], + get_indicator: function (doc) { + if (doc.status == "Pending") { + return [__(doc.status), "grey", "status,=," + doc.status]; + } else if (doc.status == "In Process") { + return [__(doc.status), "orange", "status,=," + doc.status]; + } else if (doc.status == "Accepted") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (doc.status == "Rejected") { + return [__(doc.status), "red", "status,=," + doc.status]; + } + }, +}; diff --git a/hrms/hr/doctype/employee_referral/test_employee_referral.py b/hrms/hr/doctype/employee_referral/test_employee_referral.py new file mode 100644 index 0000000..db0e31c --- /dev/null +++ b/hrms/hr/doctype/employee_referral/test_employee_referral.py @@ -0,0 +1,71 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import today + +from erpnext.setup.doctype.designation.test_designation import create_designation +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.employee_referral.employee_referral import ( + create_additional_salary, + create_job_applicant, +) + + +class TestEmployeeReferral(unittest.TestCase): + def setUp(self): + frappe.db.sql("DELETE FROM `tabJob Applicant`") + frappe.db.sql("DELETE FROM `tabEmployee Referral`") + + def test_workflow_and_status_sync(self): + emp_ref = create_employee_referral() + + # Check Initial status + self.assertTrue(emp_ref.status, "Pending") + + job_applicant = create_job_applicant(emp_ref.name) + + # Check status sync + emp_ref.reload() + self.assertTrue(emp_ref.status, "In Process") + + job_applicant.reload() + job_applicant.status = "Rejected" + job_applicant.save() + + emp_ref.reload() + self.assertTrue(emp_ref.status, "Rejected") + + job_applicant.reload() + job_applicant.status = "Accepted" + job_applicant.save() + + emp_ref.reload() + self.assertTrue(emp_ref.status, "Accepted") + + # Check for Referral reference in additional salary + + add_sal = create_additional_salary(emp_ref) + self.assertTrue(add_sal.ref_docname, emp_ref.name) + + def tearDown(self): + frappe.db.sql("DELETE FROM `tabJob Applicant`") + frappe.db.sql("DELETE FROM `tabEmployee Referral`") + + +def create_employee_referral(): + emp_ref = frappe.new_doc("Employee Referral") + emp_ref.first_name = "Mahesh" + emp_ref.last_name = "Singh" + emp_ref.email = "a@b.c" + emp_ref.date = today() + emp_ref.for_designation = create_designation().name + emp_ref.referrer = make_employee("testassetmovemp@example.com", company="_Test Company") + emp_ref.is_applicable_for_employee_referral_compensation = 1 + emp_ref.save() + emp_ref.submit() + + return emp_ref diff --git a/hrms/hr/doctype/employee_separation/__init__.py b/hrms/hr/doctype/employee_separation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_separation/employee_separation.js b/hrms/hr/doctype/employee_separation/employee_separation.js new file mode 100644 index 0000000..d2829af --- /dev/null +++ b/hrms/hr/doctype/employee_separation/employee_separation.js @@ -0,0 +1,49 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Separation', { + setup: function(frm) { + frm.add_fetch("employee_separation_template", "company", "company"); + frm.add_fetch("employee_separation_template", "department", "department"); + frm.add_fetch("employee_separation_template", "designation", "designation"); + frm.add_fetch("employee_separation_template", "employee_grade", "employee_grade"); + }, + + refresh: function(frm) { + if (frm.doc.employee) { + frm.add_custom_button(__('Employee'), function() { + frappe.set_route("Form", "Employee", frm.doc.employee); + },__("View")); + } + if (frm.doc.project) { + frm.add_custom_button(__('Project'), function() { + frappe.set_route("Form", "Project", frm.doc.project); + },__("View")); + frm.add_custom_button(__('Task'), function() { + frappe.set_route('List', 'Task', {project: frm.doc.project}); + },__("View")); + } + }, + + employee_separation_template: function(frm) { + frm.set_value("activities" ,""); + if (frm.doc.employee_separation_template) { + frappe.call({ + method: "hrms.controllers.employee_boarding_controller.get_onboarding_details", + args: { + "parent": frm.doc.employee_separation_template, + "parenttype": "Employee Separation Template" + }, + callback: function(r) { + if (r.message) { + $.each(r.message, function(i, d) { + var row = frappe.model.add_child(frm.doc, "Employee Boarding Activity", "activities"); + $.extend(row, d); + }); + } + refresh_field("activities"); + } + }); + } + } +}); diff --git a/hrms/hr/doctype/employee_separation/employee_separation.json b/hrms/hr/doctype/employee_separation/employee_separation.json new file mode 100644 index 0000000..c240493 --- /dev/null +++ b/hrms/hr/doctype/employee_separation/employee_separation.json @@ -0,0 +1,185 @@ +{ + "actions": [], + "autoname": "HR-EMP-SEP-.YYYY.-.#####", + "creation": "2018-05-10 02:29:16.740490", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "designation", + "employee_grade", + "column_break_7", + "company", + "boarding_status", + "resignation_letter_date", + "boarding_begins_on", + "project", + "table_for_activity", + "employee_separation_template", + "activities", + "notify_users_by_email", + "section_break_14", + "exit_interview", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.resignation_letter_date", + "fieldname": "resignation_letter_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Resignation Letter Date", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "Pending", + "fieldname": "boarding_status", + "fieldtype": "Select", + "label": "Status", + "options": "Pending\nIn Process\nCompleted", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "notify_users_by_email", + "fieldtype": "Check", + "label": "Notify users by email" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "employee_separation_template", + "fieldtype": "Link", + "label": "Employee Separation Template", + "options": "Employee Separation Template" + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, + { + "fetch_from": "employee.grade", + "fieldname": "employee_grade", + "fieldtype": "Link", + "label": "Employee Grade", + "options": "Employee Grade", + "read_only": 1 + }, + { + "fieldname": "table_for_activity", + "fieldtype": "Section Break", + "label": "Separation Activities" + }, + { + "allow_on_submit": 1, + "fieldname": "activities", + "fieldtype": "Table", + "label": "Activities", + "options": "Employee Boarding Activity" + }, + { + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, + { + "fieldname": "exit_interview", + "fieldtype": "Text Editor", + "label": "Exit Interview Summary" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Separation", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "boarding_begins_on", + "fieldtype": "Date", + "label": "Separation Begins On", + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2021-07-30 14:03:51.218791", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Separation", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_separation/employee_separation.py b/hrms/hr/doctype/employee_separation/employee_separation.py new file mode 100644 index 0000000..6ecfb84 --- /dev/null +++ b/hrms/hr/doctype/employee_separation/employee_separation.py @@ -0,0 +1,19 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from hrms.controllers.employee_boarding_controller import EmployeeBoardingController + + +class EmployeeSeparation(EmployeeBoardingController): + def validate(self): + super(EmployeeSeparation, self).validate() + + def on_submit(self): + super(EmployeeSeparation, self).on_submit() + + def on_update_after_submit(self): + self.create_task_and_notify_user() + + def on_cancel(self): + super(EmployeeSeparation, self).on_cancel() diff --git a/hrms/hr/doctype/employee_separation/employee_separation_list.js b/hrms/hr/doctype/employee_separation/employee_separation_list.js new file mode 100644 index 0000000..76c58f5 --- /dev/null +++ b/hrms/hr/doctype/employee_separation/employee_separation_list.js @@ -0,0 +1,7 @@ +frappe.listview_settings['Employee Separation'] = { + add_fields: ["boarding_status", "employee_name", "department"], + filters:[["boarding_status","=", "Pending"]], + get_indicator: function(doc) { + return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status]; + } +}; diff --git a/hrms/hr/doctype/employee_separation/test_employee_separation.py b/hrms/hr/doctype/employee_separation/test_employee_separation.py new file mode 100644 index 0000000..df31d09 --- /dev/null +++ b/hrms/hr/doctype/employee_separation/test_employee_separation.py @@ -0,0 +1,48 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import getdate + +test_dependencies = ["Employee Onboarding"] + + +class TestEmployeeSeparation(unittest.TestCase): + def test_employee_separation(self): + separation = create_employee_separation() + + self.assertEqual(separation.docstatus, 1) + self.assertEqual(separation.boarding_status, "Pending") + + project = frappe.get_doc("Project", separation.project) + project.percent_complete_method = "Manual" + project.status = "Completed" + project.save() + + separation.reload() + self.assertEqual(separation.boarding_status, "Completed") + + separation.cancel() + self.assertEqual(separation.project, "") + + def tearDown(self): + for entry in frappe.get_all("Employee Separation"): + doc = frappe.get_doc("Employee Separation", entry.name) + if doc.docstatus == 1: + doc.cancel() + doc.delete() + + +def create_employee_separation(): + employee = frappe.db.get_value("Employee", {"status": "Active", "company": "_Test Company"}) + separation = frappe.new_doc("Employee Separation") + separation.employee = employee + separation.boarding_begins_on = getdate() + separation.company = "_Test Company" + separation.append("activities", {"activity_name": "Deactivate Employee", "role": "HR User"}) + separation.boarding_status = "Pending" + separation.insert() + separation.submit() + return separation diff --git a/hrms/hr/doctype/employee_separation_template/__init__.py b/hrms/hr/doctype/employee_separation_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_separation_template/employee_separation_template.js b/hrms/hr/doctype/employee_separation_template/employee_separation_template.js new file mode 100644 index 0000000..172ff9f --- /dev/null +++ b/hrms/hr/doctype/employee_separation_template/employee_separation_template.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Separation Template', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/employee_separation_template/employee_separation_template.json b/hrms/hr/doctype/employee_separation_template/employee_separation_template.json new file mode 100644 index 0000000..e583231 --- /dev/null +++ b/hrms/hr/doctype/employee_separation_template/employee_separation_template.json @@ -0,0 +1,292 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "HR-EMP-STP-.#####", + "beta": 0, + "creation": "2018-05-09 06:31:44.498557", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "designation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Designation", + "length": 0, + "no_copy": 0, + "options": "Designation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_grade", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee Grade", + "length": 0, + "no_copy": 0, + "options": "Employee Grade", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Activities", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "activities", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Activities", + "length": 0, + "no_copy": 0, + "options": "Employee Boarding Activity", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-08-21 16:15:57.469756", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Separation Template", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "designation", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_separation_template/employee_separation_template.py b/hrms/hr/doctype/employee_separation_template/employee_separation_template.py new file mode 100644 index 0000000..70b84b1 --- /dev/null +++ b/hrms/hr/doctype/employee_separation_template/employee_separation_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeeSeparationTemplate(Document): + pass diff --git a/hrms/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py b/hrms/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py new file mode 100644 index 0000000..3ffd8dd --- /dev/null +++ b/hrms/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py @@ -0,0 +1,7 @@ +def get_data(): + return { + "fieldname": "employee_separation_template", + "transactions": [ + {"items": ["Employee Separation"]}, + ], + } diff --git a/hrms/hr/doctype/employee_separation_template/test_employee_separation_template.py b/hrms/hr/doctype/employee_separation_template/test_employee_separation_template.py new file mode 100644 index 0000000..6a0c947 --- /dev/null +++ b/hrms/hr/doctype/employee_separation_template/test_employee_separation_template.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeSeparationTemplate(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/employee_skill/__init__.py b/hrms/hr/doctype/employee_skill/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_skill/employee_skill.json b/hrms/hr/doctype/employee_skill/employee_skill.json new file mode 100644 index 0000000..4b1419e --- /dev/null +++ b/hrms/hr/doctype/employee_skill/employee_skill.json @@ -0,0 +1,141 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-04-16 09:57:52.751635", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "skill", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Skill", + "length": 0, + "no_copy": 0, + "options": "Skill", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "proficiency", + "fieldtype": "Rating", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Proficiency", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fetch_if_empty": 0, + "fieldname": "evaluation_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Evaluation Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-16 14:13:17.111035", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Skill", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_skill/employee_skill.py b/hrms/hr/doctype/employee_skill/employee_skill.py new file mode 100644 index 0000000..13bee34 --- /dev/null +++ b/hrms/hr/doctype/employee_skill/employee_skill.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeSkill(Document): + pass diff --git a/hrms/hr/doctype/employee_skill_map/__init__.py b/hrms/hr/doctype/employee_skill_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_skill_map/employee_skill_map.js b/hrms/hr/doctype/employee_skill_map/employee_skill_map.js new file mode 100644 index 0000000..b82b18d --- /dev/null +++ b/hrms/hr/doctype/employee_skill_map/employee_skill_map.js @@ -0,0 +1,21 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Skill Map', { + // refresh: function(frm) { + + // } + designation: (frm) => { + frm.set_value('employee_skills', null); + if (frm.doc.designation) { + frappe.db.get_doc('Designation', frm.doc.designation).then((designation) => { + designation.skills.forEach(designation_skill => { + let row = frappe.model.add_child(frm.doc, 'Employee Skill', 'employee_skills'); + row.skill = designation_skill.skill; + row.proficiency = 1; + }); + refresh_field('employee_skills'); + }); + } + } +}); diff --git a/hrms/hr/doctype/employee_skill_map/employee_skill_map.json b/hrms/hr/doctype/employee_skill_map/employee_skill_map.json new file mode 100644 index 0000000..21e2513 --- /dev/null +++ b/hrms/hr/doctype/employee_skill_map/employee_skill_map.json @@ -0,0 +1,88 @@ +{ + "actions": [], + "autoname": "field:employee", + "creation": "2019-04-16 10:07:48.303426", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "column_break_3", + "designation", + "skills_section", + "employee_skills", + "trainings_section", + "trainings" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee", + "unique": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "label": "Employee Name" + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Read Only", + "label": "Designation" + }, + { + "fieldname": "skills_section", + "fieldtype": "Section Break", + "label": "Skills" + }, + { + "fieldname": "employee_skills", + "fieldtype": "Table", + "label": "Employee Skills", + "options": "Employee Skill" + }, + { + "fieldname": "trainings_section", + "fieldtype": "Section Break", + "label": "Trainings" + }, + { + "fieldname": "trainings", + "fieldtype": "Table", + "label": "Trainings", + "options": "Employee Training" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + } + ], + "links": [], + "modified": "2019-12-16 11:31:09.916893", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Skill Map", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_skill_map/employee_skill_map.py b/hrms/hr/doctype/employee_skill_map/employee_skill_map.py new file mode 100644 index 0000000..ea7da9e --- /dev/null +++ b/hrms/hr/doctype/employee_skill_map/employee_skill_map.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeSkillMap(Document): + pass diff --git a/hrms/hr/doctype/employee_training/__init__.py b/hrms/hr/doctype/employee_training/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_training/employee_training.json b/hrms/hr/doctype/employee_training/employee_training.json new file mode 100644 index 0000000..0e0dc15 --- /dev/null +++ b/hrms/hr/doctype/employee_training/employee_training.json @@ -0,0 +1,108 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-04-16 16:15:50.931545", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "training", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Training", + "length": 0, + "no_copy": 0, + "options": "Training Event", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "training.end_time", + "fetch_if_empty": 0, + "fieldname": "training_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Training Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-22 12:48:56.925419", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Training", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_training/employee_training.py b/hrms/hr/doctype/employee_training/employee_training.py new file mode 100644 index 0000000..cd92dd6 --- /dev/null +++ b/hrms/hr/doctype/employee_training/employee_training.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeTraining(Document): + pass diff --git a/hrms/hr/doctype/employee_transfer/__init__.py b/hrms/hr/doctype/employee_transfer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employee_transfer/employee_transfer.js b/hrms/hr/doctype/employee_transfer/employee_transfer.js new file mode 100644 index 0000000..762672b --- /dev/null +++ b/hrms/hr/doctype/employee_transfer/employee_transfer.js @@ -0,0 +1,10 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +{% include 'hrms/hr/employee_property_update.js' %} + +frappe.ui.form.on('Employee Transfer', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/employee_transfer/employee_transfer.json b/hrms/hr/doctype/employee_transfer/employee_transfer.json new file mode 100644 index 0000000..d66ef64 --- /dev/null +++ b/hrms/hr/doctype/employee_transfer/employee_transfer.json @@ -0,0 +1,528 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "HR-EMP-TRN-.YYYY.-.#####", + "beta": 0, + "creation": "2018-04-13 18:20:01.603830", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee Name", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "transfer_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Transfer Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "new_company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "New Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "default": "", + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "details_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee Transfer Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "transfer_details", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee Transfer Detail", + "length": 0, + "no_copy": 0, + "options": "Employee Property History", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reallocate_leaves", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Re-allocate Leaves", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "create_new_employee_id", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Create New Employee Id", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "new_employee_id", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "New Employee ID", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Employee Transfer", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-08-21 16:15:36.876312", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Transfer", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employee_transfer/employee_transfer.py b/hrms/hr/doctype/employee_transfer/employee_transfer.py new file mode 100644 index 0000000..2cdb492 --- /dev/null +++ b/hrms/hr/doctype/employee_transfer/employee_transfer.py @@ -0,0 +1,76 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate + +from hrms.hr.utils import update_employee_work_history + + +class EmployeeTransfer(Document): + def before_submit(self): + if getdate(self.transfer_date) > getdate(): + frappe.throw( + _("Employee Transfer cannot be submitted before Transfer Date"), + frappe.DocstatusTransitionError, + ) + + def on_submit(self): + employee = frappe.get_doc("Employee", self.employee) + if self.create_new_employee_id: + new_employee = frappe.copy_doc(employee) + new_employee.name = None + new_employee.employee_number = None + new_employee = update_employee_work_history( + new_employee, self.transfer_details, date=self.transfer_date + ) + if self.new_company and self.company != self.new_company: + new_employee.internal_work_history = [] + new_employee.date_of_joining = self.transfer_date + new_employee.company = self.new_company + # move user_id to new employee before insert + if employee.user_id and not self.validate_user_in_details(): + new_employee.user_id = employee.user_id + employee.db_set("user_id", "") + new_employee.insert() + self.db_set("new_employee_id", new_employee.name) + # relieve the old employee + employee.db_set("relieving_date", self.transfer_date) + employee.db_set("status", "Left") + else: + employee = update_employee_work_history( + employee, self.transfer_details, date=self.transfer_date + ) + if self.new_company and self.company != self.new_company: + employee.company = self.new_company + employee.date_of_joining = self.transfer_date + employee.save() + + def on_cancel(self): + employee = frappe.get_doc("Employee", self.employee) + if self.create_new_employee_id: + if self.new_employee_id: + frappe.throw( + _("Please delete the Employee {0} to cancel this document").format( + "{0}".format(self.new_employee_id) + ) + ) + # mark the employee as active + employee.status = "Active" + employee.relieving_date = "" + else: + employee = update_employee_work_history( + employee, self.transfer_details, date=self.transfer_date, cancel=True + ) + if self.new_company != self.company: + employee.company = self.company + employee.save() + + def validate_user_in_details(self): + for item in self.transfer_details: + if item.fieldname == "user_id" and item.new != item.current: + return True + return False diff --git a/hrms/hr/doctype/employee_transfer/test_employee_transfer.py b/hrms/hr/doctype/employee_transfer/test_employee_transfer.py new file mode 100644 index 0000000..e2707cf --- /dev/null +++ b/hrms/hr/doctype/employee_transfer/test_employee_transfer.py @@ -0,0 +1,141 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + + +class TestEmployeeTransfer(unittest.TestCase): + def setUp(self): + create_company() + + def tearDown(self): + frappe.db.rollback() + + def test_submit_before_transfer_date(self): + make_employee("employee2@transfers.com") + + transfer_obj = frappe.get_doc( + { + "doctype": "Employee Transfer", + "employee": frappe.get_value("Employee", {"user_id": "employee2@transfers.com"}, "name"), + "transfer_details": [ + { + "property": "Designation", + "current": "Software Developer", + "new": "Project Manager", + "fieldname": "designation", + } + ], + } + ) + transfer_obj.transfer_date = add_days(getdate(), 1) + transfer_obj.save() + self.assertRaises(frappe.DocstatusTransitionError, transfer_obj.submit) + transfer = frappe.get_doc("Employee Transfer", transfer_obj.name) + transfer.transfer_date = getdate() + transfer.submit() + self.assertEqual(transfer.docstatus, 1) + + def test_new_employee_creation(self): + make_employee("employee3@transfers.com") + + transfer = frappe.get_doc( + { + "doctype": "Employee Transfer", + "employee": frappe.get_value("Employee", {"user_id": "employee3@transfers.com"}, "name"), + "create_new_employee_id": 1, + "transfer_date": getdate(), + "transfer_details": [ + { + "property": "Designation", + "current": "Software Developer", + "new": "Project Manager", + "fieldname": "designation", + } + ], + } + ).insert() + transfer.submit() + self.assertTrue(transfer.new_employee_id) + self.assertEqual(frappe.get_value("Employee", transfer.new_employee_id, "status"), "Active") + self.assertEqual(frappe.get_value("Employee", transfer.employee, "status"), "Left") + + def test_employee_history(self): + employee = make_employee( + "employee4@transfers.com", + company="Test Company", + date_of_birth=getdate("30-09-1980"), + date_of_joining=getdate("01-10-2021"), + department="Accounts - TC", + designation="Accountant", + ) + transfer = create_employee_transfer(employee) + + count = 0 + department = ["Accounts - TC", "Management - TC"] + designation = ["Accountant", "Manager"] + dt = [getdate("01-10-2021"), getdate()] + to_date = [add_days(dt[1], -1), None] + + employee = frappe.get_doc("Employee", employee) + for data in employee.internal_work_history: + self.assertEqual(data.department, department[count]) + self.assertEqual(data.designation, designation[count]) + self.assertEqual(data.from_date, dt[count]) + self.assertEqual(data.to_date, to_date[count]) + count = count + 1 + + transfer.cancel() + employee.reload() + + for data in employee.internal_work_history: + self.assertEqual(data.designation, designation[0]) + self.assertEqual(data.department, department[0]) + self.assertEqual(data.from_date, dt[0]) + self.assertEqual(data.to_date, None) + + +def create_company(): + if not frappe.db.exists("Company", "Test Company"): + frappe.get_doc( + { + "doctype": "Company", + "company_name": "Test Company", + "default_currency": "INR", + "country": "India", + } + ).insert() + + +def create_employee_transfer(employee): + doc = frappe.get_doc( + { + "doctype": "Employee Transfer", + "employee": employee, + "transfer_date": getdate(), + "transfer_details": [ + { + "property": "Designation", + "current": "Accountant", + "new": "Manager", + "fieldname": "designation", + }, + { + "property": "Department", + "current": "Accounts - TC", + "new": "Management - TC", + "fieldname": "department", + }, + ], + } + ) + + doc.save() + doc.submit() + + return doc diff --git a/hrms/hr/doctype/employment_type/README.md b/hrms/hr/doctype/employment_type/README.md new file mode 100644 index 0000000..19de703 --- /dev/null +++ b/hrms/hr/doctype/employment_type/README.md @@ -0,0 +1,3 @@ +Type of employment. + +e.g. Permanent, Probation, Intern etc. \ No newline at end of file diff --git a/hrms/hr/doctype/employment_type/__init__.py b/hrms/hr/doctype/employment_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/employment_type/employment_type.json b/hrms/hr/doctype/employment_type/employment_type.json new file mode 100644 index 0000000..6b0b505 --- /dev/null +++ b/hrms/hr/doctype/employment_type/employment_type.json @@ -0,0 +1,113 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:employee_type_name", + "beta": 0, + "creation": "2013-01-10 16:34:14", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_type_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employment Type", + "length": 0, + "no_copy": 0, + "oldfieldname": "employee_type_name", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-flag", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-08-29 06:37:02.041245", + "modified_by": "Administrator", + "module": "HR", + "name": "Employment Type", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 1, + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/employment_type/employment_type.py b/hrms/hr/doctype/employment_type/employment_type.py new file mode 100644 index 0000000..b2262c0 --- /dev/null +++ b/hrms/hr/doctype/employment_type/employment_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.document import Document + + +class EmploymentType(Document): + pass diff --git a/hrms/hr/doctype/employment_type/test_employment_type.py b/hrms/hr/doctype/employment_type/test_employment_type.py new file mode 100644 index 0000000..fdf6965 --- /dev/null +++ b/hrms/hr/doctype/employment_type/test_employment_type.py @@ -0,0 +1,6 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +test_records = frappe.get_test_records("Employment Type") diff --git a/hrms/hr/doctype/employment_type/test_records.json b/hrms/hr/doctype/employment_type/test_records.json new file mode 100644 index 0000000..fa57179 --- /dev/null +++ b/hrms/hr/doctype/employment_type/test_records.json @@ -0,0 +1,6 @@ +[ + { + "doctype": "Employment Type", + "employee_type_name": "_Test Employment Type" + } +] \ No newline at end of file diff --git a/hrms/hr/doctype/exit_interview/__init__.py b/hrms/hr/doctype/exit_interview/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/exit_interview/exit_interview.js b/hrms/hr/doctype/exit_interview/exit_interview.js new file mode 100644 index 0000000..c4fefd0 --- /dev/null +++ b/hrms/hr/doctype/exit_interview/exit_interview.js @@ -0,0 +1,38 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Exit Interview', { + refresh: function(frm) { + if (!frm.doc.__islocal && !frm.doc.questionnaire_email_sent && frappe.boot.user.can_write.includes('Exit Interview')) { + frm.add_custom_button(__('Send Exit Questionnaire'), function () { + frm.trigger('send_exit_questionnaire'); + }); + } + }, + + employee: function(frm) { + frappe.db.get_value('Employee', frm.doc.employee, 'relieving_date', (message) => { + if (!message.relieving_date) { + frappe.throw({ + message: __('Please set the relieving date for employee {0}', + ['' + frm.doc.employee + '']), + title: __('Relieving Date Missing') + }); + } + }); + }, + + send_exit_questionnaire: function(frm) { + frappe.call({ + method: 'hrms.hr.doctype.exit_interview.exit_interview.send_exit_questionnaire', + args: { + 'interviews': [frm.doc] + }, + callback: function(r) { + if (!r.exc) { + frm.refresh_field('questionnaire_email_sent'); + } + } + }); + } +}); diff --git a/hrms/hr/doctype/exit_interview/exit_interview.json b/hrms/hr/doctype/exit_interview/exit_interview.json new file mode 100644 index 0000000..989a1b8 --- /dev/null +++ b/hrms/hr/doctype/exit_interview/exit_interview.json @@ -0,0 +1,246 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2021-12-05 13:56:36.241690", + "doctype": "DocType", + "editable_grid": 1, + "email_append_to": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "email", + "column_break_5", + "company", + "status", + "date", + "employee_details_section", + "department", + "designation", + "reports_to", + "column_break_9", + "date_of_joining", + "relieving_date", + "exit_questionnaire_section", + "ref_doctype", + "questionnaire_email_sent", + "column_break_10", + "reference_document_name", + "interview_summary_section", + "interviewers", + "interview_summary", + "employee_status_section", + "employee_status", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.relieving_date", + "fieldname": "relieving_date", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Relieving Date", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Date", + "mandatory_depends_on": "eval:doc.status==='Scheduled';" + }, + { + "fieldname": "exit_questionnaire_section", + "fieldtype": "Section Break", + "label": "Exit Questionnaire" + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType" + }, + { + "fieldname": "reference_document_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Document Name", + "options": "ref_doctype" + }, + { + "fieldname": "interview_summary_section", + "fieldtype": "Section Break", + "label": "Interview Details" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "interviewers", + "fieldtype": "Table MultiSelect", + "label": "Interviewers", + "mandatory_depends_on": "eval:doc.status==='Scheduled';", + "options": "Interviewer" + }, + { + "fetch_from": "employee.date_of_joining", + "fieldname": "date_of_joining", + "fieldtype": "Date", + "label": "Date of Joining", + "read_only": 1 + }, + { + "fetch_from": "employee.reports_to", + "fieldname": "reports_to", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Reports To", + "options": "Employee", + "read_only": 1 + }, + { + "fieldname": "employee_details_section", + "fieldtype": "Section Break", + "label": "Employee Details" + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "HR-EXIT-INT-" + }, + { + "default": "0", + "fieldname": "questionnaire_email_sent", + "fieldtype": "Check", + "in_standard_filter": 1, + "label": "Questionnaire Email Sent", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email ID", + "options": "Email", + "read_only": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Pending\nScheduled\nCompleted\nCancelled", + "reqd": 1 + }, + { + "fieldname": "employee_status_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "employee_status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Final Decision", + "mandatory_depends_on": "eval:doc.status==='Completed';", + "options": "\nEmployee Retained\nExit Confirmed" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Exit Interview", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "interview_summary", + "fieldtype": "Text Editor", + "label": "Interview Summary" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-12-07 23:39:22.645401", + "modified_by": "Administrator", + "module": "HR", + "name": "Exit Interview", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sender_field": "email", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/exit_interview/exit_interview.py b/hrms/hr/doctype/exit_interview/exit_interview.py new file mode 100644 index 0000000..d6b71e0 --- /dev/null +++ b/hrms/hr/doctype/exit_interview/exit_interview.py @@ -0,0 +1,146 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import get_link_to_form + +from erpnext.setup.doctype.employee.employee import get_employee_email + + +class ExitInterview(Document): + def validate(self): + self.validate_relieving_date() + self.validate_duplicate_interview() + self.set_employee_email() + + def validate_relieving_date(self): + if not frappe.db.get_value("Employee", self.employee, "relieving_date"): + frappe.throw( + _("Please set the relieving date for employee {0}").format( + get_link_to_form("Employee", self.employee) + ), + title=_("Relieving Date Missing"), + ) + + def validate_duplicate_interview(self): + doc = frappe.db.exists( + "Exit Interview", {"employee": self.employee, "name": ("!=", self.name), "docstatus": ("!=", 2)} + ) + if doc: + frappe.throw( + _("Exit Interview {0} already exists for Employee: {1}").format( + get_link_to_form("Exit Interview", doc), frappe.bold(self.employee) + ), + frappe.DuplicateEntryError, + ) + + def set_employee_email(self): + employee = frappe.get_doc("Employee", self.employee) + self.email = get_employee_email(employee) + + def on_submit(self): + if self.status != "Completed": + frappe.throw(_("Only Completed documents can be submitted")) + + self.update_interview_date_in_employee() + + def on_cancel(self): + self.update_interview_date_in_employee() + self.db_set("status", "Cancelled") + + def update_interview_date_in_employee(self): + if self.docstatus == 1: + frappe.db.set_value("Employee", self.employee, "held_on", self.date) + elif self.docstatus == 2: + frappe.db.set_value("Employee", self.employee, "held_on", None) + + +@frappe.whitelist() +def send_exit_questionnaire(interviews): + interviews = get_interviews(interviews) + validate_questionnaire_settings() + + email_success = [] + email_failure = [] + + for exit_interview in interviews: + interview = frappe.get_doc("Exit Interview", exit_interview.get("name")) + if interview.get("questionnaire_email_sent"): + continue + + employee = frappe.get_doc("Employee", interview.employee) + email = get_employee_email(employee) + + context = interview.as_dict() + context.update(employee.as_dict()) + template_name = frappe.db.get_single_value( + "HR Settings", "exit_questionnaire_notification_template" + ) + template = frappe.get_doc("Email Template", template_name) + + if email: + frappe.sendmail( + recipients=email, + subject=template.subject, + message=frappe.render_template(template.response, context), + reference_doctype=interview.doctype, + reference_name=interview.name, + ) + interview.db_set("questionnaire_email_sent", 1) + interview.notify_update() + email_success.append(email) + else: + email_failure.append(get_link_to_form("Employee", employee.name)) + + show_email_summary(email_success, email_failure) + + +def get_interviews(interviews): + import json + + if isinstance(interviews, str): + interviews = json.loads(interviews) + + if not len(interviews): + frappe.throw(_("Atleast one interview has to be selected.")) + + return interviews + + +def validate_questionnaire_settings(): + settings = frappe.db.get_value( + "HR Settings", + "HR Settings", + ["exit_questionnaire_web_form", "exit_questionnaire_notification_template"], + as_dict=True, + ) + + if ( + not settings.exit_questionnaire_web_form or not settings.exit_questionnaire_notification_template + ): + frappe.throw( + _("Please set {0} and {1} in {2}.").format( + frappe.bold("Exit Questionnaire Web Form"), + frappe.bold("Notification Template"), + get_link_to_form("HR Settings", "HR Settings"), + ), + title=_("Settings Missing"), + ) + + +def show_email_summary(email_success, email_failure): + message = "" + if email_success: + message += _("{0}: {1}").format(frappe.bold("Sent Successfully"), ", ".join(email_success)) + if message and email_failure: + message += "

" + if email_failure: + message += _("{0} due to missing email information for employee(s): {1}").format( + frappe.bold("Sending Failed"), ", ".join(email_failure) + ) + + frappe.msgprint( + message, title=_("Exit Questionnaire"), indicator="blue", is_minimizable=True, wide=True + ) diff --git a/hrms/hr/doctype/exit_interview/exit_interview_list.js b/hrms/hr/doctype/exit_interview/exit_interview_list.js new file mode 100644 index 0000000..32d3668 --- /dev/null +++ b/hrms/hr/doctype/exit_interview/exit_interview_list.js @@ -0,0 +1,27 @@ +frappe.listview_settings['Exit Interview'] = { + has_indicator_for_draft: 1, + get_indicator: function(doc) { + let status_color = { + 'Pending': 'orange', + 'Scheduled': 'yellow', + 'Completed': 'green', + 'Cancelled': 'red', + }; + return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status]; + }, + + onload: function(listview) { + if (frappe.boot.user.can_write.includes('Exit Interview')) { + listview.page.add_action_item(__('Send Exit Questionnaires'), function() { + const interviews = listview.get_checked_items(); + frappe.call({ + method: 'hrms.hr.doctype.exit_interview.exit_interview.send_exit_questionnaire', + freeze: true, + args: { + 'interviews': interviews + } + }); + }); + } + } +}; diff --git a/hrms/hr/doctype/exit_interview/exit_questionnaire_notification_template.html b/hrms/hr/doctype/exit_interview/exit_questionnaire_notification_template.html new file mode 100644 index 0000000..0317b1a --- /dev/null +++ b/hrms/hr/doctype/exit_interview/exit_questionnaire_notification_template.html @@ -0,0 +1,16 @@ +

Exit Questionnaire

+
+ +

+ Dear {{ employee_name }}, +

+ + Thank you for the contribution you have made during your time at {{ company }}. We value your opinion and welcome the feedback on your experience working with us. + Request you to take out a few minutes to fill up this Exit Questionnaire. + + {% set web_form = frappe.db.get_value('HR Settings', 'HR Settings', 'exit_questionnaire_web_form') %} + {% set web_form_link = frappe.utils.get_url(uri=frappe.db.get_value('Web Form', web_form, 'route')) %} + +

+ {{ _('Submit Now') }} +

diff --git a/hrms/hr/doctype/exit_interview/test_exit_interview.py b/hrms/hr/doctype/exit_interview/test_exit_interview.py new file mode 100644 index 0000000..2da8598 --- /dev/null +++ b/hrms/hr/doctype/exit_interview/test_exit_interview.py @@ -0,0 +1,127 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import os +import unittest + +import frappe +from frappe import _ +from frappe.core.doctype.user_permission.test_user_permission import create_user +from frappe.tests.test_webform import create_custom_doctype, create_webform +from frappe.utils import getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.exit_interview.exit_interview import send_exit_questionnaire + + +class TestExitInterview(unittest.TestCase): + def setUp(self): + frappe.db.sql("delete from `tabExit Interview`") + + def test_duplicate_interview(self): + employee = make_employee("employeeexitint1@example.com") + frappe.db.set_value("Employee", employee, "relieving_date", getdate()) + interview = create_exit_interview(employee) + + doc = frappe.copy_doc(interview) + self.assertRaises(frappe.DuplicateEntryError, doc.save) + + def test_relieving_date_validation(self): + employee = make_employee("employeeexitint2@example.com") + # unset relieving date + frappe.db.set_value("Employee", employee, "relieving_date", None) + + interview = create_exit_interview(employee, save=False) + self.assertRaises(frappe.ValidationError, interview.save) + + # set relieving date + frappe.db.set_value("Employee", employee, "relieving_date", getdate()) + interview = create_exit_interview(employee) + self.assertTrue(interview.name) + + def test_interview_date_updated_in_employee_master(self): + employee = make_employee("employeeexit3@example.com") + frappe.db.set_value("Employee", employee, "relieving_date", getdate()) + + interview = create_exit_interview(employee) + interview.status = "Completed" + interview.employee_status = "Exit Confirmed" + + # exit interview date updated on submit + interview.submit() + self.assertEqual(frappe.db.get_value("Employee", employee, "held_on"), interview.date) + + # exit interview reset on cancel + interview.reload() + interview.cancel() + self.assertEqual(frappe.db.get_value("Employee", employee, "held_on"), None) + + def test_send_exit_questionnaire(self): + create_custom_doctype() + create_webform() + template = create_notification_template() + + webform = frappe.db.get_all("Web Form", limit=1) + frappe.db.set_value( + "HR Settings", + "HR Settings", + { + "exit_questionnaire_web_form": webform[0].name, + "exit_questionnaire_notification_template": template, + }, + ) + + employee = make_employee("employeeexit3@example.com") + frappe.db.set_value("Employee", employee, "relieving_date", getdate()) + + interview = create_exit_interview(employee) + send_exit_questionnaire([interview]) + + email_queue = frappe.db.get_all("Email Queue", ["name", "message"], limit=1) + self.assertTrue("Subject: Exit Questionnaire Notification" in email_queue[0].message) + + def tearDown(self): + frappe.db.rollback() + + +def create_exit_interview(employee, save=True): + interviewer = create_user("test_exit_interviewer@example.com") + + doc = frappe.get_doc( + { + "doctype": "Exit Interview", + "employee": employee, + "company": "_Test Company", + "status": "Pending", + "date": getdate(), + "interviewers": [{"interviewer": interviewer.name}], + "interview_summary": "Test", + } + ) + + if save: + return doc.insert() + return doc + + +def create_notification_template(): + template = frappe.db.exists("Email Template", _("Exit Questionnaire Notification")) + if not template: + base_path = frappe.get_app_path("erpnext", "hr", "doctype") + response = frappe.read_file( + os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html") + ) + + template = frappe.get_doc( + { + "doctype": "Email Template", + "name": _("Exit Questionnaire Notification"), + "response": response, + "subject": _("Exit Questionnaire Notification"), + "owner": frappe.session.user, + } + ).insert(ignore_permissions=True) + template = template.name + + return template diff --git a/hrms/hr/doctype/expected_skill_set/__init__.py b/hrms/hr/doctype/expected_skill_set/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expected_skill_set/expected_skill_set.json b/hrms/hr/doctype/expected_skill_set/expected_skill_set.json new file mode 100644 index 0000000..899f5bd --- /dev/null +++ b/hrms/hr/doctype/expected_skill_set/expected_skill_set.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2021-04-12 13:05:06.741330", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "skill", + "description" + ], + "fields": [ + { + "fieldname": "skill", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Skill", + "options": "Skill", + "reqd": 1 + }, + { + "fetch_from": "skill.description", + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-12 14:26:33.062549", + "modified_by": "Administrator", + "module": "HR", + "name": "Expected Skill Set", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/expected_skill_set/expected_skill_set.py b/hrms/hr/doctype/expected_skill_set/expected_skill_set.py new file mode 100644 index 0000000..0062ba9 --- /dev/null +++ b/hrms/hr/doctype/expected_skill_set/expected_skill_set.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class ExpectedSkillSet(Document): + pass diff --git a/hrms/hr/doctype/expense_claim/README.md b/hrms/hr/doctype/expense_claim/README.md new file mode 100644 index 0000000..489029c --- /dev/null +++ b/hrms/hr/doctype/expense_claim/README.md @@ -0,0 +1 @@ +Amount claimed by Employee for expense made by the Employee on organization's behalf. \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim/__init__.py b/hrms/hr/doctype/expense_claim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expense_claim/expense_claim.js b/hrms/hr/doctype/expense_claim/expense_claim.js new file mode 100644 index 0000000..166c7fc --- /dev/null +++ b/hrms/hr/doctype/expense_claim/expense_claim.js @@ -0,0 +1,469 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.provide("hrms.hr"); +frappe.provide("erpnext.accounts.dimensions"); + +frappe.ui.form.on('Expense Claim', { + onload: function(frm) { + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + var expenses = frm.doc.expenses; + for (var i = 0; i < expenses.length; i++) { + var expense = expenses[i]; + if (!expense.expense_type) { + continue; + } + frappe.call({ + method: "hrms.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center", + args: { + "expense_claim_type": expense.expense_type, + "company": frm.doc.company + }, + callback: function(r) { + if (r.message) { + expense.default_account = r.message.account; + expense.cost_center = r.message.cost_center; + } + } + }); + } + }, +}); + +frappe.ui.form.on('Expense Claim Detail', { + expense_type: function(frm, cdt, cdn) { + var d = locals[cdt][cdn]; + if (!frm.doc.company) { + d.expense_type = ""; + frappe.msgprint(__("Please set the Company")); + this.frm.refresh_fields(); + return; + } + + if(!d.expense_type) { + return; + } + return frappe.call({ + method: "hrms.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center", + args: { + "expense_claim_type": d.expense_type, + "company": frm.doc.company + }, + callback: function(r) { + if (r.message) { + d.default_account = r.message.account; + d.cost_center = r.message.cost_center; + } + } + }); + } +}); + +cur_frm.add_fetch('employee', 'company', 'company'); +cur_frm.add_fetch('employee','employee_name','employee_name'); +cur_frm.add_fetch('expense_type','description','description'); + +cur_frm.cscript.onload = function(doc) { + if (doc.__islocal) { + cur_frm.set_value("posting_date", frappe.datetime.get_today()); + cur_frm.cscript.clear_sanctioned(doc); + } +}; + +cur_frm.cscript.clear_sanctioned = function(doc) { + var val = doc.expenses || []; + for(var i = 0; i 0 && frappe.model.can_read(entry_doctype)) { + cur_frm.add_custom_button(__('Bank Entries'), function() { + frappe.route_options = { + party_type: "Employee", + party: doc.employee, + company: doc.company + }; + frappe.set_route("List", entry_doctype); + }, __("View")); + } + /* eslint-enable */ + } + } +}; + +cur_frm.cscript.set_help = function(doc) { + cur_frm.set_intro(""); + if(doc.__islocal && !in_list(frappe.user_roles, "HR User")) { + cur_frm.set_intro(__("Fill the form and save it")); + } +}; + +cur_frm.cscript.validate = function(doc) { + cur_frm.cscript.calculate_total(doc); +}; + +cur_frm.cscript.calculate_total = function(doc){ + doc.total_claimed_amount = 0; + doc.total_sanctioned_amount = 0; + $.each((doc.expenses || []), function(i, d) { + doc.total_claimed_amount += d.amount; + doc.total_sanctioned_amount += d.sanctioned_amount; + }); +}; + +cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){ + cur_frm.cscript.calculate_total(doc,cdt,cdn); +}; + +cur_frm.fields_dict['cost_center'].get_query = function(doc) { + return { + filters: { + "company": doc.company + } + } +}; + +erpnext.expense_claim = { + set_title: function(frm) { + if (!frm.doc.task) { + frm.set_value("title", frm.doc.employee_name); + } + else { + frm.set_value("title", frm.doc.employee_name + " for "+ frm.doc.task); + } + } +}; + +frappe.ui.form.on("Expense Claim", { + setup: function(frm) { + frm.add_fetch("company", "cost_center", "cost_center"); + frm.add_fetch("company", "default_expense_claim_payable_account", "payable_account"); + + frm.set_query("employee_advance", "advances", function() { + return { + filters: [ + ['docstatus', '=', 1], + ['employee', '=', frm.doc.employee], + ['paid_amount', '>', 0], + ['status', 'not in', ['Claimed', 'Returned', 'Partly Claimed and Returned']] + ] + }; + }); + + frm.set_query("expense_approver", function() { + return { + query: "hrms.hr.doctype.department_approver.department_approver.get_approvers", + filters: { + employee: frm.doc.employee, + doctype: frm.doc.doctype + } + }; + }); + + frm.set_query("account_head", "taxes", function() { + return { + filters: [ + ['company', '=', frm.doc.company], + ['account_type', 'in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]] + ] + }; + }); + + frm.set_query("payable_account", function() { + return { + filters: { + "report_type": "Balance Sheet", + "account_type": "Payable", + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + + frm.set_query("task", function() { + return { + filters: { + 'project': frm.doc.project + } + }; + }); + + frm.set_query("employee", function() { + return { + query: "erpnext.controllers.queries.employee_query" + }; + }); + }, + + onload: function(frm) { + if (frm.doc.docstatus == 0) { + return frappe.call({ + method: "hrms.hr.doctype.leave_application.leave_application.get_mandatory_approval", + args: { + doctype: frm.doc.doctype, + }, + callback: function(r) { + if (!r.exc && r.message) { + frm.toggle_reqd("expense_approver", true); + } + } + }); + } + }, + + refresh: function(frm) { + frm.trigger("toggle_fields"); + + if(frm.doc.docstatus > 0 && frm.doc.approval_status !== "Rejected") { + frm.add_custom_button(__('Accounting Ledger'), function() { + frappe.route_options = { + voucher_no: frm.doc.name, + company: frm.doc.company, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + group_by: '', + show_cancelled_entries: frm.doc.docstatus === 2 + }; + frappe.set_route("query-report", "General Ledger"); + }, __("View")); + } + + if ( + frm.doc.docstatus === 1 + && frm.doc.status !== "Paid" + && frappe.model.can_create("Payment Entry") + ) { + frm.add_custom_button(__('Payment'), + function() { frm.events.make_payment_entry(frm); }, __('Create')); + } + }, + + calculate_grand_total: function(frm) { + var grand_total = flt(frm.doc.total_sanctioned_amount) + flt(frm.doc.total_taxes_and_charges) - flt(frm.doc.total_advance_amount); + frm.set_value("grand_total", grand_total); + frm.refresh_fields(); + }, + + grand_total: function(frm) { + frm.trigger("update_employee_advance_claimed_amount"); + }, + + update_employee_advance_claimed_amount: function(frm) { + let amount_to_be_allocated = frm.doc.grand_total; + $.each(frm.doc.advances || [], function(i, advance){ + if (amount_to_be_allocated >= advance.unclaimed_amount){ + advance.allocated_amount = frm.doc.advances[i].unclaimed_amount; + amount_to_be_allocated -= advance.allocated_amount; + } else { + advance.allocated_amount = amount_to_be_allocated; + amount_to_be_allocated = 0; + } + frm.refresh_field("advances"); + }); + }, + + make_payment_entry: function(frm) { + let method = "hrms.overrides.employee_payment_entry.get_payment_entry_for_employee"; + if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { + method = "hrms.hr.doctype.expense_claim.expense_claim.make_bank_entry"; + } + return frappe.call({ + method: method, + args: { + "dt": frm.doc.doctype, + "dn": frm.doc.name + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, + + is_paid: function(frm) { + frm.trigger("toggle_fields"); + }, + + toggle_fields: function(frm) { + frm.toggle_reqd("mode_of_payment", frm.doc.is_paid); + }, + + employee_name: function(frm) { + erpnext.expense_claim.set_title(frm); + }, + + task: function(frm) { + erpnext.expense_claim.set_title(frm); + }, + + employee: function(frm) { + frm.events.get_advances(frm); + }, + + cost_center: function(frm) { + frm.events.set_child_cost_center(frm); + }, + + validate: function(frm) { + frm.events.set_child_cost_center(frm); + }, + + set_child_cost_center: function(frm){ + (frm.doc.expenses || []).forEach(function(d) { + if (!d.cost_center){ + d.cost_center = frm.doc.cost_center; + } + }); + }, + get_taxes: function(frm) { + if(frm.doc.taxes) { + frappe.call({ + method: "calculate_taxes", + doc: frm.doc, + callback: () => { + refresh_field("taxes"); + frm.trigger("update_employee_advance_claimed_amount"); + } + }); + } + }, + + get_advances: function(frm) { + frappe.model.clear_table(frm.doc, "advances"); + if (frm.doc.employee) { + return frappe.call({ + method: "hrms.hr.doctype.expense_claim.expense_claim.get_advances", + args: { + employee: frm.doc.employee + }, + callback: function(r, rt) { + + if(r.message) { + $.each(r.message, function(i, d) { + var row = frappe.model.add_child(frm.doc, "Expense Claim Advance", "advances"); + row.employee_advance = d.name; + row.posting_date = d.posting_date; + row.advance_account = d.advance_account; + row.advance_paid = d.paid_amount; + row.unclaimed_amount = flt(d.paid_amount) - flt(d.claimed_amount); + row.allocated_amount = 0; + }); + refresh_field("advances"); + } + } + }); + } + } +}); + +frappe.ui.form.on("Expense Claim Detail", { + amount: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); + }, + + sanctioned_amount: function(frm, cdt, cdn) { + cur_frm.cscript.calculate_total(frm.doc, cdt, cdn); + frm.trigger("get_taxes"); + frm.trigger("calculate_grand_total"); + }, + + cost_center: function(frm, cdt, cdn) { + erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "expenses", "cost_center"); + } +}); + +frappe.ui.form.on("Expense Claim Advance", { + employee_advance: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(!frm.doc.employee){ + frappe.msgprint(__('Select an employee to get the employee advance.')); + frm.doc.advances = []; + refresh_field("advances"); + } + else { + return frappe.call({ + method: "hrms.hr.doctype.expense_claim.expense_claim.get_advances", + args: { + employee: frm.doc.employee, + advance_id: child.employee_advance + }, + callback: function(r, rt) { + if(r.message) { + child.employee_advance = r.message[0].name; + child.posting_date = r.message[0].posting_date; + child.advance_account = r.message[0].advance_account; + child.advance_paid = r.message[0].paid_amount; + child.unclaimed_amount = flt(r.message[0].paid_amount) - flt(r.message[0].claimed_amount); + child.allocated_amount = flt(r.message[0].paid_amount) - flt(r.message[0].claimed_amount); + frm.trigger('calculate_grand_total'); + refresh_field("advances"); + } + } + }); + } + } +}); + +frappe.ui.form.on("Expense Taxes and Charges", { + account_head: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.account_head && !child.description) { + // set description from account head + child.description = child.account_head.split(' - ').slice(0, -1).join(' - '); + refresh_field("taxes"); + } + }, + + calculate_total_tax: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + child.total = flt(frm.doc.total_sanctioned_amount) + flt(child.tax_amount); + frm.trigger("calculate_tax_amount", cdt, cdn); + }, + + calculate_tax_amount: function(frm) { + frm.doc.total_taxes_and_charges = 0; + (frm.doc.taxes || []).forEach(function(d) { + frm.doc.total_taxes_and_charges += d.tax_amount; + }); + frm.trigger("calculate_grand_total"); + }, + + rate: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(!child.amount) { + child.tax_amount = flt(frm.doc.total_sanctioned_amount) * (flt(child.rate)/100); + } + frm.trigger("calculate_total_tax", cdt, cdn); + }, + + tax_amount: function(frm, cdt, cdn) { + frm.trigger("calculate_total_tax", cdt, cdn); + } +}); diff --git a/hrms/hr/doctype/expense_claim/expense_claim.json b/hrms/hr/doctype/expense_claim/expense_claim.json new file mode 100644 index 0000000..45b78bf --- /dev/null +++ b/hrms/hr/doctype/expense_claim/expense_claim.json @@ -0,0 +1,450 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-01-10 16:34:14", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "department", + "column_break_5", + "expense_approver", + "approval_status", + "delivery_trip", + "is_paid", + "expense_details", + "expenses", + "sb1", + "taxes", + "transactions_section", + "total_sanctioned_amount", + "total_taxes_and_charges", + "total_advance_amount", + "column_break_17", + "grand_total", + "total_claimed_amount", + "total_amount_reimbursed", + "section_break_16", + "posting_date", + "vehicle_log", + "task", + "cb1", + "remark", + "title", + "email_id", + "accounting_details", + "company", + "mode_of_payment", + "clearance_date", + "column_break_24", + "payable_account", + "accounting_dimensions_section", + "project", + "dimension_col_break", + "cost_center", + "more_details", + "status", + "amended_from", + "advance_payments", + "advances" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-EXP-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "From Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Employee Name", + "oldfieldname": "employee_name", + "oldfieldtype": "Data", + "read_only": 1, + "width": "150px" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "expense_approver", + "fieldtype": "Link", + "label": "Expense Approver", + "options": "User" + }, + { + "default": "Draft", + "fieldname": "approval_status", + "fieldtype": "Select", + "label": "Approval Status", + "no_copy": 1, + "options": "Draft\nApproved\nRejected", + "search_index": 1 + }, + { + "fieldname": "total_claimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Claimed Amount", + "no_copy": 1, + "oldfieldname": "total_claimed_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1, + "width": "160px" + }, + { + "fieldname": "total_sanctioned_amount", + "fieldtype": "Currency", + "label": "Total Sanctioned Amount", + "no_copy": 1, + "oldfieldname": "total_sanctioned_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1, + "width": "160px" + }, + { + "default": "0", + "depends_on": "eval:(doc.docstatus==0 || doc.is_paid)", + "fieldname": "is_paid", + "fieldtype": "Check", + "label": "Is Paid" + }, + { + "fieldname": "expense_details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "expenses", + "fieldtype": "Table", + "label": "Expenses", + "oldfieldname": "expense_voucher_details", + "oldfieldtype": "Table", + "options": "Expense Claim Detail", + "reqd": 1 + }, + { + "fieldname": "sb1", + "fieldtype": "Section Break", + "options": "Simple" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_standard_filter": 1, + "label": "Posting Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "reqd": 1 + }, + { + "fieldname": "vehicle_log", + "fieldtype": "Link", + "label": "Vehicle Log", + "options": "Vehicle Log", + "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Task", + "remember_last_selected_value": 1 + }, + { + "fieldname": "cb1", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_amount_reimbursed", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Amount Reimbursed", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "remark", + "fieldtype": "Small Text", + "label": "Remark", + "no_copy": 1, + "oldfieldname": "remark", + "oldfieldtype": "Small Text" + }, + { + "allow_on_submit": 1, + "default": "{employee_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1 + }, + { + "fieldname": "email_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Employees Email Id", + "oldfieldname": "email_id", + "oldfieldtype": "Data", + "print_hide": 1 + }, + { + "fieldname": "accounting_details", + "fieldtype": "Section Break", + "label": "Accounting Details" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "depends_on": "is_paid", + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, + { + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "payable_account", + "fieldtype": "Link", + "label": "Payable Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "collapsible": 1, + "fieldname": "more_details", + "fieldtype": "Section Break", + "label": "More Details" + }, + { + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "no_copy": 1, + "options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Expense Claim", + "print_hide": 1, + "read_only": 1, + "report_hide": 1, + "width": "160px" + }, + { + "fieldname": "advance_payments", + "fieldtype": "Section Break", + "label": "Advance Payments" + }, + { + "fieldname": "advances", + "fieldtype": "Table", + "label": "Advances", + "options": "Expense Claim Advance" + }, + { + "fieldname": "total_advance_amount", + "fieldtype": "Currency", + "label": "Total Advance Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Expense Taxes and Charges", + "options": "Expense Taxes and Charges" + }, + { + "fieldname": "section_break_16", + "fieldtype": "Section Break" + }, + { + "fieldname": "transactions_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "depends_on": "eval: doc.delivery_trip", + "fieldname": "delivery_trip", + "fieldtype": "Link", + "label": "Delivery Trip", + "options": "Delivery Trip" + } + ], + "icon": "fa fa-money", + "idx": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-11-22 16:26:57.787838", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim", + "name_case": "Title Case", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Expense Approver", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "title" +} \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim/expense_claim.py b/hrms/hr/doctype/expense_claim/expense_claim.py new file mode 100644 index 0000000..1a37397 --- /dev/null +++ b/hrms/hr/doctype/expense_claim/expense_claim.py @@ -0,0 +1,542 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.mapper import get_mapped_doc +from frappe.query_builder.functions import Sum +from frappe.utils import cstr, flt, get_link_to_form + +import erpnext +from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account +from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.controllers.accounts_controller import AccountsController + +from hrms.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee + + +class InvalidExpenseApproverError(frappe.ValidationError): + pass + + +class ExpenseApproverIdentityError(frappe.ValidationError): + pass + + +class ExpenseClaim(AccountsController): + def onload(self): + self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value( + "Accounts Settings", "make_payment_via_journal_entry" + ) + + def validate(self): + validate_active_employee(self.employee) + set_employee_name(self) + self.validate_sanctioned_amount() + self.calculate_total_amount() + self.validate_advances() + self.set_expense_account(validate=True) + self.set_payable_account() + self.set_cost_center() + self.calculate_taxes() + self.set_status() + if self.task and not self.project: + self.project = frappe.db.get_value("Task", self.task, "project") + + def set_status(self, update=False): + status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[cstr(self.docstatus or 0)] + + precision = self.precision("grand_total") + + if ( + # set as paid + self.is_paid + or ( + flt(self.total_sanctioned_amount > 0) + and ( + # grand total is reimbursed + ( + self.docstatus == 1 + and flt(self.grand_total, precision) == flt(self.total_amount_reimbursed, precision) + ) + # grand total (to be paid) is 0 since linked advances already cover the claimed amount + or (flt(self.grand_total, precision) == 0) + ) + ) + ) and self.approval_status == "Approved": + status = "Paid" + elif ( + flt(self.total_sanctioned_amount) > 0 + and self.docstatus == 1 + and self.approval_status == "Approved" + ): + status = "Unpaid" + elif self.docstatus == 1 and self.approval_status == "Rejected": + status = "Rejected" + + if update: + self.db_set("status", status) + else: + self.status = status + + def on_update(self): + share_doc_with_approver(self, self.expense_approver) + + def set_payable_account(self): + if not self.payable_account and not self.is_paid: + self.payable_account = frappe.get_cached_value( + "Company", self.company, "default_expense_claim_payable_account" + ) + + def set_cost_center(self): + if not self.cost_center: + self.cost_center = frappe.get_cached_value("Company", self.company, "cost_center") + + def on_submit(self): + if self.approval_status == "Draft": + frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'""")) + + self.update_task_and_project() + self.make_gl_entries() + + if self.is_paid: + update_reimbursed_amount(self, self.grand_total) + + self.set_status(update=True) + self.update_claimed_amount_in_employee_advance() + + def on_cancel(self): + self.update_task_and_project() + self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") + if self.payable_account: + self.make_gl_entries(cancel=True) + + if self.is_paid: + update_reimbursed_amount(self, -1 * self.grand_total) + + self.update_claimed_amount_in_employee_advance() + + def update_claimed_amount_in_employee_advance(self): + for d in self.get("advances"): + frappe.get_doc("Employee Advance", d.employee_advance).update_claimed_amount() + + def update_task_and_project(self): + if self.task: + task = frappe.get_doc("Task", self.task) + + ExpenseClaim = frappe.qb.DocType("Expense Claim") + task.total_expense_claim = ( + frappe.qb.from_(ExpenseClaim) + .select(Sum(ExpenseClaim.total_sanctioned_amount)) + .where( + (ExpenseClaim.docstatus == 1) + & (ExpenseClaim.project == self.project) + & (ExpenseClaim.task == self.task) + ) + ).run()[0][0] + + task.save() + elif self.project: + frappe.get_doc("Project", self.project).update_project() + + def make_gl_entries(self, cancel=False): + if flt(self.total_sanctioned_amount) > 0: + gl_entries = self.get_gl_entries() + make_gl_entries(gl_entries, cancel) + + def get_gl_entries(self): + gl_entry = [] + self.validate_account_details() + + # payable entry + if self.grand_total: + gl_entry.append( + self.get_gl_dict( + { + "account": self.payable_account, + "credit": self.grand_total, + "credit_in_account_currency": self.grand_total, + "against": ",".join([d.default_account for d in self.expenses]), + "party_type": "Employee", + "party": self.employee, + "against_voucher_type": self.doctype, + "against_voucher": self.name, + "cost_center": self.cost_center, + }, + item=self, + ) + ) + + # expense entries + for data in self.expenses: + gl_entry.append( + self.get_gl_dict( + { + "account": data.default_account, + "debit": data.sanctioned_amount, + "debit_in_account_currency": data.sanctioned_amount, + "against": self.employee, + "cost_center": data.cost_center or self.cost_center, + }, + item=data, + ) + ) + + for data in self.advances: + gl_entry.append( + self.get_gl_dict( + { + "account": data.advance_account, + "credit": data.allocated_amount, + "credit_in_account_currency": data.allocated_amount, + "against": ",".join([d.default_account for d in self.expenses]), + "party_type": "Employee", + "party": self.employee, + "against_voucher_type": "Employee Advance", + "against_voucher": data.employee_advance, + } + ) + ) + + self.add_tax_gl_entries(gl_entry) + + if self.is_paid and self.grand_total: + # payment entry + payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account") + gl_entry.append( + self.get_gl_dict( + { + "account": payment_account, + "credit": self.grand_total, + "credit_in_account_currency": self.grand_total, + "against": self.employee, + }, + item=self, + ) + ) + + gl_entry.append( + self.get_gl_dict( + { + "account": self.payable_account, + "party_type": "Employee", + "party": self.employee, + "against": payment_account, + "debit": self.grand_total, + "debit_in_account_currency": self.grand_total, + "against_voucher": self.name, + "against_voucher_type": self.doctype, + }, + item=self, + ) + ) + + return gl_entry + + def add_tax_gl_entries(self, gl_entries): + # tax table gl entries + for tax in self.get("taxes"): + gl_entries.append( + self.get_gl_dict( + { + "account": tax.account_head, + "debit": tax.tax_amount, + "debit_in_account_currency": tax.tax_amount, + "against": self.employee, + "cost_center": self.cost_center, + "against_voucher_type": self.doctype, + "against_voucher": self.name, + }, + item=tax, + ) + ) + + def validate_account_details(self): + for data in self.expenses: + if not data.cost_center: + frappe.throw( + _("Row {0}: {1} is required in the expenses table to book an expense claim.").format( + data.idx, frappe.bold("Cost Center") + ) + ) + + if self.is_paid: + if not self.mode_of_payment: + frappe.throw(_("Mode of payment is required to make a payment").format(self.employee)) + + def calculate_total_amount(self): + self.total_claimed_amount = 0 + self.total_sanctioned_amount = 0 + for d in self.get("expenses"): + if self.approval_status == "Rejected": + d.sanctioned_amount = 0.0 + + self.total_claimed_amount += flt(d.amount) + self.total_sanctioned_amount += flt(d.sanctioned_amount) + + @frappe.whitelist() + def calculate_taxes(self): + self.total_taxes_and_charges = 0 + for tax in self.taxes: + if tax.rate: + tax.tax_amount = flt(self.total_sanctioned_amount) * flt(tax.rate / 100) + + tax.total = flt(tax.tax_amount) + flt(self.total_sanctioned_amount) + self.total_taxes_and_charges += flt(tax.tax_amount) + + self.grand_total = ( + flt(self.total_sanctioned_amount) + + flt(self.total_taxes_and_charges) + - flt(self.total_advance_amount) + ) + + def validate_advances(self): + self.total_advance_amount = 0 + for d in self.get("advances"): + ref_doc = frappe.db.get_value( + "Employee Advance", + d.employee_advance, + ["posting_date", "paid_amount", "claimed_amount", "advance_account"], + as_dict=1, + ) + d.posting_date = ref_doc.posting_date + d.advance_account = ref_doc.advance_account + d.advance_paid = ref_doc.paid_amount + d.unclaimed_amount = flt(ref_doc.paid_amount) - flt(ref_doc.claimed_amount) + + if d.allocated_amount and flt(d.allocated_amount) > flt(d.unclaimed_amount): + frappe.throw( + _("Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}").format( + d.idx, d.allocated_amount, d.unclaimed_amount + ) + ) + + self.total_advance_amount += flt(d.allocated_amount) + + if self.total_advance_amount: + precision = self.precision("total_advance_amount") + amount_with_taxes = flt( + (flt(self.total_sanctioned_amount, precision) + flt(self.total_taxes_and_charges, precision)), + precision, + ) + + if flt(self.total_advance_amount, precision) > amount_with_taxes: + frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount")) + + def validate_sanctioned_amount(self): + for d in self.get("expenses"): + if flt(d.sanctioned_amount) > flt(d.amount): + frappe.throw( + _("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx) + ) + + def set_expense_account(self, validate=False): + for expense in self.expenses: + if not expense.default_account or not validate: + expense.default_account = get_expense_claim_account(expense.expense_type, self.company)[ + "account" + ] + + +def update_reimbursed_amount(doc, amount): + doc.total_amount_reimbursed += amount + frappe.db.set_value( + "Expense Claim", doc.name, "total_amount_reimbursed", doc.total_amount_reimbursed + ) + + doc.set_status() + frappe.db.set_value("Expense Claim", doc.name, "status", doc.status) + + +def get_outstanding_amount_for_claim(claim): + if isinstance(claim, str): + claim = frappe.db.get_value( + "Expense Claim", + claim, + ( + "total_sanctioned_amount", + "total_taxes_and_charges", + "total_amount_reimbursed", + "total_advance_amount", + ), + as_dict=True, + ) + + outstanding_amt = ( + flt(claim.total_sanctioned_amount) + + flt(claim.total_taxes_and_charges) + - flt(claim.total_amount_reimbursed) + - flt(claim.total_advance_amount) + ) + + return outstanding_amt + + +@frappe.whitelist() +def make_bank_entry(dt, dn): + from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account + + expense_claim = frappe.get_doc(dt, dn) + default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Bank") + if not default_bank_cash_account: + default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash") + + payable_amount = get_outstanding_amount_for_claim(expense_claim) + + je = frappe.new_doc("Journal Entry") + je.voucher_type = "Bank Entry" + je.company = expense_claim.company + je.remark = "Payment against Expense Claim: " + dn + + je.append( + "accounts", + { + "account": expense_claim.payable_account, + "debit_in_account_currency": payable_amount, + "reference_type": "Expense Claim", + "party_type": "Employee", + "party": expense_claim.employee, + "cost_center": erpnext.get_default_cost_center(expense_claim.company), + "reference_name": expense_claim.name, + }, + ) + + je.append( + "accounts", + { + "account": default_bank_cash_account.account, + "credit_in_account_currency": payable_amount, + "reference_type": "Expense Claim", + "reference_name": expense_claim.name, + "balance": default_bank_cash_account.balance, + "account_currency": default_bank_cash_account.account_currency, + "cost_center": erpnext.get_default_cost_center(expense_claim.company), + "account_type": default_bank_cash_account.account_type, + }, + ) + + return je.as_dict() + + +@frappe.whitelist() +def get_expense_claim_account_and_cost_center(expense_claim_type, company): + data = get_expense_claim_account(expense_claim_type, company) + cost_center = erpnext.get_default_cost_center(company) + + return {"account": data.get("account"), "cost_center": cost_center} + + +@frappe.whitelist() +def get_expense_claim_account(expense_claim_type, company): + account = frappe.db.get_value( + "Expense Claim Account", {"parent": expense_claim_type, "company": company}, "default_account" + ) + if not account: + frappe.throw( + _("Set the default account for the {0} {1}").format( + frappe.bold("Expense Claim Type"), get_link_to_form("Expense Claim Type", expense_claim_type) + ) + ) + + return {"account": account} + + +@frappe.whitelist() +def get_advances(employee, advance_id=None): + advance = frappe.qb.DocType("Employee Advance") + + query = frappe.qb.from_(advance).select( + advance.name, + advance.posting_date, + advance.paid_amount, + advance.claimed_amount, + advance.advance_account, + ) + + if not advance_id: + query = query.where( + (advance.docstatus == 1) + & (advance.employee == employee) + & (advance.paid_amount > 0) + & (advance.status.notin(["Claimed", "Returned", "Partly Claimed and Returned"])) + ) + else: + query = query.where(advance.name == advance_id) + + return query.run(as_dict=True) + + +@frappe.whitelist() +def get_expense_claim( + employee_name, company, employee_advance_name, posting_date, paid_amount, claimed_amount +): + default_payable_account = frappe.get_cached_value( + "Company", company, "default_expense_claim_payable_account" + ) + default_cost_center = frappe.get_cached_value("Company", company, "cost_center") + + expense_claim = frappe.new_doc("Expense Claim") + expense_claim.company = company + expense_claim.employee = employee_name + expense_claim.payable_account = default_payable_account + expense_claim.cost_center = default_cost_center + expense_claim.is_paid = 1 if flt(paid_amount) else 0 + expense_claim.append( + "advances", + { + "employee_advance": employee_advance_name, + "posting_date": posting_date, + "advance_paid": flt(paid_amount), + "unclaimed_amount": flt(paid_amount) - flt(claimed_amount), + "allocated_amount": flt(paid_amount) - flt(claimed_amount), + }, + ) + + return expense_claim + + +def update_payment_for_expense_claim(doc, method=None): + """ + Updates payment/reimbursed amount in Expense Claim + on Payment Entry/Journal Entry cancellation/submission + """ + if doc.doctype == "Payment Entry" and not (doc.payment_type == "Pay" and doc.party): + return + + payment_table = "accounts" if doc.doctype == "Journal Entry" else "references" + amount_field = "debit" if doc.doctype == "Journal Entry" else "allocated_amount" + doctype_field = "reference_type" if doc.doctype == "Journal Entry" else "reference_doctype" + + for d in doc.get(payment_table): + if d.get(doctype_field) == "Expense Claim" and d.reference_name: + expense_claim = frappe.get_doc("Expense Claim", d.reference_name) + if doc.docstatus == 2: + update_reimbursed_amount(expense_claim, -1 * d.get(amount_field)) + else: + update_reimbursed_amount(expense_claim, d.get(amount_field)) + + +def validate_expense_claim_in_jv(doc, method=None): + """Validates Expense Claim amount in Journal Entry""" + for d in doc.accounts: + if d.reference_type == "Expense Claim": + outstanding_amt = get_outstanding_amount_for_claim(d.reference_name) + if d.debit > outstanding_amt: + frappe.throw( + _( + "Row No {0}: Amount cannot be greater than the Outstanding Amount against Expense Claim {1}. Outstanding Amount is {2}" + ).format(d.idx, d.reference_name, outstanding_amt) + ) + + +@frappe.whitelist() +def make_expense_claim_for_delivery_trip(source_name, target_doc=None): + doc = get_mapped_doc( + "Delivery Trip", + source_name, + {"Delivery Trip": {"doctype": "Expense Claim", "field_map": {"name": "delivery_trip"}}}, + target_doc, + ) + + return doc diff --git a/hrms/hr/doctype/expense_claim/expense_claim_dashboard.py b/hrms/hr/doctype/expense_claim/expense_claim_dashboard.py new file mode 100644 index 0000000..8b1acc6 --- /dev/null +++ b/hrms/hr/doctype/expense_claim/expense_claim_dashboard.py @@ -0,0 +1,12 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "reference_name", + "internal_links": {"Employee Advance": ["advances", "employee_advance"]}, + "transactions": [ + {"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]}, + {"label": _("Reference"), "items": ["Employee Advance"]}, + ], + } diff --git a/hrms/hr/doctype/expense_claim/expense_claim_list.js b/hrms/hr/doctype/expense_claim/expense_claim_list.js new file mode 100644 index 0000000..9bafc18 --- /dev/null +++ b/hrms/hr/doctype/expense_claim/expense_claim_list.js @@ -0,0 +1,12 @@ +frappe.listview_settings['Expense Claim'] = { + add_fields: ["total_claimed_amount", "docstatus", "company"], + get_indicator: function(doc) { + if(doc.status == "Paid") { + return [__("Paid"), "green", "status,=,Paid"]; + }else if(doc.status == "Unpaid") { + return [__("Unpaid"), "orange", "status,=,Unpaid"]; + } else if(doc.status == "Rejected") { + return [__("Rejected"), "grey", "status,=,Rejected"]; + } + } +}; diff --git a/hrms/hr/doctype/expense_claim/test_expense_claim.py b/hrms/hr/doctype/expense_claim/test_expense_claim.py new file mode 100644 index 0000000..36d8747 --- /dev/null +++ b/hrms/hr/doctype/expense_claim/test_expense_claim.py @@ -0,0 +1,463 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import flt, nowdate, random_string + +from erpnext.accounts.doctype.account.test_account import create_account +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.expense_claim.expense_claim import ( + make_bank_entry, + make_expense_claim_for_delivery_trip, +) + +test_dependencies = ["Employee"] +company_name = "_Test Company 3" + + +class TestExpenseClaim(FrappeTestCase): + def setUp(self): + if not frappe.db.get_value("Cost Center", {"company": company_name}): + frappe.get_doc( + { + "doctype": "Cost Center", + "cost_center_name": "_Test Cost Center 3", + "parent_cost_center": "_Test Company 3 - _TC3", + "is_group": 0, + "company": company_name, + } + ).insert() + + def test_total_expense_claim_for_project(self): + frappe.db.sql("""delete from `tabTask`""") + frappe.db.sql("""delete from `tabProject`""") + frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") + + project = frappe.get_doc({"project_name": "_Test Project 1", "doctype": "Project"}) + project.save() + + task = frappe.get_doc( + dict(doctype="Task", subject="_Test Project Task 1", status="Open", project=project.name) + ).insert() + + task_name = task.name + payable_account = get_payable_account(company_name) + + make_expense_claim( + payable_account, 300, 200, company_name, "Travel Expenses - _TC3", project.name, task_name + ) + + self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) + + expense_claim2 = make_expense_claim( + payable_account, 600, 500, company_name, "Travel Expenses - _TC3", project.name, task_name + ) + + self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700) + + expense_claim2.cancel() + + self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) + + def test_expense_claim_status(self): + payable_account = get_payable_account(company_name) + expense_claim = make_expense_claim( + payable_account, 300, 200, company_name, "Travel Expenses - _TC3" + ) + + je = make_journal_entry(expense_claim) + + expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) + self.assertEqual(expense_claim.status, "Paid") + + je.cancel() + expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) + self.assertEqual(expense_claim.status, "Unpaid") + + # expense claim without any sanctioned amount should not have status as Paid + claim = make_expense_claim(payable_account, 1000, 0, "_Test Company", "Travel Expenses - _TC") + self.assertEqual(claim.total_sanctioned_amount, 0) + self.assertEqual(claim.status, "Submitted") + + # no gl entries created + gl_entry = frappe.get_all( + "GL Entry", {"voucher_type": "Expense Claim", "voucher_no": claim.name} + ) + self.assertEqual(len(gl_entry), 0) + + def test_expense_claim_against_fully_paid_advances(self): + from hrms.hr.doctype.employee_advance.test_employee_advance import ( + get_advances_for_claim, + make_employee_advance, + make_payment_entry, + ) + + frappe.db.delete("Employee Advance") + + payable_account = get_payable_account("_Test Company") + claim = make_expense_claim( + payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True + ) + + advance = make_employee_advance(claim.employee) + pe = make_payment_entry(advance) + pe.submit() + + # claim for already paid out advances + claim = get_advances_for_claim(claim, advance.name) + claim.save() + claim.submit() + + self.assertEqual(claim.grand_total, 0) + self.assertEqual(claim.status, "Paid") + + def test_advance_amount_allocation_against_claim_with_taxes(self): + from hrms.hr.doctype.employee_advance.test_employee_advance import ( + get_advances_for_claim, + make_employee_advance, + make_payment_entry, + ) + + frappe.db.delete("Employee Advance") + + payable_account = get_payable_account("_Test Company") + taxes = generate_taxes("_Test Company") + claim = make_expense_claim( + payable_account, + 700, + 700, + "_Test Company", + "Travel Expenses - _TC", + do_not_submit=True, + taxes=taxes, + ) + claim.save() + + advance = make_employee_advance(claim.employee) + pe = make_payment_entry(advance) + pe.submit() + + # claim for already paid out advances + claim = get_advances_for_claim(claim, advance.name, 763) + claim.save() + claim.submit() + + self.assertEqual(claim.grand_total, 0) + self.assertEqual(claim.status, "Paid") + + def test_expense_claim_partially_paid_via_advance(self): + from hrms.hr.doctype.employee_advance.test_employee_advance import ( + get_advances_for_claim, + make_employee_advance, + ) + from hrms.hr.doctype.employee_advance.test_employee_advance import ( + make_payment_entry as make_advance_payment, + ) + + frappe.db.delete("Employee Advance") + + payable_account = get_payable_account("_Test Company") + claim = make_expense_claim( + payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True + ) + + # link advance for partial amount + advance = make_employee_advance(claim.employee, {"advance_amount": 500}) + pe = make_advance_payment(advance) + pe.submit() + + claim = get_advances_for_claim(claim, advance.name) + claim.save() + claim.submit() + + self.assertEqual(claim.grand_total, 500) + self.assertEqual(claim.status, "Unpaid") + + # reimburse remaning amount + make_payment_entry(claim, payable_account, 500) + claim.reload() + + self.assertEqual(claim.total_amount_reimbursed, 500) + self.assertEqual(claim.status, "Paid") + + def test_expense_claim_gl_entry(self): + payable_account = get_payable_account(company_name) + taxes = generate_taxes() + expense_claim = make_expense_claim( + payable_account, + 300, + 200, + company_name, + "Travel Expenses - _TC3", + do_not_submit=True, + taxes=taxes, + ) + expense_claim.submit() + + gl_entries = frappe.db.sql( + """select account, debit, credit + from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s + order by account asc""", + expense_claim.name, + as_dict=1, + ) + + self.assertTrue(gl_entries) + + expected_values = dict( + (d[0], d) + for d in [ + ["Output Tax CGST - _TC3", 18.0, 0.0], + [payable_account, 0.0, 218.0], + ["Travel Expenses - _TC3", 200.0, 0.0], + ] + ) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account][0], gle.account) + self.assertEqual(expected_values[gle.account][1], gle.debit) + self.assertEqual(expected_values[gle.account][2], gle.credit) + + def test_rejected_expense_claim(self): + payable_account = get_payable_account(company_name) + expense_claim = frappe.get_doc( + { + "doctype": "Expense Claim", + "employee": "_T-Employee-00001", + "payable_account": payable_account, + "approval_status": "Rejected", + "expenses": [ + { + "expense_type": "Travel", + "default_account": "Travel Expenses - _TC3", + "amount": 300, + "sanctioned_amount": 200, + } + ], + } + ) + expense_claim.submit() + + self.assertEqual(expense_claim.status, "Rejected") + self.assertEqual(expense_claim.total_sanctioned_amount, 0.0) + + gl_entry = frappe.get_all( + "GL Entry", {"voucher_type": "Expense Claim", "voucher_no": expense_claim.name} + ) + self.assertEqual(len(gl_entry), 0) + + def test_expense_approver_perms(self): + user = "test_approver_perm_emp@example.com" + make_employee(user, "_Test Company") + + # check doc shared + payable_account = get_payable_account("_Test Company") + expense_claim = make_expense_claim( + payable_account, 300, 200, "_Test Company", "Travel Expenses - _TC", do_not_submit=True + ) + expense_claim.expense_approver = user + expense_claim.save() + self.assertTrue(expense_claim.name in frappe.share.get_shared("Expense Claim", user)) + + # check shared doc revoked + expense_claim.reload() + expense_claim.expense_approver = "test@example.com" + expense_claim.save() + self.assertTrue(expense_claim.name not in frappe.share.get_shared("Expense Claim", user)) + + expense_claim.reload() + expense_claim.expense_approver = user + expense_claim.save() + + frappe.set_user(user) + expense_claim.reload() + expense_claim.status = "Approved" + expense_claim.submit() + frappe.set_user("Administrator") + + def test_multiple_payment_entries_against_expense(self): + # Creating expense claim + payable_account = get_payable_account("_Test Company") + expense_claim = make_expense_claim( + payable_account, 5500, 5500, "_Test Company", "Travel Expenses - _TC" + ) + expense_claim.save() + expense_claim.submit() + + # Payment entry 1: paying 500 + make_payment_entry(expense_claim, payable_account, 500) + outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts( + expense_claim + ) + self.assertEqual(outstanding_amount, 5000) + self.assertEqual(total_amount_reimbursed, 500) + + # Payment entry 1: paying 2000 + make_payment_entry(expense_claim, payable_account, 2000) + outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts( + expense_claim + ) + self.assertEqual(outstanding_amount, 3000) + self.assertEqual(total_amount_reimbursed, 2500) + + # Payment entry 1: paying 3000 + make_payment_entry(expense_claim, payable_account, 3000) + outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts( + expense_claim + ) + self.assertEqual(outstanding_amount, 0) + self.assertEqual(total_amount_reimbursed, 5500) + + def test_expense_claim_against_delivery_trip(self): + from erpnext.stock.doctype.delivery_trip.test_delivery_trip import ( + create_address, + create_delivery_trip, + create_driver, + create_vehicle, + ) + from erpnext.tests.utils import create_test_contact_and_address + + driver = create_driver() + create_vehicle() + create_test_contact_and_address() + address = create_address(driver) + + delivery_trip = create_delivery_trip(driver, address) + expense_claim = make_expense_claim_for_delivery_trip(delivery_trip.name) + self.assertEqual(delivery_trip.name, expense_claim.delivery_trip) + + def test_journal_entry_against_expense_claim(self): + payable_account = get_payable_account(company_name) + taxes = generate_taxes() + expense_claim = make_expense_claim( + payable_account, + 300, + 200, + company_name, + "Travel Expenses - _TC3", + do_not_submit=True, + taxes=taxes, + ) + expense_claim.submit() + + je = make_journal_entry(expense_claim) + + self.assertEqual(je.accounts[0].debit_in_account_currency, expense_claim.grand_total) + + +def get_payable_account(company): + return frappe.get_cached_value("Company", company, "default_payable_account") + + +def generate_taxes(company=None): + company = company or company_name + parent_account = frappe.db.get_value( + "Account", filters={"account_name": "Duties and Taxes", "company": company} + ) + account = create_account( + company=company, + account_name="Output Tax CGST", + account_type="Tax", + parent_account=parent_account, + ) + return { + "taxes": [ + {"account_head": account, "rate": 9, "description": "CGST", "tax_amount": 10, "total": 210} + ] + } + + +def make_expense_claim( + payable_account, + amount, + sanctioned_amount, + company, + account, + project=None, + task_name=None, + do_not_submit=False, + taxes=None, +): + employee = frappe.db.get_value("Employee", {"status": "Active"}) + if not employee: + employee = make_employee("test_employee@expense_claim.com", company=company) + + currency, cost_center = frappe.db.get_value( + "Company", company, ["default_currency", "cost_center"] + ) + expense_claim = { + "doctype": "Expense Claim", + "employee": employee, + "payable_account": payable_account, + "approval_status": "Approved", + "company": company, + "currency": currency, + "expenses": [ + { + "expense_type": "Travel", + "default_account": account, + "currency": currency, + "amount": amount, + "sanctioned_amount": sanctioned_amount, + "cost_center": cost_center, + } + ], + } + if taxes: + expense_claim.update(taxes) + + expense_claim = frappe.get_doc(expense_claim) + + if project: + expense_claim.project = project + if task_name: + expense_claim.task = task_name + + if do_not_submit: + return expense_claim + expense_claim.submit() + return expense_claim + + +def get_outstanding_and_total_reimbursed_amounts(expense_claim): + outstanding_amount = flt( + frappe.db.get_value("Expense Claim", expense_claim.name, "total_sanctioned_amount") + ) - flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed")) + total_amount_reimbursed = flt( + frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed") + ) + + return outstanding_amount, total_amount_reimbursed + + +def make_payment_entry(expense_claim, payable_account, amt): + from hrms.overrides.employee_payment_entry import get_payment_entry_for_employee + + pe = get_payment_entry_for_employee( + "Expense Claim", expense_claim.name, bank_account="_Test Bank USD - _TC", bank_amount=amt + ) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.source_exchange_rate = 1 + pe.paid_to = payable_account + pe.references[0].allocated_amount = amt + pe.insert() + pe.submit() + + +def make_journal_entry(expense_claim): + je_dict = make_bank_entry("Expense Claim", expense_claim.name) + je = frappe.get_doc(je_dict) + je.posting_date = nowdate() + je.cheque_no = random_string(5) + je.cheque_date = nowdate() + je.submit() + + return je diff --git a/hrms/hr/doctype/expense_claim_account/__init__.py b/hrms/hr/doctype/expense_claim_account/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expense_claim_account/expense_claim_account.json b/hrms/hr/doctype/expense_claim_account/expense_claim_account.json new file mode 100644 index 0000000..c7d71d3 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_account/expense_claim_account.json @@ -0,0 +1,89 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-07-18 12:24:16.507860", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "default_account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Default Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-07-18 12:39:29.709848", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Account", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim_account/expense_claim_account.py b/hrms/hr/doctype/expense_claim_account/expense_claim_account.py new file mode 100644 index 0000000..0d46a22 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_account/expense_claim_account.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class ExpenseClaimAccount(Document): + pass diff --git a/hrms/hr/doctype/expense_claim_advance/__init__.py b/hrms/hr/doctype/expense_claim_advance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expense_claim_advance/expense_claim_advance.json b/hrms/hr/doctype/expense_claim_advance/expense_claim_advance.json new file mode 100644 index 0000000..aa479c8 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_advance/expense_claim_advance.json @@ -0,0 +1,100 @@ +{ + "actions": [], + "creation": "2017-10-09 16:53:26.410762", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee_advance", + "posting_date", + "advance_paid", + "column_break_4", + "unclaimed_amount", + "allocated_amount", + "advance_account" + ], + "fields": [ + { + "columns": 2, + "fieldname": "employee_advance", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee Advance", + "no_copy": 1, + "oldfieldname": "journal_voucher", + "oldfieldtype": "Link", + "options": "Employee Advance", + "print_width": "250px", + "reqd": 1, + "width": "250px" + }, + { + "columns": 2, + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "read_only": 1 + }, + { + "columns": 2, + "fieldname": "advance_paid", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance Paid", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "columns": 2, + "fieldname": "unclaimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Unclaimed Amount", + "no_copy": 1, + "oldfieldname": "advance_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", + "read_only": 1, + "reqd": 1, + "width": "120px" + }, + { + "columns": 2, + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Allocated Amount", + "no_copy": 1, + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", + "width": "120px" + }, + { + "fieldname": "advance_account", + "fieldtype": "Link", + "hidden": 1, + "label": "Advance Account", + "options": "Account" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + } + ], + "istable": 1, + "links": [], + "modified": "2021-11-22 16:33:58.515819", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Advance", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim_advance/expense_claim_advance.py b/hrms/hr/doctype/expense_claim_advance/expense_claim_advance.py new file mode 100644 index 0000000..68b2963 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_advance/expense_claim_advance.py @@ -0,0 +1,9 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class ExpenseClaimAdvance(Document): + pass diff --git a/hrms/hr/doctype/expense_claim_detail/README.md b/hrms/hr/doctype/expense_claim_detail/README.md new file mode 100644 index 0000000..4a747e2 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_detail/README.md @@ -0,0 +1 @@ +Detail of expense in parent Expense Claim. \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim_detail/__init__.py b/hrms/hr/doctype/expense_claim_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expense_claim_detail/expense_claim_detail.json b/hrms/hr/doctype/expense_claim_detail/expense_claim_detail.json new file mode 100644 index 0000000..6edbcb5 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_detail/expense_claim_detail.json @@ -0,0 +1,130 @@ +{ + "actions": [], + "creation": "2013-02-22 01:27:46", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "expense_date", + "column_break_2", + "expense_type", + "default_account", + "section_break_4", + "description", + "section_break_6", + "amount", + "column_break_8", + "sanctioned_amount", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" + ], + "fields": [ + { + "fieldname": "expense_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expense Date", + "oldfieldname": "expense_date", + "oldfieldtype": "Date", + "print_width": "150px", + "width": "150px" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "expense_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Expense Claim Type", + "oldfieldname": "expense_type", + "oldfieldtype": "Link", + "options": "Expense Claim Type", + "print_width": "150px", + "reqd": 1, + "width": "150px" + }, + { + "depends_on": "expense_type", + "fieldname": "default_account", + "fieldtype": "Link", + "hidden": 1, + "label": "Default Account", + "options": "Account", + "read_only": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "print_width": "300px", + "width": "300px" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "oldfieldname": "claim_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "150px", + "reqd": 1, + "width": "150px" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "sanctioned_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Sanctioned Amount", + "oldfieldname": "sanctioned_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "150px", + "width": "150px" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + } + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2021-11-26 14:23:45.539922", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Detail", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim_detail/expense_claim_detail.py b/hrms/hr/doctype/expense_claim_detail/expense_claim_detail.py new file mode 100644 index 0000000..f58f128 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_detail/expense_claim_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.document import Document + + +class ExpenseClaimDetail(Document): + pass diff --git a/hrms/hr/doctype/expense_claim_type/README.md b/hrms/hr/doctype/expense_claim_type/README.md new file mode 100644 index 0000000..13a654e --- /dev/null +++ b/hrms/hr/doctype/expense_claim_type/README.md @@ -0,0 +1 @@ +Type of expense for Expense Claim. \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim_type/__init__.py b/hrms/hr/doctype/expense_claim_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expense_claim_type/expense_claim_type.js b/hrms/hr/doctype/expense_claim_type/expense_claim_type.js new file mode 100644 index 0000000..d007e1a --- /dev/null +++ b/hrms/hr/doctype/expense_claim_type/expense_claim_type.js @@ -0,0 +1,17 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on("Expense Claim Type", { + refresh: function(frm) { + frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "is_group": 0, + "root_type": frm.doc.deferred_expense_account ? "Asset" : "Expense", + 'company': d.company + } + } + } + } +}) diff --git a/hrms/hr/doctype/expense_claim_type/expense_claim_type.json b/hrms/hr/doctype/expense_claim_type/expense_claim_type.json new file mode 100644 index 0000000..02ab4cb --- /dev/null +++ b/hrms/hr/doctype/expense_claim_type/expense_claim_type.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:expense_type", + "creation": "2012-03-27 14:35:55", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "deferred_expense_account", + "expense_type", + "accounts", + "description" + ], + "fields": [ + { + "fieldname": "expense_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Expense Claim Type", + "oldfieldname": "expense_type", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "width": "300px" + }, + { + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Expense Claim Account" + }, + { + "default": "0", + "fieldname": "deferred_expense_account", + "fieldtype": "Check", + "label": "Deferred Expense Account" + } + ], + "icon": "fa fa-flag", + "idx": 1, + "links": [], + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/expense_claim_type/expense_claim_type.py b/hrms/hr/doctype/expense_claim_type/expense_claim_type.py new file mode 100644 index 0000000..6d29f7d --- /dev/null +++ b/hrms/hr/doctype/expense_claim_type/expense_claim_type.py @@ -0,0 +1,30 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class ExpenseClaimType(Document): + def validate(self): + self.validate_accounts() + self.validate_repeating_companies() + + def validate_repeating_companies(self): + """Error when Same Company is entered multiple times in accounts""" + accounts_list = [] + for entry in self.accounts: + accounts_list.append(entry.company) + + if len(accounts_list) != len(set(accounts_list)): + frappe.throw(_("Same Company is entered more than once")) + + def validate_accounts(self): + for entry in self.accounts: + """Error when Company of Ledger account doesn't match with Company Selected""" + if frappe.db.get_value("Account", entry.default_account, "company") != entry.company: + frappe.throw( + _("Account {0} does not match with Company {1}").format(entry.default_account, entry.company) + ) diff --git a/hrms/hr/doctype/expense_claim_type/test_expense_claim_type.py b/hrms/hr/doctype/expense_claim_type/test_expense_claim_type.py new file mode 100644 index 0000000..62348e2 --- /dev/null +++ b/hrms/hr/doctype/expense_claim_type/test_expense_claim_type.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +# test_records = frappe.get_test_records('Expense Claim Type') + + +class TestExpenseClaimType(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/expense_taxes_and_charges/__init__.py b/hrms/hr/doctype/expense_taxes_and_charges/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json new file mode 100644 index 0000000..2f7b8fc --- /dev/null +++ b/hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -0,0 +1,112 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2019-06-03 11:42:33.123976", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account_head", + "rate", + "col_break1", + "description", + "section_break_6", + "tax_amount", + "column_break_8", + "total", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" + ], + "fields": [ + { + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "account_head", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account Head", + "oldfieldname": "account_head", + "oldfieldtype": "Link", + "options": "Account", + "reqd": 1 + }, + { + "default": ":Company", + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "oldfieldname": "cost_center", + "oldfieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "print_width": "300px", + "reqd": 1, + "width": "300px" + }, + { + "columns": 2, + "fieldname": "rate", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency" + }, + { + "columns": 2, + "fieldname": "tax_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency" + }, + { + "columns": 2, + "fieldname": "total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + } + ], + "istable": 1, + "links": [], + "modified": "2021-10-26 20:27:36.027728", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Taxes and Charges", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} diff --git a/hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py b/hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py new file mode 100644 index 0000000..a28ef57 --- /dev/null +++ b/hrms/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class ExpenseTaxesandCharges(Document): + pass diff --git a/hrms/hr/doctype/full_and_final_asset/__init__.py b/hrms/hr/doctype/full_and_final_asset/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.js b/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.js new file mode 100644 index 0000000..1965b46 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Full and Final Asset', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.json b/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.json new file mode 100644 index 0000000..3ad8335 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.json @@ -0,0 +1,64 @@ +{ + "actions": [], + "creation": "2021-06-28 13:36:58.658985", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference", + "asset_name", + "date", + "status", + "description" + ], + "fields": [ + { + "fieldname": "reference", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference", + "options": "Asset Movement", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Owned\nReturned", + "reqd": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "fieldname": "asset_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "date", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Date", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-15 15:17:31.309834", + "modified_by": "Administrator", + "module": "HR", + "name": "Full and Final Asset", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.py b/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.py new file mode 100644 index 0000000..661af7d --- /dev/null +++ b/hrms/hr/doctype/full_and_final_asset/full_and_final_asset.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class FullandFinalAsset(Document): + pass diff --git a/hrms/hr/doctype/full_and_final_asset/test_full_and_final_asset.py b/hrms/hr/doctype/full_and_final_asset/test_full_and_final_asset.py new file mode 100644 index 0000000..9afe0f2 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_asset/test_full_and_final_asset.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestFullandFinalAsset(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/full_and_final_outstanding_statement/__init__.py b/hrms/hr/doctype/full_and_final_outstanding_statement/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json b/hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json new file mode 100644 index 0000000..be242e2 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json @@ -0,0 +1,96 @@ +{ + "actions": [], + "creation": "2021-06-28 13:32:02.167317", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "component", + "reference_document_type", + "reference_document", + "account", + "paid_via_salary_slip", + "column_break_4", + "amount", + "status", + "remark" + ], + "fields": [ + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "default": "Unsettled", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Settled\nUnsettled" + }, + { + "fieldname": "remark", + "fieldtype": "Small Text", + "label": "Remark" + }, + { + "columns": 2, + "depends_on": "reference_document_type", + "fieldname": "reference_document", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Document", + "mandatory_depends_on": "reference_document_type", + "options": "reference_document_type", + "search_index": 1 + }, + { + "columns": 2, + "fieldname": "component", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Component", + "reqd": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account" + }, + { + "columns": 2, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount" + }, + { + "columns": 2, + "fieldname": "reference_document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType" + }, + { + "default": "0", + "fieldname": "paid_via_salary_slip", + "fieldtype": "Check", + "label": "Paid via Salary Slip" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-20 16:59:34.447934", + "modified_by": "Administrator", + "module": "HR", + "name": "Full and Final Outstanding Statement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py b/hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py new file mode 100644 index 0000000..4b239ab --- /dev/null +++ b/hrms/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class FullandFinalOutstandingStatement(Document): + pass diff --git a/hrms/hr/doctype/full_and_final_statement/__init__.py b/hrms/hr/doctype/full_and_final_statement/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.js b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.js new file mode 100644 index 0000000..7646479 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.js @@ -0,0 +1,115 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Full and Final Statement', { + refresh: function(frm) { + frm.events.set_queries(frm, "payables"); + frm.events.set_queries(frm, "receivables"); + + if (frm.doc.docstatus == 1 && frm.doc.status == "Unpaid") { + frm.add_custom_button(__("Create Journal Entry"), function () { + frm.events.create_journal_entry(frm); + }); + } + }, + + set_queries: function(frm, type) { + frm.set_query("reference_document_type", type, function () { + let modules = ["HR", "Payroll", "Loan Management"]; + return { + filters: { + istable: 0, + issingle: 0, + module: ["In", modules] + } + }; + }); + + let filters = {}; + + frm.set_query('reference_document', type, function(doc, cdt, cdn) { + let fnf_doc = frappe.get_doc(cdt, cdn); + + frappe.model.with_doctype(fnf_doc.reference_document_type, function() { + if (frappe.model.is_tree(fnf_doc.reference_document_type)) { + filters['is_group'] = 0; + } + + if (frappe.meta.has_field(fnf_doc.reference_document_type, 'company')) { + filters['company'] = frm.doc.company; + } + + if (frappe.meta.has_field(fnf_doc.reference_document_type, 'employee')) { + filters['employee'] = frm.doc.employee; + } + }); + + return { + filters: filters + }; + }); + }, + + employee: function(frm) { + frm.events.get_outstanding_statements(frm); + }, + + get_outstanding_statements: function(frm) { + if (frm.doc.employee) { + frappe.call({ + method: "get_outstanding_statements", + doc: frm.doc, + callback: function() { + frm.refresh(); + } + }); + } + }, + + create_journal_entry: function(frm) { + frappe.call({ + method: "create_journal_entry", + doc: frm.doc, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + } +}); + +frappe.ui.form.on("Full and Final Outstanding Statement", { + reference_document: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if (child.reference_document_type && child.reference_document) { + frappe.call({ + method: "hrms.hr.doctype.full_and_final_statement.full_and_final_statement.get_account_and_amount", + args: { + ref_doctype: child.reference_document_type, + ref_document: child.reference_document + }, + callback: function(r) { + if (r.message) { + frappe.model.set_value(cdt, cdn, "account", r.message[0]); + frappe.model.set_value(cdt, cdn, "amount", r.message[1]); + } + } + }); + } + }, + + amount: function(frm) { + var total_payable_amount = 0; + var total_receivable_amount = 0; + + frm.doc.payables.forEach(element => { + total_payable_amount = total_payable_amount + element.amount; + }); + + frm.doc.receivables.forEach(element => { + total_receivable_amount = total_receivable_amount + element.amount; + }); + frm.set_value("total_payable_amount", flt(total_payable_amount)); + frm.set_value("total_receivable_amount", flt(total_receivable_amount)); + } +}); diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.json b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.json new file mode 100644 index 0000000..ebcf36d --- /dev/null +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.json @@ -0,0 +1,231 @@ +{ + "actions": [], + "autoname": "HR-FNF-.YYYY.-.#####", + "creation": "2021-06-28 13:17:36.050459", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "transaction_date", + "column_break_12", + "company", + "status", + "amended_from", + "employee_details_section", + "date_of_joining", + "relieving_date", + "column_break_4", + "designation", + "department", + "section_break_8", + "payables", + "section_break_10", + "receivables", + "totals_section", + "total_payable_amount", + "column_break_21", + "total_receivable_amount", + "section_break_15", + "assets_allocated" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "Unpaid", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Paid\nUnpaid", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Full and Final Statement", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Payables" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "label": "Receivables" + }, + { + "fieldname": "assets_allocated", + "fieldtype": "Table", + "options": "Full and Final Asset" + }, + { + "fetch_from": "employee.relieving_date", + "fieldname": "relieving_date", + "fieldtype": "Date", + "label": "Relieving Date ", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.date_of_joining", + "fieldname": "date_of_joining", + "fieldtype": "Date", + "label": "Date of Joining", + "read_only": 1 + }, + { + "fieldname": "section_break_15", + "fieldtype": "Section Break", + "label": "Assets Allocated" + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "read_only": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "payables", + "fieldtype": "Table", + "options": "Full and Final Outstanding Statement" + }, + { + "fieldname": "receivables", + "fieldtype": "Table", + "options": "Full and Final Outstanding Statement" + }, + { + "fieldname": "employee_details_section", + "fieldtype": "Section Break", + "label": "Employee Details" + }, + { + "fieldname": "transaction_date", + "fieldtype": "Date", + "in_standard_filter": 1, + "label": "Transaction Date", + "reqd": 1 + }, + { + "fieldname": "totals_section", + "fieldtype": "Section Break", + "label": "Totals" + }, + { + "fieldname": "total_payable_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Payable Amount", + "read_only": 1 + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_receivable_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Receivable Amount", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-08-30 21:11:09.892560", + "modified_by": "Administrator", + "module": "HR", + "name": "Full and Final Statement", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py new file mode 100644 index 0000000..f5d8fa5 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py @@ -0,0 +1,211 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt, get_link_to_form, today + + +class FullandFinalStatement(Document): + def validate(self): + self.get_outstanding_statements() + if self.docstatus == 1: + self.validate_settlement("payables") + self.validate_settlement("receivables") + self.validate_asset() + + def validate_settlement(self, component_type): + for data in self.get(component_type, []): + if data.status == "Unsettled": + frappe.throw(_("Settle all Payables and Receivables before submission")) + + def validate_asset(self): + for data in self.assets_allocated: + if data.status == "Owned": + frappe.throw(_("All allocated assets should be returned before submission")) + + @frappe.whitelist() + def get_outstanding_statements(self): + if self.relieving_date: + if not len(self.get("payables", [])): + components = self.get_payable_component() + self.create_component_row(components, "payables") + if not len(self.get("receivables", [])): + components = self.get_receivable_component() + self.create_component_row(components, "receivables") + + if not len(self.get("assets_allocated", [])): + for data in self.get_assets_movement(): + self.append("assets_allocated", data) + else: + frappe.throw( + _("Set Relieving Date for Employee: {0}").format(get_link_to_form("Employee", self.employee)) + ) + + def create_component_row(self, components, component_type): + for component in components: + self.append( + component_type, + { + "status": "Unsettled", + "reference_document_type": component if component != "Bonus" else "Additional Salary", + "component": component, + }, + ) + + def get_payable_component(self): + return [ + "Salary Slip", + "Gratuity", + "Expense Claim", + "Bonus", + "Leave Encashment", + ] + + def get_receivable_component(self): + return [ + "Loan", + "Employee Advance", + ] + + def get_assets_movement(self): + asset_movements = frappe.get_all( + "Asset Movement Item", + filters={"docstatus": 1}, + fields=["asset", "from_employee", "to_employee", "parent", "asset_name"], + or_filters={"from_employee": self.employee, "to_employee": self.employee}, + ) + + data = [] + inward_movements = [] + outward_movements = [] + for movement in asset_movements: + if movement.to_employee and movement.to_employee == self.employee: + inward_movements.append(movement) + + if movement.from_employee and movement.from_employee == self.employee: + outward_movements.append(movement) + + for movement in inward_movements: + outwards_count = [movement.asset for movement in outward_movements].count(movement.asset) + inwards_counts = [movement.asset for movement in inward_movements].count(movement.asset) + + if inwards_counts > outwards_count: + data.append( + { + "reference": movement.parent, + "asset_name": movement.asset_name, + "date": frappe.db.get_value("Asset Movement", movement.parent, "transaction_date"), + "status": "Owned", + } + ) + return data + + @frappe.whitelist() + def create_journal_entry(self): + precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") + jv = frappe.new_doc("Journal Entry") + jv.company = self.company + jv.voucher_type = "Bank Entry" + jv.posting_date = today() + + difference = self.total_payable_amount - self.total_receivable_amount + + for data in self.payables: + if data.amount > 0 and not data.paid_via_salary_slip: + account_dict = { + "account": data.account, + "debit_in_account_currency": flt(data.amount, precision), + } + if data.reference_document_type == "Expense Claim": + account_dict["party_type"] = "Employee" + account_dict["party"] = self.employee + + jv.append("accounts", account_dict) + + for data in self.receivables: + if data.amount > 0: + account_dict = { + "account": data.account, + "credit_in_account_currency": flt(data.amount, precision), + } + if data.reference_document_type == "Employee Advance": + account_dict["party_type"] = "Employee" + account_dict["party"] = self.employee + + jv.append("accounts", account_dict) + + jv.append( + "accounts", + { + "credit_in_account_currency": difference if difference > 0 else 0, + "debit_in_account_currency": -(difference) if difference < 0 else 0, + "reference_type": self.doctype, + "reference_name": self.name, + }, + ) + return jv + + +@frappe.whitelist() +def get_account_and_amount(ref_doctype, ref_document): + if not ref_doctype or not ref_document: + return None + + if ref_doctype == "Salary Slip": + salary_details = frappe.db.get_value( + "Salary Slip", ref_document, ["payroll_entry", "net_pay"], as_dict=1 + ) + amount = salary_details.net_pay + payable_account = ( + frappe.db.get_value("Payroll Entry", salary_details.payroll_entry, "payroll_payable_account") + if salary_details.payroll_entry + else None + ) + return [payable_account, amount] + + if ref_doctype == "Gratuity": + payable_account, amount = frappe.db.get_value( + "Gratuity", ref_document, ["payable_account", "amount"] + ) + return [payable_account, amount] + + if ref_doctype == "Expense Claim": + details = frappe.db.get_value( + "Expense Claim", + ref_document, + ["payable_account", "grand_total", "total_amount_reimbursed", "total_advance_amount"], + as_dict=True, + ) + payable_account = details.payable_account + amount = details.grand_total - (details.total_amount_reimbursed + details.total_advance_amount) + return [payable_account, amount] + + if ref_doctype == "Loan": + details = frappe.db.get_value( + "Loan", ref_document, ["payment_account", "total_payment", "total_amount_paid"], as_dict=1 + ) + payment_account = details.payment_account + amount = details.total_payment - details.total_amount_paid + return [payment_account, amount] + + if ref_doctype == "Employee Advance": + details = frappe.db.get_value( + "Employee Advance", + ref_document, + ["advance_account", "paid_amount", "claimed_amount", "return_amount"], + as_dict=1, + ) + payment_account = details.advance_account + amount = details.paid_amount - (details.claimed_amount + details.return_amount) + return [payment_account, amount] + + +def update_full_and_final_statement_status(doc, method=None): + """Updates FnF status on Journal Entry Submission/Cancellation""" + status = "Paid" if doc.docstatus == 1 else "Unpaid" + + for entry in doc.accounts: + if entry.reference_type == "Full and Final Statement": + frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", status) diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_list.js b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_list.js new file mode 100644 index 0000000..4aedec7 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_list.js @@ -0,0 +1,11 @@ +frappe.listview_settings["Full and Final Statement"] = { + get_indicator: function(doc) { + var colors = { + "Draft": "red", + "Unpaid": "orange", + "Paid": "green", + "Cancelled": "red" + }; + return [__(doc.status), colors[doc.status], "status,=," + doc.status]; + } +}; diff --git a/hrms/hr/doctype/full_and_final_statement/test_full_and_final_statement.py b/hrms/hr/doctype/full_and_final_statement/test_full_and_final_statement.py new file mode 100644 index 0000000..2e2f0d4 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_statement/test_full_and_final_statement.py @@ -0,0 +1,81 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, today + +from erpnext.assets.doctype.asset.test_asset import create_asset_data +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt + + +class TestFullandFinalStatement(unittest.TestCase): + def setUp(self): + create_asset_data() + + def tearDown(self): + frappe.db.sql("Delete from `tabFull and Final Statement`") + frappe.db.sql("Delete from `tabAsset`") + frappe.db.sql("Delete from `tabAsset Movement`") + + def test_check_bootstraped_data_asset_movement_and_jv_creation(self): + employee = make_employee("test_fnf@example.com", company="_Test Company") + movement = create_asset_movement(employee) + frappe.db.set_value("Employee", employee, "relieving_date", add_days(today(), 30)) + fnf = create_full_and_final_statement(employee) + + payables_bootstraped_component = [ + "Salary Slip", + "Gratuity", + "Expense Claim", + "Bonus", + "Leave Encashment", + ] + + receivable_bootstraped_component = ["Loan", "Employee Advance"] + + # checking payable s and receivables bootstraped value + self.assertEqual([payable.component for payable in fnf.payables], payables_bootstraped_component) + self.assertEqual( + [receivable.component for receivable in fnf.receivables], receivable_bootstraped_component + ) + + # checking allocated asset + self.assertIn(movement, [asset.reference for asset in fnf.assets_allocated]) + + +def create_full_and_final_statement(employee): + fnf = frappe.new_doc("Full and Final Statement") + fnf.employee = employee + fnf.transaction_date = today() + fnf.save() + return fnf + + +def create_asset_movement(employee): + asset_name = create_asset() + movement = frappe.new_doc("Asset Movement") + movement.company = "_Test Company" + movement.purpose = "Issue" + movement.transaction_date = today() + + movement.append("assets", {"asset": asset_name, "to_employee": employee}) + + movement.save() + movement.submit() + return movement.name + + +def create_asset(): + pr = make_purchase_receipt( + item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location" + ) + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") + asset = frappe.get_doc("Asset", asset_name) + asset.calculate_depreciation = 0 + asset.available_for_use_date = today() + asset.submit() + return asset_name diff --git a/hrms/hr/doctype/grievance_type/__init__.py b/hrms/hr/doctype/grievance_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/grievance_type/grievance_type.js b/hrms/hr/doctype/grievance_type/grievance_type.js new file mode 100644 index 0000000..425f2fd --- /dev/null +++ b/hrms/hr/doctype/grievance_type/grievance_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Grievance Type', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/grievance_type/grievance_type.json b/hrms/hr/doctype/grievance_type/grievance_type.json new file mode 100644 index 0000000..94445a4 --- /dev/null +++ b/hrms/hr/doctype/grievance_type/grievance_type.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2021-05-11 12:41:50.256071", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_5", + "description" + ], + "fields": [ + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Description" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-08-21 13:05:39.326191", + "modified_by": "Administrator", + "module": "HR", + "name": "Grievance Type", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/hrms/hr/doctype/grievance_type/grievance_type.py b/hrms/hr/doctype/grievance_type/grievance_type.py new file mode 100644 index 0000000..5d8d41c --- /dev/null +++ b/hrms/hr/doctype/grievance_type/grievance_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class GrievanceType(Document): + pass diff --git a/hrms/hr/doctype/grievance_type/test_grievance_type.py b/hrms/hr/doctype/grievance_type/test_grievance_type.py new file mode 100644 index 0000000..481f4e5 --- /dev/null +++ b/hrms/hr/doctype/grievance_type/test_grievance_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestGrievanceType(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/hr_settings/__init__.py b/hrms/hr/doctype/hr_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/hr_settings/hr_settings.js b/hrms/hr/doctype/hr_settings/hr_settings.js new file mode 100644 index 0000000..6e26a1f --- /dev/null +++ b/hrms/hr/doctype/hr_settings/hr_settings.js @@ -0,0 +1,23 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('HR Settings', { +}); + +frappe.tour['HR Settings'] = [ + { + fieldname: 'emp_created_by', + title: 'Employee Naming By', + description: __('Employee can be named by Employee ID if you assign one, or via Naming Series. Select your preference here.'), + }, + { + fieldname: 'standard_working_hours', + title: 'Standard Working Hours', + description: __('Enter the Standard Working Hours for a normal work day. These hours will be used in calculations of reports such as Employee Hours Utilization and Project Profitability analysis.'), + }, + { + fieldname: 'leave_and_expense_claim_settings', + title: 'Leave and Expense Clain Settings', + description: __('Review various other settings related to Employee Leaves and Expense Claim') + } +]; diff --git a/hrms/hr/doctype/hr_settings/hr_settings.json b/hrms/hr/doctype/hr_settings/hr_settings.json new file mode 100644 index 0000000..f9a3e05 --- /dev/null +++ b/hrms/hr/doctype/hr_settings/hr_settings.json @@ -0,0 +1,279 @@ +{ + "actions": [], + "creation": "2013-08-02 13:45:23", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee_settings", + "emp_created_by", + "standard_working_hours", + "column_break_9", + "retirement_age", + "reminders_section", + "send_birthday_reminders", + "column_break_11", + "send_work_anniversary_reminders", + "column_break_18", + "send_holiday_reminders", + "frequency", + "leave_and_expense_claim_settings", + "send_leave_notification", + "leave_approval_notification_template", + "leave_status_notification_template", + "leave_approver_mandatory_in_leave_application", + "restrict_backdated_leave_application", + "role_allowed_to_create_backdated_leave_application", + "column_break_29", + "expense_approver_mandatory_in_expense_claim", + "show_leaves_of_all_department_members_in_calendar", + "auto_leave_encashment", + "hiring_settings_section", + "check_vacancies", + "send_interview_reminder", + "interview_reminder_template", + "remind_before", + "column_break_4", + "send_interview_feedback_reminder", + "feedback_reminder_notification_template", + "employee_exit_section", + "exit_questionnaire_web_form", + "column_break_34", + "exit_questionnaire_notification_template" + ], + "fields": [ + { + "fieldname": "employee_settings", + "fieldtype": "Section Break", + "label": "Employee Settings" + }, + { + "fieldname": "retirement_age", + "fieldtype": "Data", + "label": "Retirement Age (In Years)" + }, + { + "default": "Naming Series", + "description": "Employee records are created using the selected option", + "fieldname": "emp_created_by", + "fieldtype": "Select", + "label": "Employee Naming By", + "options": "Naming Series\nEmployee Number\nFull Name" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "expense_approver_mandatory_in_expense_claim", + "fieldtype": "Check", + "label": "Expense Approver Mandatory In Expense Claim" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "leave_approver_mandatory_in_leave_application", + "fieldtype": "Check", + "label": "Leave Approver Mandatory In Leave Application" + }, + { + "default": "0", + "fieldname": "show_leaves_of_all_department_members_in_calendar", + "fieldtype": "Check", + "label": "Show Leaves Of All Department Members In Calendar" + }, + { + "default": "0", + "fieldname": "auto_leave_encashment", + "fieldtype": "Check", + "label": "Auto Leave Encashment" + }, + { + "depends_on": "eval:doc.restrict_backdated_leave_application == 1", + "fieldname": "role_allowed_to_create_backdated_leave_application", + "fieldtype": "Link", + "label": "Role Allowed to Create Backdated Leave Application", + "mandatory_depends_on": "eval:doc.restrict_backdated_leave_application == 1", + "options": "Role" + }, + { + "default": "1", + "fieldname": "send_leave_notification", + "fieldtype": "Check", + "label": "Send Leave Notification" + }, + { + "depends_on": "eval: doc.send_leave_notification == 1", + "fieldname": "leave_approval_notification_template", + "fieldtype": "Link", + "label": "Leave Approval Notification Template", + "mandatory_depends_on": "eval: doc.send_leave_notification == 1", + "options": "Email Template" + }, + { + "depends_on": "eval: doc.send_leave_notification == 1", + "fieldname": "leave_status_notification_template", + "fieldtype": "Link", + "label": "Leave Status Notification Template", + "mandatory_depends_on": "eval: doc.send_leave_notification == 1", + "options": "Email Template" + }, + { + "fieldname": "standard_working_hours", + "fieldtype": "Int", + "label": "Standard Working Hours" + }, + { + "collapsible": 1, + "fieldname": "leave_and_expense_claim_settings", + "fieldtype": "Section Break", + "label": "Leave and Expense Claim Settings" + }, + { + "default": "00:15:00", + "depends_on": "send_interview_reminder", + "fieldname": "remind_before", + "fieldtype": "Time", + "label": "Remind Before" + }, + { + "collapsible": 1, + "fieldname": "reminders_section", + "fieldtype": "Section Break", + "label": "Reminders" + }, + { + "default": "1", + "fieldname": "send_holiday_reminders", + "fieldtype": "Check", + "label": "Holidays" + }, + { + "default": "1", + "fieldname": "send_work_anniversary_reminders", + "fieldtype": "Check", + "label": "Work Anniversaries " + }, + { + "default": "Weekly", + "depends_on": "eval:doc.send_holiday_reminders", + "fieldname": "frequency", + "fieldtype": "Select", + "label": "Set the frequency for holiday reminders", + "mandatory_depends_on": "send_holiday_reminders", + "options": "Weekly\nMonthly" + }, + { + "default": "1", + "fieldname": "send_birthday_reminders", + "fieldtype": "Check", + "label": "Birthdays" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "send_interview_reminder", + "fieldtype": "Check", + "label": "Send Interview Reminder" + }, + { + "default": "0", + "fieldname": "send_interview_feedback_reminder", + "fieldtype": "Check", + "label": "Send Interview Feedback Reminder" + }, + { + "fieldname": "column_break_29", + "fieldtype": "Column Break" + }, + { + "depends_on": "send_interview_feedback_reminder", + "fieldname": "feedback_reminder_notification_template", + "fieldtype": "Link", + "label": "Feedback Reminder Notification Template", + "mandatory_depends_on": "send_interview_feedback_reminder", + "options": "Email Template" + }, + { + "depends_on": "send_interview_reminder", + "fieldname": "interview_reminder_template", + "fieldtype": "Link", + "label": "Interview Reminder Notification Template", + "mandatory_depends_on": "send_interview_reminder", + "options": "Email Template" + }, + { + "default": "0", + "fieldname": "restrict_backdated_leave_application", + "fieldtype": "Check", + "label": "Restrict Backdated Leave Application" + }, + { + "fieldname": "hiring_settings_section", + "fieldtype": "Section Break", + "label": "Hiring Settings" + }, + { + "default": "0", + "fieldname": "check_vacancies", + "fieldtype": "Check", + "label": "Check Vacancies On Job Offer Creation" + }, + { + "fieldname": "employee_exit_section", + "fieldtype": "Section Break", + "label": "Employee Exit Settings" + }, + { + "fieldname": "exit_questionnaire_web_form", + "fieldtype": "Link", + "label": "Exit Questionnaire Web Form", + "options": "Web Form" + }, + { + "fieldname": "exit_questionnaire_notification_template", + "fieldtype": "Link", + "label": "Exit Questionnaire Notification Template", + "options": "Email Template" + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" + } + ], + "icon": "fa fa-cog", + "idx": 1, + "issingle": 1, + "links": [], + "modified": "2021-12-05 14:48:10.884253", + "modified_by": "Administrator", + "module": "HR", + "name": "HR Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/hr_settings/hr_settings.py b/hrms/hr/doctype/hr_settings/hr_settings.py new file mode 100644 index 0000000..b56f3db --- /dev/null +++ b/hrms/hr/doctype/hr_settings/hr_settings.py @@ -0,0 +1,85 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from frappe.utils import format_date + +# Wether to proceed with frequency change +PROCEED_WITH_FREQUENCY_CHANGE = False + + +class HRSettings(Document): + def validate(self): + self.set_naming_series() + + # Based on proceed flag + global PROCEED_WITH_FREQUENCY_CHANGE + if not PROCEED_WITH_FREQUENCY_CHANGE: + self.validate_frequency_change() + PROCEED_WITH_FREQUENCY_CHANGE = False + + def set_naming_series(self): + from erpnext.utilities.naming import set_by_naming_series + + set_by_naming_series( + "Employee", + "employee_number", + self.get("emp_created_by") == "Naming Series", + hide_name_field=True, + ) + + def validate_frequency_change(self): + weekly_job, monthly_job = None, None + + try: + weekly_job = frappe.get_doc( + "Scheduled Job Type", "employee_reminders.send_reminders_in_advance_weekly" + ) + + monthly_job = frappe.get_doc( + "Scheduled Job Type", "employee_reminders.send_reminders_in_advance_monthly" + ) + except frappe.DoesNotExistError: + return + + next_weekly_trigger = weekly_job.get_next_execution() + next_monthly_trigger = monthly_job.get_next_execution() + + if self.freq_changed_from_monthly_to_weekly(): + if next_monthly_trigger < next_weekly_trigger: + self.show_freq_change_warning(next_monthly_trigger, next_weekly_trigger) + + elif self.freq_changed_from_weekly_to_monthly(): + if next_monthly_trigger > next_weekly_trigger: + self.show_freq_change_warning(next_weekly_trigger, next_monthly_trigger) + + def freq_changed_from_weekly_to_monthly(self): + return self.has_value_changed("frequency") and self.frequency == "Monthly" + + def freq_changed_from_monthly_to_weekly(self): + return self.has_value_changed("frequency") and self.frequency == "Weekly" + + def show_freq_change_warning(self, from_date, to_date): + from_date = frappe.bold(format_date(from_date)) + to_date = frappe.bold(format_date(to_date)) + frappe.msgprint( + msg=frappe._( + "Employees will miss holiday reminders from {} until {}.
Do you want to proceed with this change?" + ).format(from_date, to_date), + title="Confirm change in Frequency", + primary_action={ + "label": frappe._("Yes, Proceed"), + "client_action": "erpnext.proceed_save_with_reminders_frequency_change", + }, + raise_exception=frappe.ValidationError, + ) + + +@frappe.whitelist() +def set_proceed_with_frequency_change(): + """Enables proceed with frequency change""" + global PROCEED_WITH_FREQUENCY_CHANGE + PROCEED_WITH_FREQUENCY_CHANGE = True diff --git a/hrms/hr/doctype/hr_settings/test_hr_settings.py b/hrms/hr/doctype/hr_settings/test_hr_settings.py new file mode 100644 index 0000000..7e13213 --- /dev/null +++ b/hrms/hr/doctype/hr_settings/test_hr_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestHRSettings(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/identification_document_type/__init__.py b/hrms/hr/doctype/identification_document_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/identification_document_type/identification_document_type.js b/hrms/hr/doctype/identification_document_type/identification_document_type.js new file mode 100644 index 0000000..351cf9d --- /dev/null +++ b/hrms/hr/doctype/identification_document_type/identification_document_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Identification Document Type', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/identification_document_type/identification_document_type.json b/hrms/hr/doctype/identification_document_type/identification_document_type.json new file mode 100644 index 0000000..33cbde7 --- /dev/null +++ b/hrms/hr/doctype/identification_document_type/identification_document_type.json @@ -0,0 +1,93 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:identification_document_type", + "beta": 0, + "creation": "2018-05-15 07:13:28.620570", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "identification_document_type", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Identification Document Type", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-05-16 04:34:00.448680", + "modified_by": "Administrator", + "module": "HR", + "name": "Identification Document Type", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/identification_document_type/identification_document_type.py b/hrms/hr/doctype/identification_document_type/identification_document_type.py new file mode 100644 index 0000000..3bfcfaa --- /dev/null +++ b/hrms/hr/doctype/identification_document_type/identification_document_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class IdentificationDocumentType(Document): + pass diff --git a/hrms/hr/doctype/identification_document_type/test_identification_document_type.py b/hrms/hr/doctype/identification_document_type/test_identification_document_type.py new file mode 100644 index 0000000..3e8f7ab --- /dev/null +++ b/hrms/hr/doctype/identification_document_type/test_identification_document_type.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestIdentificationDocumentType(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/interest/__init__.py b/hrms/hr/doctype/interest/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interest/interest.js b/hrms/hr/doctype/interest/interest.js new file mode 100644 index 0000000..70e1b6a --- /dev/null +++ b/hrms/hr/doctype/interest/interest.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Interest', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/interest/interest.json b/hrms/hr/doctype/interest/interest.json new file mode 100644 index 0000000..d6d2342 --- /dev/null +++ b/hrms/hr/doctype/interest/interest.json @@ -0,0 +1,144 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:interest", + "beta": 0, + "creation": "2016-07-25 07:12:33.600702", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "interest", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Interest", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-08-01 05:47:29.479141", + "modified_by": "Administrator", + "module": "HR", + "name": "Interest", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interest/interest.py b/hrms/hr/doctype/interest/interest.py new file mode 100644 index 0000000..3563f7f --- /dev/null +++ b/hrms/hr/doctype/interest/interest.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class Interest(Document): + pass diff --git a/hrms/hr/doctype/interest/test_interest.py b/hrms/hr/doctype/interest/test_interest.py new file mode 100644 index 0000000..eacb57f --- /dev/null +++ b/hrms/hr/doctype/interest/test_interest.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +# test_records = frappe.get_test_records('Interest') + + +class TestInterest(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/interview/__init__.py b/hrms/hr/doctype/interview/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interview/interview.js b/hrms/hr/doctype/interview/interview.js new file mode 100644 index 0000000..6866b50 --- /dev/null +++ b/hrms/hr/doctype/interview/interview.js @@ -0,0 +1,237 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Interview', { + onload: function (frm) { + frm.events.set_job_applicant_query(frm); + + frm.set_query('interviewer', 'interview_details', function () { + return { + query: 'hrms.hr.doctype.interview.interview.get_interviewer_list' + }; + }); + }, + + refresh: function (frm) { + if (frm.doc.docstatus != 2 && !frm.doc.__islocal) { + if (frm.doc.status === 'Pending') { + frm.add_custom_button(__('Reschedule Interview'), function() { + frm.events.show_reschedule_dialog(frm); + frm.refresh(); + }); + } + + let allowed_interviewers = []; + frm.doc.interview_details.forEach(values => { + allowed_interviewers.push(values.interviewer); + }); + + if ((allowed_interviewers.includes(frappe.session.user))) { + frappe.db.get_value('Interview Feedback', {'interviewer': frappe.session.user, 'interview': frm.doc.name, 'docstatus': 1}, 'name', (r) => { + if (Object.keys(r).length === 0) { + frm.add_custom_button(__('Submit Feedback'), function () { + frappe.call({ + method: 'hrms.hr.doctype.interview.interview.get_expected_skill_set', + args: { + interview_round: frm.doc.interview_round + }, + callback: function (r) { + frm.events.show_feedback_dialog(frm, r.message); + frm.refresh(); + } + }); + }).addClass('btn-primary'); + } + }); + } + } + }, + + show_reschedule_dialog: function (frm) { + let d = new frappe.ui.Dialog({ + title: 'Reschedule Interview', + fields: [ + { + label: 'Schedule On', + fieldname: 'scheduled_on', + fieldtype: 'Date', + reqd: 1 + }, + { + label: 'From Time', + fieldname: 'from_time', + fieldtype: 'Time', + reqd: 1 + }, + { + label: 'To Time', + fieldname: 'to_time', + fieldtype: 'Time', + reqd: 1 + } + ], + primary_action_label: 'Reschedule', + primary_action(values) { + frm.call({ + method: 'reschedule_interview', + doc: frm.doc, + args: { + scheduled_on: values.scheduled_on, + from_time: values.from_time, + to_time: values.to_time + } + }).then(() => { + frm.refresh(); + d.hide(); + }); + } + }); + d.show(); + }, + + show_feedback_dialog: function (frm, data) { + let fields = frm.events.get_fields_for_feedback(); + + let d = new frappe.ui.Dialog({ + title: __('Submit Feedback'), + fields: [ + { + fieldname: 'skill_set', + fieldtype: 'Table', + label: __('Skill Assessment'), + cannot_add_rows: false, + in_editable_grid: true, + reqd: 1, + fields: fields, + data: data + }, + { + fieldname: 'result', + fieldtype: 'Select', + options: ['', 'Cleared', 'Rejected'], + label: __('Result') + }, + { + fieldname: 'feedback', + fieldtype: 'Small Text', + label: __('Feedback') + } + ], + size: 'large', + minimizable: true, + primary_action: function(values) { + frappe.call({ + method: 'hrms.hr.doctype.interview.interview.create_interview_feedback', + args: { + data: values, + interview_name: frm.doc.name, + interviewer: frappe.session.user, + job_applicant: frm.doc.job_applicant + } + }).then(() => { + frm.refresh(); + }); + d.hide(); + } + }); + d.show(); + }, + + get_fields_for_feedback: function () { + return [{ + fieldtype: 'Link', + fieldname: 'skill', + options: 'Skill', + in_list_view: 1, + label: __('Skill') + }, { + fieldtype: 'Rating', + fieldname: 'rating', + label: __('Rating'), + in_list_view: 1, + reqd: 1, + }]; + }, + + set_job_applicant_query: function (frm) { + frm.set_query('job_applicant', function () { + let job_applicant_filters = { + status: ['!=', 'Rejected'] + }; + if (frm.doc.designation) { + job_applicant_filters.designation = frm.doc.designation; + } + return { + filters: job_applicant_filters + }; + }); + }, + + interview_round: async function (frm) { + frm.events.reset_values(frm); + frm.set_value('job_applicant', ''); + + let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message; + frm.set_value('designation', round_data.designation); + frm.events.set_job_applicant_query(frm); + + if (frm.doc.interview_round) { + frm.events.set_interview_details(frm); + } else { + frm.set_value('interview_details', []); + } + }, + + set_interview_details: function (frm) { + frappe.call({ + method: 'hrms.hr.doctype.interview.interview.get_interviewers', + args: { + interview_round: frm.doc.interview_round + }, + callback: function (data) { + let interview_details = data.message; + frm.set_value('interview_details', []); + if (data.message.length) { + frm.set_value('interview_details', interview_details); + } + } + }); + }, + + job_applicant: function (frm) { + if (!frm.doc.interview_round) { + frm.doc.job_applicant = ''; + frm.refresh(); + frappe.throw(__('Select Interview Round First')); + } + + if (frm.doc.job_applicant) { + frm.events.set_designation_and_job_opening(frm); + } else { + frm.events.reset_values(frm); + } + }, + + set_designation_and_job_opening: async function (frm) { + let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message; + frm.set_value('designation', round_data.designation); + frm.events.set_job_applicant_query(frm); + + let job_applicant_data = (await frappe.db.get_value( + 'Job Applicant', frm.doc.job_applicant, ['designation', 'job_title', 'resume_link'], + )).message; + + if (!round_data.designation) { + frm.set_value('designation', job_applicant_data.designation); + } + + frm.set_value('job_opening', job_applicant_data.job_title); + frm.set_value('resume_link', job_applicant_data.resume_link); + }, + + reset_values: function (frm) { + frm.set_value('designation', ''); + frm.set_value('job_opening', ''); + frm.set_value('resume_link', ''); + } +}); diff --git a/hrms/hr/doctype/interview/interview.json b/hrms/hr/doctype/interview/interview.json new file mode 100644 index 0000000..0d393e7 --- /dev/null +++ b/hrms/hr/doctype/interview/interview.json @@ -0,0 +1,254 @@ +{ + "actions": [], + "autoname": "HR-INT-.YYYY.-.####", + "creation": "2021-04-12 15:03:11.524090", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "interview_details_section", + "interview_round", + "job_applicant", + "job_opening", + "designation", + "resume_link", + "column_break_4", + "status", + "scheduled_on", + "from_time", + "to_time", + "interview_feedback_section", + "interview_details", + "ratings_section", + "expected_average_rating", + "column_break_12", + "average_rating", + "section_break_13", + "interview_summary", + "reminded", + "amended_from" + ], + "fields": [ + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Job Applicant", + "options": "Job Applicant", + "reqd": 1 + }, + { + "fieldname": "job_opening", + "fieldtype": "Link", + "label": "Job Opening", + "options": "Job Opening", + "read_only": 1 + }, + { + "fieldname": "interview_round", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Interview Round", + "options": "Interview Round", + "reqd": 1 + }, + { + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Pending\nUnder Review\nCleared\nRejected", + "reqd": 1 + }, + { + "fieldname": "ratings_section", + "fieldtype": "Section Break", + "label": "Ratings" + }, + { + "allow_on_submit": 1, + "fieldname": "average_rating", + "fieldtype": "Rating", + "in_list_view": 1, + "label": "Obtained Average Rating", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "interview_summary", + "fieldtype": "Text" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "resume_link", + "fieldtype": "Data", + "label": "Resume link" + }, + { + "fieldname": "interview_details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "fetch_from": "interview_round.expected_average_rating", + "fieldname": "expected_average_rating", + "fieldtype": "Rating", + "label": "Expected Average Rating", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_13", + "fieldtype": "Section Break", + "label": "Interview Summary" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fetch_from": "interview_round.designation", + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Interview", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "scheduled_on", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Scheduled On", + "reqd": 1, + "set_only_once": 1 + }, + { + "default": "0", + "fieldname": "reminded", + "fieldtype": "Check", + "hidden": 1, + "label": "Reminded" + }, + { + "allow_on_submit": 1, + "fieldname": "interview_details", + "fieldtype": "Table", + "options": "Interview Detail" + }, + { + "fieldname": "interview_feedback_section", + "fieldtype": "Section Break", + "label": "Feedback" + }, + { + "fieldname": "from_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "From Time", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "to_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "To Time", + "reqd": 1, + "set_only_once": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [ + { + "link_doctype": "Interview Feedback", + "link_fieldname": "interview" + } + ], + "modified": "2021-09-30 13:30:05.421035", + "modified_by": "Administrator", + "module": "HR", + "name": "Interview", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Interviewer", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "job_applicant", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interview/interview.py b/hrms/hr/doctype/interview/interview.py new file mode 100644 index 0000000..d730428 --- /dev/null +++ b/hrms/hr/doctype/interview/interview.py @@ -0,0 +1,360 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import datetime + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import cstr, flt, get_datetime, get_link_to_form + + +class DuplicateInterviewRoundError(frappe.ValidationError): + pass + + +class Interview(Document): + def validate(self): + self.validate_duplicate_interview() + self.validate_designation() + self.validate_overlap() + self.set_average_rating() + + def on_submit(self): + if self.status not in ["Cleared", "Rejected"]: + frappe.throw( + _("Only Interviews with Cleared or Rejected status can be submitted."), title=_("Not Allowed") + ) + + def validate_duplicate_interview(self): + duplicate_interview = frappe.db.exists( + "Interview", + {"job_applicant": self.job_applicant, "interview_round": self.interview_round, "docstatus": 1}, + ) + + if duplicate_interview: + frappe.throw( + _( + "Job Applicants are not allowed to appear twice for the same Interview round. Interview {0} already scheduled for Job Applicant {1}" + ).format( + frappe.bold(get_link_to_form("Interview", duplicate_interview)), + frappe.bold(self.job_applicant), + ) + ) + + def validate_designation(self): + applicant_designation = frappe.db.get_value("Job Applicant", self.job_applicant, "designation") + if self.designation: + if self.designation != applicant_designation: + frappe.throw( + _( + "Interview Round {0} is only for Designation {1}. Job Applicant has applied for the role {2}" + ).format( + self.interview_round, frappe.bold(self.designation), applicant_designation + ), + exc=DuplicateInterviewRoundError, + ) + else: + self.designation = applicant_designation + + def validate_overlap(self): + interviewers = [entry.interviewer for entry in self.interview_details] or [""] + + overlaps = frappe.db.sql( + """ + SELECT interview.name + FROM `tabInterview` as interview + INNER JOIN `tabInterview Detail` as detail + WHERE + interview.scheduled_on = %s and interview.name != %s and interview.docstatus != 2 + and (interview.job_applicant = %s or detail.interviewer IN %s) and + ((from_time < %s and to_time > %s) or + (from_time > %s and to_time < %s) or + (from_time = %s)) + """, + ( + self.scheduled_on, + self.name, + self.job_applicant, + interviewers, + self.from_time, + self.to_time, + self.from_time, + self.to_time, + self.from_time, + ), + ) + + if overlaps: + overlapping_details = _("Interview overlaps with {0}").format( + get_link_to_form("Interview", overlaps[0][0]) + ) + frappe.throw(overlapping_details, title=_("Overlap")) + + def set_average_rating(self): + total_rating = 0 + for entry in self.interview_details: + if entry.average_rating: + total_rating += entry.average_rating + + self.average_rating = flt( + total_rating / len(self.interview_details) if len(self.interview_details) else 0 + ) + + @frappe.whitelist() + def reschedule_interview(self, scheduled_on, from_time, to_time): + original_date = self.scheduled_on + original_from_time = self.from_time + original_to_time = self.to_time + + self.db_set({"scheduled_on": scheduled_on, "from_time": from_time, "to_time": to_time}) + self.notify_update() + + recipients = get_recipients(self.name) + + try: + frappe.sendmail( + recipients=recipients, + subject=_("Interview: {0} Rescheduled").format(self.name), + message=_("Your Interview session is rescheduled from {0} {1} - {2} to {3} {4} - {5}").format( + original_date, + original_from_time, + original_to_time, + self.scheduled_on, + self.from_time, + self.to_time, + ), + reference_doctype=self.doctype, + reference_name=self.name, + ) + except Exception: + frappe.msgprint( + _("Failed to send the Interview Reschedule notification. Please configure your email account.") + ) + + frappe.msgprint(_("Interview Rescheduled successfully"), indicator="green") + + +def get_recipients(name, for_feedback=0): + interview = frappe.get_doc("Interview", name) + + if for_feedback: + recipients = [d.interviewer for d in interview.interview_details if not d.interview_feedback] + else: + recipients = [d.interviewer for d in interview.interview_details] + recipients.append(frappe.db.get_value("Job Applicant", interview.job_applicant, "email_id")) + + return recipients + + +@frappe.whitelist() +def get_interviewers(interview_round): + return frappe.get_all( + "Interviewer", filters={"parent": interview_round}, fields=["user as interviewer"] + ) + + +def send_interview_reminder(): + reminder_settings = frappe.db.get_value( + "HR Settings", + "HR Settings", + ["send_interview_reminder", "interview_reminder_template"], + as_dict=True, + ) + + if not reminder_settings.send_interview_reminder: + return + + remind_before = cstr(frappe.db.get_single_value("HR Settings", "remind_before")) or "01:00:00" + remind_before = datetime.datetime.strptime(remind_before, "%H:%M:%S") + reminder_date_time = datetime.datetime.now() + datetime.timedelta( + hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second + ) + + interviews = frappe.get_all( + "Interview", + filters={ + "scheduled_on": ["between", (datetime.datetime.now(), reminder_date_time)], + "status": "Pending", + "reminded": 0, + "docstatus": ["!=", 2], + }, + ) + + interview_template = frappe.get_doc( + "Email Template", reminder_settings.interview_reminder_template + ) + + for d in interviews: + doc = frappe.get_doc("Interview", d.name) + context = doc.as_dict() + message = frappe.render_template(interview_template.response, context) + recipients = get_recipients(doc.name) + + frappe.sendmail( + recipients=recipients, + subject=interview_template.subject, + message=message, + reference_doctype=doc.doctype, + reference_name=doc.name, + ) + + doc.db_set("reminded", 1) + + +def send_daily_feedback_reminder(): + reminder_settings = frappe.db.get_value( + "HR Settings", + "HR Settings", + ["send_interview_feedback_reminder", "feedback_reminder_notification_template"], + as_dict=True, + ) + + if not reminder_settings.send_interview_feedback_reminder: + return + + interview_feedback_template = frappe.get_doc( + "Email Template", reminder_settings.feedback_reminder_notification_template + ) + interviews = frappe.get_all( + "Interview", filters={"status": ["in", ["Under Review", "Pending"]], "docstatus": ["!=", 2]} + ) + + for entry in interviews: + recipients = get_recipients(entry.name, for_feedback=1) + + doc = frappe.get_doc("Interview", entry.name) + context = doc.as_dict() + + message = frappe.render_template(interview_feedback_template.response, context) + + if len(recipients): + frappe.sendmail( + recipients=recipients, + subject=interview_feedback_template.subject, + message=message, + reference_doctype="Interview", + reference_name=entry.name, + ) + + +@frappe.whitelist() +def get_expected_skill_set(interview_round): + return frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields=["skill"]) + + +@frappe.whitelist() +def create_interview_feedback(data, interview_name, interviewer, job_applicant): + import json + + if isinstance(data, str): + data = frappe._dict(json.loads(data)) + + if frappe.session.user != interviewer: + frappe.throw(_("Only Interviewer Are allowed to submit Interview Feedback")) + + interview_feedback = frappe.new_doc("Interview Feedback") + interview_feedback.interview = interview_name + interview_feedback.interviewer = interviewer + interview_feedback.job_applicant = job_applicant + + for d in data.skill_set: + d = frappe._dict(d) + interview_feedback.append("skill_assessment", {"skill": d.skill, "rating": d.rating}) + + interview_feedback.feedback = data.feedback + interview_feedback.result = data.result + + interview_feedback.save() + interview_feedback.submit() + + frappe.msgprint( + _("Interview Feedback {0} submitted successfully").format( + get_link_to_form("Interview Feedback", interview_feedback.name) + ) + ) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_interviewer_list(doctype, txt, searchfield, start, page_len, filters): + filters = [ + ["Has Role", "parent", "like", "%{}%".format(txt)], + ["Has Role", "role", "=", "interviewer"], + ["Has Role", "parenttype", "=", "User"], + ] + + if filters and isinstance(filters, list): + filters.extend(filters) + + return frappe.get_all( + "Has Role", + limit_start=start, + limit_page_length=page_len, + filters=filters, + fields=["parent"], + as_list=1, + ) + + +@frappe.whitelist() +def get_events(start, end, filters=None): + """Returns events for Gantt / Calendar view rendering. + + :param start: Start date-time. + :param end: End date-time. + :param filters: Filters (JSON). + """ + from frappe.desk.calendar import get_event_conditions + + events = [] + + event_color = { + "Pending": "#fff4f0", + "Under Review": "#d3e8fc", + "Cleared": "#eaf5ed", + "Rejected": "#fce7e7", + } + + conditions = get_event_conditions("Interview", filters) + + interviews = frappe.db.sql( + """ + SELECT DISTINCT + `tabInterview`.name, `tabInterview`.job_applicant, `tabInterview`.interview_round, + `tabInterview`.scheduled_on, `tabInterview`.status, `tabInterview`.from_time as from_time, + `tabInterview`.to_time as to_time + from + `tabInterview` + where + (`tabInterview`.scheduled_on between %(start)s and %(end)s) + and docstatus != 2 + {conditions} + """.format( + conditions=conditions + ), + {"start": start, "end": end}, + as_dict=True, + update={"allDay": 0}, + ) + + for d in interviews: + subject_data = [] + for field in ["name", "job_applicant", "interview_round"]: + if not d.get(field): + continue + subject_data.append(d.get(field)) + + color = event_color.get(d.status) + interview_data = { + "from": get_datetime("%s %s" % (d.scheduled_on, d.from_time or "00:00:00")), + "to": get_datetime("%s %s" % (d.scheduled_on, d.to_time or "00:00:00")), + "name": d.name, + "subject": "\n".join(subject_data), + "color": color if color else "#89bcde", + } + + events.append(interview_data) + + return events diff --git a/hrms/hr/doctype/interview/interview_calendar.js b/hrms/hr/doctype/interview/interview_calendar.js new file mode 100644 index 0000000..6d12134 --- /dev/null +++ b/hrms/hr/doctype/interview/interview_calendar.js @@ -0,0 +1,14 @@ + +frappe.views.calendar['Interview'] = { + field_map: { + 'start': 'from', + 'end': 'to', + 'id': 'name', + 'title': 'subject', + 'allDay': 'allDay', + 'color': 'color' + }, + order_by: 'scheduled_on', + gantt: true, + get_events_method: 'hrms.hr.doctype.interview.interview.get_events' +}; diff --git a/hrms/hr/doctype/interview/interview_feedback_reminder_template.html b/hrms/hr/doctype/interview/interview_feedback_reminder_template.html new file mode 100644 index 0000000..8d39fb5 --- /dev/null +++ b/hrms/hr/doctype/interview/interview_feedback_reminder_template.html @@ -0,0 +1,5 @@ +

Interview Feedback Reminder

+ +

+ Interview Feedback for Interview {{ name }} is not submitted yet. Please submit your feedback. Thank you, good day! +

diff --git a/hrms/hr/doctype/interview/interview_list.js b/hrms/hr/doctype/interview/interview_list.js new file mode 100644 index 0000000..b1f072f --- /dev/null +++ b/hrms/hr/doctype/interview/interview_list.js @@ -0,0 +1,12 @@ +frappe.listview_settings['Interview'] = { + has_indicator_for_draft: 1, + get_indicator: function(doc) { + let status_color = { + 'Pending': 'orange', + 'Under Review': 'blue', + 'Cleared': 'green', + 'Rejected': 'red', + }; + return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status]; + } +}; diff --git a/hrms/hr/doctype/interview/interview_reminder_notification_template.html b/hrms/hr/doctype/interview/interview_reminder_notification_template.html new file mode 100644 index 0000000..76de46e --- /dev/null +++ b/hrms/hr/doctype/interview/interview_reminder_notification_template.html @@ -0,0 +1,5 @@ +

Interview Reminder

+ +

+ Interview: {{name}} is scheduled on {{scheduled_on}} from {{from_time}} to {{to_time}} +

diff --git a/hrms/hr/doctype/interview/test_interview.py b/hrms/hr/doctype/interview/test_interview.py new file mode 100644 index 0000000..c1cee01 --- /dev/null +++ b/hrms/hr/doctype/interview/test_interview.py @@ -0,0 +1,215 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import datetime +import os +import unittest + +import frappe +from frappe import _ +from frappe.core.doctype.user_permission.test_user_permission import create_user +from frappe.utils import add_days, get_time, getdate, nowtime + +from erpnext.setup.doctype.designation.test_designation import create_designation + +from hrms.hr.doctype.interview.interview import DuplicateInterviewRoundError +from hrms.hr.doctype.job_applicant.job_applicant import get_interview_details +from hrms.hr.doctype.job_applicant.test_job_applicant import create_job_applicant + + +class TestInterview(unittest.TestCase): + def test_validations_for_designation(self): + job_applicant = create_job_applicant() + interview = create_interview_and_dependencies( + job_applicant.name, designation="_Test_Sales_manager", save=0 + ) + self.assertRaises(DuplicateInterviewRoundError, interview.save) + + def test_notification_on_rescheduling(self): + job_applicant = create_job_applicant() + interview = create_interview_and_dependencies( + job_applicant.name, + scheduled_on=add_days(getdate(), -4), + from_time="10:00:00", + to_time="11:00:00", + ) + + previous_scheduled_date = interview.scheduled_on + frappe.db.sql("DELETE FROM `tabEmail Queue`") + + interview.reschedule_interview( + add_days(getdate(previous_scheduled_date), 2), from_time="11:00:00", to_time="12:00:00" + ) + interview.reload() + + self.assertEqual(interview.scheduled_on, add_days(getdate(previous_scheduled_date), 2)) + self.assertEqual(get_time(interview.from_time), get_time("11:00:00")) + self.assertEqual(get_time(interview.to_time), get_time("12:00:00")) + + notification = frappe.get_all( + "Email Queue", filters={"message": ("like", "%Your Interview session is rescheduled from%")} + ) + self.assertIsNotNone(notification) + + def test_notification_for_scheduling(self): + from hrms.hr.doctype.interview.interview import send_interview_reminder + + setup_reminder_settings() + + job_applicant = create_job_applicant() + scheduled_on = datetime.datetime.now() + datetime.timedelta(minutes=10) + + interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on) + + frappe.db.sql("DELETE FROM `tabEmail Queue`") + send_interview_reminder() + + interview.reload() + + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertTrue("Subject: Interview Reminder" in email_queue[0].message) + + def test_notification_for_feedback_submission(self): + from hrms.hr.doctype.interview.interview import send_daily_feedback_reminder + + setup_reminder_settings() + + job_applicant = create_job_applicant() + scheduled_on = add_days(getdate(), -4) + create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on) + + frappe.db.sql("DELETE FROM `tabEmail Queue`") + send_daily_feedback_reminder() + + email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + self.assertTrue("Subject: Interview Feedback Reminder" in email_queue[0].message) + + def test_get_interview_details_for_applicant_dashboard(self): + job_applicant = create_job_applicant() + interview = create_interview_and_dependencies(job_applicant.name) + + details = get_interview_details(job_applicant.name) + self.assertEqual(details.get("stars"), 5) + self.assertEqual( + details.get("interviews").get(interview.name), + { + "name": interview.name, + "interview_round": interview.interview_round, + "scheduled_on": interview.scheduled_on, + "average_rating": interview.average_rating * 5, + "status": "Pending", + }, + ) + + def tearDown(self): + frappe.db.rollback() + + +def create_interview_and_dependencies( + job_applicant, scheduled_on=None, from_time=None, to_time=None, designation=None, save=1 +): + if designation: + designation = create_designation(designation_name="_Test_Sales_manager").name + + interviewer_1 = create_user("test_interviewer1@example.com", "Interviewer") + interviewer_2 = create_user("test_interviewer2@example.com", "Interviewer") + + interview_round = create_interview_round( + "Technical Round", ["Python", "JS"], designation=designation, save=True + ) + + interview = frappe.new_doc("Interview") + interview.interview_round = interview_round.name + interview.job_applicant = job_applicant + interview.scheduled_on = scheduled_on or getdate() + interview.from_time = from_time or nowtime() + interview.to_time = to_time or nowtime() + + interview.append("interview_details", {"interviewer": interviewer_1.name}) + interview.append("interview_details", {"interviewer": interviewer_2.name}) + + if save: + interview.save() + + return interview + + +def create_interview_round(name, skill_set, interviewers=[], designation=None, save=True): + create_skill_set(skill_set) + interview_round = frappe.new_doc("Interview Round") + interview_round.round_name = name + interview_round.interview_type = create_interview_type() + # average rating = 4 + interview_round.expected_average_rating = 0.8 + if designation: + interview_round.designation = designation + + for skill in skill_set: + interview_round.append("expected_skill_set", {"skill": skill}) + + for interviewer in interviewers: + interview_round.append("interviewer", {"user": interviewer}) + + if save: + interview_round.save() + + return interview_round + + +def create_skill_set(skill_set): + for skill in skill_set: + if not frappe.db.exists("Skill", skill): + doc = frappe.new_doc("Skill") + doc.skill_name = skill + doc.save() + + +def create_interview_type(name="test_interview_type"): + if frappe.db.exists("Interview Type", name): + return frappe.get_doc("Interview Type", name).name + else: + doc = frappe.new_doc("Interview Type") + doc.name = name + doc.description = "_Test_Description" + doc.save() + + return doc.name + + +def setup_reminder_settings(): + if not frappe.db.exists("Email Template", _("Interview Reminder")): + base_path = frappe.get_app_path("erpnext", "hr", "doctype") + response = frappe.read_file( + os.path.join(base_path, "interview/interview_reminder_notification_template.html") + ) + + frappe.get_doc( + { + "doctype": "Email Template", + "name": _("Interview Reminder"), + "response": response, + "subject": _("Interview Reminder"), + "owner": frappe.session.user, + } + ).insert(ignore_permissions=True) + + if not frappe.db.exists("Email Template", _("Interview Feedback Reminder")): + base_path = frappe.get_app_path("erpnext", "hr", "doctype") + response = frappe.read_file( + os.path.join(base_path, "interview/interview_feedback_reminder_template.html") + ) + + frappe.get_doc( + { + "doctype": "Email Template", + "name": _("Interview Feedback Reminder"), + "response": response, + "subject": _("Interview Feedback Reminder"), + "owner": frappe.session.user, + } + ).insert(ignore_permissions=True) + + hr_settings = frappe.get_doc("HR Settings") + hr_settings.interview_reminder_template = _("Interview Reminder") + hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder") + hr_settings.save() diff --git a/hrms/hr/doctype/interview_detail/__init__.py b/hrms/hr/doctype/interview_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interview_detail/interview_detail.js b/hrms/hr/doctype/interview_detail/interview_detail.js new file mode 100644 index 0000000..88518ca --- /dev/null +++ b/hrms/hr/doctype/interview_detail/interview_detail.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Interview Detail', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/interview_detail/interview_detail.json b/hrms/hr/doctype/interview_detail/interview_detail.json new file mode 100644 index 0000000..b5b49c0 --- /dev/null +++ b/hrms/hr/doctype/interview_detail/interview_detail.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "creation": "2021-04-12 16:24:10.382863", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "interviewer", + "interview_feedback", + "average_rating", + "result", + "column_break_4", + "comments" + ], + "fields": [ + { + "fieldname": "interviewer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Interviewer", + "options": "User" + }, + { + "allow_on_submit": 1, + "fieldname": "interview_feedback", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Interview Feedback", + "options": "Interview Feedback", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "average_rating", + "fieldtype": "Rating", + "in_list_view": 1, + "label": "Average Rating", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fetch_from": "interview_feedback.feedback", + "fieldname": "comments", + "fieldtype": "Text", + "label": "Comments", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "result", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Result", + "options": "\nCleared\nRejected", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-29 13:13:25.865063", + "modified_by": "Administrator", + "module": "HR", + "name": "Interview Detail", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interview_detail/interview_detail.py b/hrms/hr/doctype/interview_detail/interview_detail.py new file mode 100644 index 0000000..d44e29a --- /dev/null +++ b/hrms/hr/doctype/interview_detail/interview_detail.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class InterviewDetail(Document): + pass diff --git a/hrms/hr/doctype/interview_detail/test_interview_detail.py b/hrms/hr/doctype/interview_detail/test_interview_detail.py new file mode 100644 index 0000000..68a1f72 --- /dev/null +++ b/hrms/hr/doctype/interview_detail/test_interview_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestInterviewDetail(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/interview_feedback/__init__.py b/hrms/hr/doctype/interview_feedback/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interview_feedback/interview_feedback.js b/hrms/hr/doctype/interview_feedback/interview_feedback.js new file mode 100644 index 0000000..2c5b6a7 --- /dev/null +++ b/hrms/hr/doctype/interview_feedback/interview_feedback.js @@ -0,0 +1,54 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Interview Feedback', { + onload: function(frm) { + frm.ignore_doctypes_on_cancel_all = ['Interview']; + + frm.set_query('interview', function() { + return { + filters: { + docstatus: ['!=', 2] + } + }; + }); + }, + + interview_round: function(frm) { + frappe.call({ + method: 'hrms.hr.doctype.interview.interview.get_expected_skill_set', + args: { + interview_round: frm.doc.interview_round + }, + callback: function(r) { + frm.set_value('skill_assessment', r.message); + } + }); + }, + + interview: function(frm) { + frappe.call({ + method: 'hrms.hr.doctype.interview_feedback.interview_feedback.get_applicable_interviewers', + args: { + interview: frm.doc.interview || '' + }, + callback: function(r) { + frm.set_query('interviewer', function() { + return { + filters: { + name: ['in', r.message] + } + }; + }); + } + }); + + }, + + interviewer: function(frm) { + if (!frm.doc.interview) { + frappe.throw(__('Select Interview first')); + frm.set_value('interviewer', ''); + } + } +}); diff --git a/hrms/hr/doctype/interview_feedback/interview_feedback.json b/hrms/hr/doctype/interview_feedback/interview_feedback.json new file mode 100644 index 0000000..6a2f7e8 --- /dev/null +++ b/hrms/hr/doctype/interview_feedback/interview_feedback.json @@ -0,0 +1,171 @@ +{ + "actions": [], + "autoname": "HR-INT-FEED-.####", + "creation": "2021-04-12 17:03:13.833285", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "details_section", + "interview", + "interview_round", + "job_applicant", + "column_break_3", + "interviewer", + "result", + "section_break_4", + "skill_assessment", + "average_rating", + "section_break_7", + "feedback", + "amended_from" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "interview", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Interview", + "options": "Interview", + "reqd": 1 + }, + { + "allow_in_quick_entry": 1, + "fetch_from": "interview.interview_round", + "fieldname": "interview_round", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Interview Round", + "options": "Interview Round", + "read_only": 1, + "reqd": 1 + }, + { + "allow_in_quick_entry": 1, + "fieldname": "interviewer", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Interviewer", + "options": "User", + "reqd": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Skill Assessment" + }, + { + "allow_in_quick_entry": 1, + "fieldname": "skill_assessment", + "fieldtype": "Table", + "options": "Skill Assessment", + "reqd": 1 + }, + { + "allow_in_quick_entry": 1, + "fieldname": "average_rating", + "fieldtype": "Rating", + "in_list_view": 1, + "label": "Average Rating", + "read_only": 1 + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Feedback" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Interview Feedback", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_in_quick_entry": 1, + "fieldname": "feedback", + "fieldtype": "Text" + }, + { + "fieldname": "result", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Result", + "options": "\nCleared\nRejected", + "reqd": 1 + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "fetch_from": "interview.job_applicant", + "fieldname": "job_applicant", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Job Applicant", + "options": "Job Applicant", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-09-30 13:30:49.955352", + "modified_by": "Administrator", + "module": "HR", + "name": "Interview Feedback", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Interviewer", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "interviewer", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interview_feedback/interview_feedback.py b/hrms/hr/doctype/interview_feedback/interview_feedback.py new file mode 100644 index 0000000..5bb498f --- /dev/null +++ b/hrms/hr/doctype/interview_feedback/interview_feedback.py @@ -0,0 +1,91 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt, get_link_to_form, getdate + + +class InterviewFeedback(Document): + def validate(self): + self.validate_interviewer() + self.validate_interview_date() + self.validate_duplicate() + self.calculate_average_rating() + + def on_submit(self): + self.update_interview_details() + + def on_cancel(self): + self.update_interview_details() + + def validate_interviewer(self): + applicable_interviewers = get_applicable_interviewers(self.interview) + if self.interviewer not in applicable_interviewers: + frappe.throw( + _("{0} is not allowed to submit Interview Feedback for the Interview: {1}").format( + frappe.bold(self.interviewer), frappe.bold(self.interview) + ) + ) + + def validate_interview_date(self): + scheduled_date = frappe.db.get_value("Interview", self.interview, "scheduled_on") + + if getdate() < getdate(scheduled_date) and self.docstatus == 1: + frappe.throw( + _("{0} submission before {1} is not allowed").format( + frappe.bold("Interview Feedback"), frappe.bold("Interview Scheduled Date") + ) + ) + + def validate_duplicate(self): + duplicate_feedback = frappe.db.exists( + "Interview Feedback", + {"interviewer": self.interviewer, "interview": self.interview, "docstatus": 1}, + ) + + if duplicate_feedback: + frappe.throw( + _( + "Feedback already submitted for the Interview {0}. Please cancel the previous Interview Feedback {1} to continue." + ).format( + self.interview, get_link_to_form("Interview Feedback", duplicate_feedback) + ) + ) + + def calculate_average_rating(self): + total_rating = 0 + for d in self.skill_assessment: + if d.rating: + total_rating += d.rating + + self.average_rating = flt( + total_rating / len(self.skill_assessment) if len(self.skill_assessment) else 0 + ) + + def update_interview_details(self): + doc = frappe.get_doc("Interview", self.interview) + + if self.docstatus == 2: + for entry in doc.interview_details: + if entry.interview_feedback == self.name: + entry.average_rating = entry.interview_feedback = entry.comments = entry.result = None + break + else: + for entry in doc.interview_details: + if entry.interviewer == self.interviewer: + entry.average_rating = self.average_rating + entry.interview_feedback = self.name + entry.comments = self.feedback + entry.result = self.result + + doc.save() + doc.notify_update() + + +@frappe.whitelist() +def get_applicable_interviewers(interview): + data = frappe.get_all("Interview Detail", filters={"parent": interview}, fields=["interviewer"]) + return [d.interviewer for d in data] diff --git a/hrms/hr/doctype/interview_feedback/test_interview_feedback.py b/hrms/hr/doctype/interview_feedback/test_interview_feedback.py new file mode 100644 index 0000000..8cfc0e6 --- /dev/null +++ b/hrms/hr/doctype/interview_feedback/test_interview_feedback.py @@ -0,0 +1,113 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, flt, getdate + +from hrms.hr.doctype.interview.test_interview import ( + create_interview_and_dependencies, + create_skill_set, +) +from hrms.hr.doctype.job_applicant.test_job_applicant import create_job_applicant + + +class TestInterviewFeedback(unittest.TestCase): + def test_validation_for_skill_set(self): + frappe.set_user("Administrator") + job_applicant = create_job_applicant() + interview = create_interview_and_dependencies( + job_applicant.name, scheduled_on=add_days(getdate(), -1) + ) + skill_ratings = get_skills_rating(interview.interview_round) + + interviewer = interview.interview_details[0].interviewer + create_skill_set(["Leadership"]) + + interview_feedback = create_interview_feedback(interview.name, interviewer, skill_ratings) + interview_feedback.append("skill_assessment", {"skill": "Leadership", "rating": 0.8}) + frappe.set_user(interviewer) + + self.assertRaises(frappe.ValidationError, interview_feedback.save) + + frappe.set_user("Administrator") + + def test_average_ratings_on_feedback_submission_and_cancellation(self): + job_applicant = create_job_applicant() + interview = create_interview_and_dependencies( + job_applicant.name, scheduled_on=add_days(getdate(), -1) + ) + skill_ratings = get_skills_rating(interview.interview_round) + + # For First Interviewer Feedback + interviewer = interview.interview_details[0].interviewer + frappe.set_user(interviewer) + + # calculating Average + feedback_1 = create_interview_feedback(interview.name, interviewer, skill_ratings) + + total_rating = 0 + for d in feedback_1.skill_assessment: + if d.rating: + total_rating += d.rating + + avg_rating = flt( + total_rating / len(feedback_1.skill_assessment) if len(feedback_1.skill_assessment) else 0 + ) + + self.assertEqual(flt(avg_rating, 2), flt(feedback_1.average_rating, 2)) + + avg_on_interview_detail = frappe.db.get_value( + "Interview Detail", + { + "parent": feedback_1.interview, + "interviewer": feedback_1.interviewer, + "interview_feedback": feedback_1.name, + }, + "average_rating", + ) + + # 1. average should be reflected in Interview Detail. + self.assertEqual(flt(avg_on_interview_detail, 2), flt(feedback_1.average_rating, 2)) + + """For Second Interviewer Feedback""" + interviewer = interview.interview_details[1].interviewer + frappe.set_user(interviewer) + + feedback_2 = create_interview_feedback(interview.name, interviewer, skill_ratings) + interview.reload() + + feedback_2.cancel() + interview.reload() + + frappe.set_user("Administrator") + + def tearDown(self): + frappe.db.rollback() + + +def create_interview_feedback(interview, interviewer, skills_ratings): + interview_feedback = frappe.new_doc("Interview Feedback") + interview_feedback.interview = interview + interview_feedback.interviewer = interviewer + interview_feedback.result = "Cleared" + + for rating in skills_ratings: + interview_feedback.append("skill_assessment", rating) + + interview_feedback.save() + interview_feedback.submit() + + return interview_feedback + + +def get_skills_rating(interview_round): + import random + + skills = frappe.get_all( + "Expected Skill Set", filters={"parent": interview_round}, fields=["skill"] + ) + for d in skills: + d["rating"] = random.random() + return skills diff --git a/hrms/hr/doctype/interview_round/__init__.py b/hrms/hr/doctype/interview_round/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interview_round/interview_round.js b/hrms/hr/doctype/interview_round/interview_round.js new file mode 100644 index 0000000..115dd86 --- /dev/null +++ b/hrms/hr/doctype/interview_round/interview_round.js @@ -0,0 +1,24 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Interview Round", { + refresh: function(frm) { + if (!frm.doc.__islocal) { + frm.add_custom_button(__("Create Interview"), function() { + frm.events.create_interview(frm); + }); + } + }, + create_interview: function(frm) { + frappe.call({ + method: "hrms.hr.doctype.interview_round.interview_round.create_interview", + args: { + doc: frm.doc + }, + callback: function (r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + } +}); diff --git a/hrms/hr/doctype/interview_round/interview_round.json b/hrms/hr/doctype/interview_round/interview_round.json new file mode 100644 index 0000000..9c95185 --- /dev/null +++ b/hrms/hr/doctype/interview_round/interview_round.json @@ -0,0 +1,118 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:round_name", + "creation": "2021-04-12 12:57:19.902866", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "round_name", + "interview_type", + "interviewers", + "column_break_3", + "designation", + "expected_average_rating", + "expected_skills_section", + "expected_skill_set" + ], + "fields": [ + { + "fieldname": "round_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Round Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation" + }, + { + "fieldname": "expected_skills_section", + "fieldtype": "Section Break", + "label": "Expected Skillset" + }, + { + "fieldname": "expected_skill_set", + "fieldtype": "Table", + "options": "Expected Skill Set", + "reqd": 1 + }, + { + "fieldname": "expected_average_rating", + "fieldtype": "Rating", + "label": "Expected Average Rating", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "interview_type", + "fieldtype": "Link", + "label": "Interview Type", + "options": "Interview Type", + "reqd": 1 + }, + { + "fieldname": "interviewers", + "fieldtype": "Table MultiSelect", + "label": "Interviewers", + "options": "Interviewer" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-09-30 13:01:25.666660", + "modified_by": "Administrator", + "module": "HR", + "name": "Interview Round", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Interviewer", + "select": 1, + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interview_round/interview_round.py b/hrms/hr/doctype/interview_round/interview_round.py new file mode 100644 index 0000000..83dbf0e --- /dev/null +++ b/hrms/hr/doctype/interview_round/interview_round.py @@ -0,0 +1,29 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import json + +import frappe +from frappe.model.document import Document + + +class InterviewRound(Document): + pass + + +@frappe.whitelist() +def create_interview(doc): + if isinstance(doc, str): + doc = json.loads(doc) + doc = frappe.get_doc(doc) + + interview = frappe.new_doc("Interview") + interview.interview_round = doc.name + interview.designation = doc.designation + + if doc.interviewers: + interview.interview_details = [] + for data in doc.interviewers: + interview.append("interview_details", {"interviewer": data.user}) + return interview diff --git a/hrms/hr/doctype/interview_round/test_interview_round.py b/hrms/hr/doctype/interview_round/test_interview_round.py new file mode 100644 index 0000000..9568165 --- /dev/null +++ b/hrms/hr/doctype/interview_round/test_interview_round.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +# import frappe + + +class TestInterviewRound(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/interview_type/__init__.py b/hrms/hr/doctype/interview_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interview_type/interview_type.js b/hrms/hr/doctype/interview_type/interview_type.js new file mode 100644 index 0000000..af77b52 --- /dev/null +++ b/hrms/hr/doctype/interview_type/interview_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Interview Type', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/interview_type/interview_type.json b/hrms/hr/doctype/interview_type/interview_type.json new file mode 100644 index 0000000..14636a1 --- /dev/null +++ b/hrms/hr/doctype/interview_type/interview_type.json @@ -0,0 +1,73 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2021-04-12 14:44:40.664034", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "description" + ], + "fields": [ + { + "fieldname": "description", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Description" + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "link_doctype": "Interview Round", + "link_fieldname": "interview_type" + } + ], + "modified": "2021-09-30 13:00:16.471518", + "modified_by": "Administrator", + "module": "HR", + "name": "Interview Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interview_type/interview_type.py b/hrms/hr/doctype/interview_type/interview_type.py new file mode 100644 index 0000000..f5ebda4 --- /dev/null +++ b/hrms/hr/doctype/interview_type/interview_type.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class InterviewType(Document): + pass diff --git a/hrms/hr/doctype/interview_type/test_interview_type.py b/hrms/hr/doctype/interview_type/test_interview_type.py new file mode 100644 index 0000000..96fdfca --- /dev/null +++ b/hrms/hr/doctype/interview_type/test_interview_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestInterviewType(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/interviewer/__init__.py b/hrms/hr/doctype/interviewer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/interviewer/interviewer.json b/hrms/hr/doctype/interviewer/interviewer.json new file mode 100644 index 0000000..a37b8b0 --- /dev/null +++ b/hrms/hr/doctype/interviewer/interviewer.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-04-12 17:38:19.354734", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-13 13:41:35.817568", + "modified_by": "Administrator", + "module": "HR", + "name": "Interviewer", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/interviewer/interviewer.py b/hrms/hr/doctype/interviewer/interviewer.py new file mode 100644 index 0000000..2dc4a14 --- /dev/null +++ b/hrms/hr/doctype/interviewer/interviewer.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class Interviewer(Document): + pass diff --git a/hrms/hr/doctype/job_applicant/README.md b/hrms/hr/doctype/job_applicant/README.md new file mode 100644 index 0000000..8eb2edb --- /dev/null +++ b/hrms/hr/doctype/job_applicant/README.md @@ -0,0 +1 @@ +Applicant for Job. \ No newline at end of file diff --git a/hrms/hr/doctype/job_applicant/__init__.py b/hrms/hr/doctype/job_applicant/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/job_applicant/job_applicant.js b/hrms/hr/doctype/job_applicant/job_applicant.js new file mode 100644 index 0000000..d7f9c83 --- /dev/null +++ b/hrms/hr/doctype/job_applicant/job_applicant.js @@ -0,0 +1,105 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +// For license information, please see license.txt + +// for communication +cur_frm.email_field = "email_id"; + +frappe.ui.form.on("Job Applicant", { + refresh: function(frm) { + frm.set_query("job_title", function() { + return { + filters: { + 'status': 'Open' + } + }; + }); + frm.events.create_custom_buttons(frm); + frm.events.make_dashboard(frm); + }, + + create_custom_buttons: function(frm) { + if (!frm.doc.__islocal && frm.doc.status !== "Rejected" && frm.doc.status !== "Accepted") { + frm.add_custom_button(__("Interview"), function() { + frm.events.create_dialog(frm); + }, __("Create")); + } + + if (!frm.doc.__islocal) { + if (frm.doc.__onload && frm.doc.__onload.job_offer) { + $('[data-doctype="Employee Onboarding"]').find("button").show(); + $('[data-doctype="Job Offer"]').find("button").hide(); + frm.add_custom_button(__("Job Offer"), function() { + frappe.set_route("Form", "Job Offer", frm.doc.__onload.job_offer); + }, __("View")); + } else { + $('[data-doctype="Employee Onboarding"]').find("button").hide(); + $('[data-doctype="Job Offer"]').find("button").show(); + frm.add_custom_button(__("Job Offer"), function() { + frappe.route_options = { + "job_applicant": frm.doc.name, + "applicant_name": frm.doc.applicant_name, + "designation": frm.doc.job_opening || frm.doc.designation, + }; + frappe.new_doc("Job Offer"); + }, __("Create")); + } + } + }, + + make_dashboard: function(frm) { + frappe.call({ + method: "hrms.hr.doctype.job_applicant.job_applicant.get_interview_details", + args: { + job_applicant: frm.doc.name + }, + callback: function(r) { + if (r.message) { + $("div").remove(".form-dashboard-section.custom"); + frm.dashboard.add_section( + frappe.render_template("job_applicant_dashboard", { + data: r.message.interviews, + number_of_stars: r.message.stars + }), + __("Interview Summary") + ); + } + } + }); + }, + + create_dialog: function(frm) { + let d = new frappe.ui.Dialog({ + title: 'Enter Interview Round', + fields: [ + { + label: 'Interview Round', + fieldname: 'interview_round', + fieldtype: 'Link', + options: 'Interview Round' + }, + ], + primary_action_label: 'Create Interview', + primary_action(values) { + frm.events.create_interview(frm, values); + d.hide(); + } + }); + d.show(); + }, + + create_interview: function (frm, values) { + frappe.call({ + method: "hrms.hr.doctype.job_applicant.job_applicant.create_interview", + args: { + doc: frm.doc, + interview_round: values.interview_round + }, + callback: function (r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + } +}); diff --git a/hrms/hr/doctype/job_applicant/job_applicant.json b/hrms/hr/doctype/job_applicant/job_applicant.json new file mode 100644 index 0000000..66b609c --- /dev/null +++ b/hrms/hr/doctype/job_applicant/job_applicant.json @@ -0,0 +1,221 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "HR-APP-.YYYY.-.#####", + "creation": "2013-01-29 19:25:37", + "description": "Applicant for a Job", + "doctype": "DocType", + "document_type": "Document", + "email_append_to": 1, + "engine": "InnoDB", + "field_order": [ + "details_section", + "applicant_name", + "email_id", + "phone_number", + "country", + "column_break_3", + "job_title", + "designation", + "status", + "source_and_rating_section", + "source", + "source_name", + "employee_referral", + "column_break_13", + "applicant_rating", + "section_break_6", + "notes", + "cover_letter", + "resume_attachment", + "resume_link", + "section_break_16", + "currency", + "column_break_18", + "lower_range", + "upper_range" + ], + "fields": [ + { + "bold": 1, + "fieldname": "applicant_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Applicant Name", + "reqd": 1 + }, + { + "bold": 1, + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email Address", + "options": "Email", + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nReplied\nRejected\nHold\nAccepted", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "job_title", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Job Opening", + "options": "Job Opening" + }, + { + "fieldname": "source", + "fieldtype": "Link", + "label": "Source", + "options": "Job Applicant Source" + }, + { + "depends_on": "eval: doc.source==\"Employee Referral\" ", + "fieldname": "source_name", + "fieldtype": "Link", + "label": "Source Name", + "options": "Employee" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Resume" + }, + { + "fieldname": "cover_letter", + "fieldtype": "Text", + "label": "Cover Letter" + }, + { + "fieldname": "resume_attachment", + "fieldtype": "Attach", + "label": "Resume Attachment" + }, + { + "fieldname": "notes", + "fieldtype": "Data", + "label": "Notes", + "read_only": 1 + }, + { + "fieldname": "phone_number", + "fieldtype": "Data", + "label": "Phone Number", + "options": "Phone" + }, + { + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, + { + "fieldname": "resume_link", + "fieldtype": "Data", + "label": "Resume Link" + }, + { + "fieldname": "applicant_rating", + "fieldtype": "Rating", + "in_list_view": 1, + "label": "Applicant Rating" + }, + { + "fieldname": "section_break_16", + "fieldtype": "Section Break", + "label": "Salary Expectation" + }, + { + "fieldname": "lower_range", + "fieldtype": "Currency", + "label": "Lower Range", + "options": "currency", + "precision": "0" + }, + { + "fieldname": "upper_range", + "fieldtype": "Currency", + "label": "Upper Range", + "options": "currency", + "precision": "0" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + }, + { + "fieldname": "employee_referral", + "fieldtype": "Link", + "label": "Employee Referral", + "options": "Employee Referral", + "read_only": 1 + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "fieldname": "source_and_rating_section", + "fieldtype": "Section Break", + "label": "Source and Rating" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fetch_from": "job_opening.designation", + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation" + } + ], + "icon": "fa fa-user", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-01-12 16:28:53.196881", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Applicant", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "search_fields": "applicant_name, email_id, job_title, phone_number", + "sender_field": "email_id", + "sort_field": "modified", + "sort_order": "ASC", + "states": [], + "subject_field": "notes", + "title_field": "applicant_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/job_applicant/job_applicant.py b/hrms/hr/doctype/job_applicant/job_applicant.py new file mode 100644 index 0000000..a77f71c --- /dev/null +++ b/hrms/hr/doctype/job_applicant/job_applicant.py @@ -0,0 +1,109 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.model.naming import append_number_if_name_exists +from frappe.utils import flt, validate_email_address + +from hrms.hr.doctype.interview.interview import get_interviewers + + +class DuplicationError(frappe.ValidationError): + pass + + +class JobApplicant(Document): + def onload(self): + job_offer = frappe.get_all("Job Offer", filters={"job_applicant": self.name}) + if job_offer: + self.get("__onload").job_offer = job_offer[0].name + + def autoname(self): + self.name = self.email_id + + # applicant can apply more than once for a different job title or reapply + if frappe.db.exists("Job Applicant", self.name): + self.name = append_number_if_name_exists("Job Applicant", self.name) + + def validate(self): + if self.email_id: + validate_email_address(self.email_id, True) + + if self.employee_referral: + self.set_status_for_employee_referral() + + if not self.applicant_name and self.email_id: + guess = self.email_id.split("@")[0] + self.applicant_name = " ".join([p.capitalize() for p in guess.split(".")]) + + def set_status_for_employee_referral(self): + emp_ref = frappe.get_doc("Employee Referral", self.employee_referral) + if self.status in ["Open", "Replied", "Hold"]: + emp_ref.db_set("status", "In Process") + elif self.status in ["Accepted", "Rejected"]: + emp_ref.db_set("status", self.status) + + +@frappe.whitelist() +def create_interview(doc, interview_round): + import json + + if isinstance(doc, str): + doc = json.loads(doc) + doc = frappe.get_doc(doc) + + round_designation = frappe.db.get_value("Interview Round", interview_round, "designation") + + if round_designation and doc.designation and round_designation != doc.designation: + frappe.throw( + _("Interview Round {0} is only applicable for the Designation {1}").format( + interview_round, round_designation + ) + ) + + interview = frappe.new_doc("Interview") + interview.interview_round = interview_round + interview.job_applicant = doc.name + interview.designation = doc.designation + interview.resume_link = doc.resume_link + interview.job_opening = doc.job_title + interviewer_detail = get_interviewers(interview_round) + + for d in interviewer_detail: + interview.append("interview_details", {"interviewer": d.interviewer}) + return interview + + +@frappe.whitelist() +def get_interview_details(job_applicant): + interview_details = frappe.db.get_all( + "Interview", + filters={"job_applicant": job_applicant, "docstatus": ["!=", 2]}, + fields=["name", "interview_round", "scheduled_on", "average_rating", "status"], + ) + interview_detail_map = {} + meta = frappe.get_meta("Interview") + number_of_stars = meta.get_options("average_rating") or 5 + + for detail in interview_details: + detail.average_rating = detail.average_rating * number_of_stars if detail.average_rating else 0 + + interview_detail_map[detail.name] = detail + + return {"interviews": interview_detail_map, "stars": number_of_stars} + + +@frappe.whitelist() +def get_applicant_to_hire_percentage(): + total_applicants = frappe.db.count("Job Applicant") + total_hired = frappe.db.count("Job Applicant", filters={"status": "Accepted"}) + + return { + "value": flt(total_hired) / flt(total_applicants) * 100 if total_applicants else 0, + "fieldtype": "Percent", + } diff --git a/hrms/hr/doctype/job_applicant/job_applicant_dashboard.html b/hrms/hr/doctype/job_applicant/job_applicant_dashboard.html new file mode 100644 index 0000000..2616768 --- /dev/null +++ b/hrms/hr/doctype/job_applicant/job_applicant_dashboard.html @@ -0,0 +1,52 @@ + +{% if not jQuery.isEmptyObject(data) %} + + + + + + + + + + + + + {% for(const [key, value] of Object.entries(data)) { %} + + + + + + {% let right_class = ''; %} + {% let left_class = ''; %} + + + + {% } %} + +
{{ __("Interview") }}{{ __("Interview Round") }}{{ __("Date") }}{{ __("Status") }}{{ __("Rating") }}
{%= key %} {%= value["interview_round"] %} {%= frappe.datetime.str_to_user(value["scheduled_on"]) %} {%= value["status"] %} +
+ {% for (let i = 1; i <= number_of_stars; i++) { %} + {% if (i <= value["average_rating"]) { %} + {% right_class = 'star-click'; %} + {% } else { %} + {% right_class = ''; %} + {% } %} + + {% if ((i <= value["average_rating"]) || ((i - 0.5) == value["average_rating"])) { %} + {% left_class = 'star-click'; %} + {% } else { %} + {% left_class = ''; %} + {% } %} + + + + + + {% } %} +
+
+{% else %} +

No Interview has been scheduled.

+{% endif %} diff --git a/hrms/hr/doctype/job_applicant/job_applicant_dashboard.py b/hrms/hr/doctype/job_applicant/job_applicant_dashboard.py new file mode 100644 index 0000000..14b944a --- /dev/null +++ b/hrms/hr/doctype/job_applicant/job_applicant_dashboard.py @@ -0,0 +1,9 @@ +def get_data(): + return { + "fieldname": "job_applicant", + "transactions": [ + {"items": ["Employee", "Employee Onboarding"]}, + {"items": ["Job Offer", "Appointment Letter"]}, + {"items": ["Interview"]}, + ], + } diff --git a/hrms/hr/doctype/job_applicant/job_applicant_list.js b/hrms/hr/doctype/job_applicant/job_applicant_list.js new file mode 100644 index 0000000..2ad0d59 --- /dev/null +++ b/hrms/hr/doctype/job_applicant/job_applicant_list.js @@ -0,0 +1,15 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +frappe.listview_settings['Job Applicant'] = { + add_fields: ["status"], + get_indicator: function (doc) { + if (doc.status == "Accepted") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (["Open", "Replied"].includes(doc.status)) { + return [__(doc.status), "orange", "status,=," + doc.status]; + } else if (["Hold", "Rejected"].includes(doc.status)) { + return [__(doc.status), "red", "status,=," + doc.status]; + } + } +}; diff --git a/hrms/hr/doctype/job_applicant/test_job_applicant.py b/hrms/hr/doctype/job_applicant/test_job_applicant.py new file mode 100644 index 0000000..a22bd04 --- /dev/null +++ b/hrms/hr/doctype/job_applicant/test_job_applicant.py @@ -0,0 +1,59 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +import frappe + +from erpnext.setup.doctype.designation.test_designation import create_designation + + +class TestJobApplicant(unittest.TestCase): + def test_job_applicant_naming(self): + applicant = frappe.get_doc( + { + "doctype": "Job Applicant", + "status": "Open", + "applicant_name": "_Test Applicant", + "email_id": "job_applicant_naming@example.com", + } + ).insert() + self.assertEqual(applicant.name, "job_applicant_naming@example.com") + + applicant = frappe.get_doc( + { + "doctype": "Job Applicant", + "status": "Open", + "applicant_name": "_Test Applicant", + "email_id": "job_applicant_naming@example.com", + } + ).insert() + self.assertEqual(applicant.name, "job_applicant_naming@example.com-1") + + def tearDown(self): + frappe.db.rollback() + + +def create_job_applicant(**args): + args = frappe._dict(args) + + filters = { + "applicant_name": args.applicant_name or "_Test Applicant", + "email_id": args.email_id or "test_applicant@example.com", + } + + if frappe.db.exists("Job Applicant", filters): + return frappe.get_doc("Job Applicant", filters) + + job_applicant = frappe.get_doc( + { + "doctype": "Job Applicant", + "status": args.status or "Open", + "designation": create_designation().name, + } + ) + + job_applicant.update(filters) + job_applicant.save() + + return job_applicant diff --git a/hrms/hr/doctype/job_applicant_source/__init__.py b/hrms/hr/doctype/job_applicant_source/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/job_applicant_source/job_applicant_source.js b/hrms/hr/doctype/job_applicant_source/job_applicant_source.js new file mode 100644 index 0000000..8725f23 --- /dev/null +++ b/hrms/hr/doctype/job_applicant_source/job_applicant_source.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Job Applicant Source', { + refresh: function() { + + } +}); diff --git a/hrms/hr/doctype/job_applicant_source/job_applicant_source.json b/hrms/hr/doctype/job_applicant_source/job_applicant_source.json new file mode 100644 index 0000000..c3bb1eb --- /dev/null +++ b/hrms/hr/doctype/job_applicant_source/job_applicant_source.json @@ -0,0 +1,124 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:source_name", + "beta": 0, + "creation": "2018-06-16 12:28:26.432651", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "source_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Source Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "details", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-06-16 14:53:52.918474", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Applicant Source", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/job_applicant_source/job_applicant_source.py b/hrms/hr/doctype/job_applicant_source/job_applicant_source.py new file mode 100644 index 0000000..1f208c1 --- /dev/null +++ b/hrms/hr/doctype/job_applicant_source/job_applicant_source.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class JobApplicantSource(Document): + pass diff --git a/hrms/hr/doctype/job_applicant_source/test_job_applicant_source.py b/hrms/hr/doctype/job_applicant_source/test_job_applicant_source.py new file mode 100644 index 0000000..cee5daf --- /dev/null +++ b/hrms/hr/doctype/job_applicant_source/test_job_applicant_source.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestJobApplicantSource(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/job_offer/__init__.py b/hrms/hr/doctype/job_offer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/job_offer/job_offer.js b/hrms/hr/doctype/job_offer/job_offer.js new file mode 100644 index 0000000..b246ed5 --- /dev/null +++ b/hrms/hr/doctype/job_offer/job_offer.js @@ -0,0 +1,51 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.provide("erpnext.job_offer"); + +frappe.ui.form.on("Job Offer", { + onload: function (frm) { + frm.set_query("select_terms", function() { + return { filters: { hr: 1 } }; + }); + }, + + setup: function (frm) { + frm.email_field = "applicant_email"; + }, + + select_terms: function (frm) { + erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) { + if (!r.exc) { + frm.set_value("terms", r.message); + } + }); + }, + + refresh: function (frm) { + if ((!frm.doc.__islocal) && (frm.doc.status == 'Accepted') + && (frm.doc.docstatus === 1) && (!frm.doc.__onload || !frm.doc.__onload.employee)) { + frm.add_custom_button(__('Create Employee'), + function () { + erpnext.job_offer.make_employee(frm); + } + ); + } + + if(frm.doc.__onload && frm.doc.__onload.employee) { + frm.add_custom_button(__('Show Employee'), + function () { + frappe.set_route("Form", "Employee", frm.doc.__onload.employee); + } + ); + } + } + +}); + +erpnext.job_offer.make_employee = function (frm) { + frappe.model.open_mapped_doc({ + method: "hrms.hr.doctype.job_offer.job_offer.make_employee", + frm: frm + }); +}; diff --git a/hrms/hr/doctype/job_offer/job_offer.json b/hrms/hr/doctype/job_offer/job_offer.json new file mode 100644 index 0000000..c0b7f69 --- /dev/null +++ b/hrms/hr/doctype/job_offer/job_offer.json @@ -0,0 +1,190 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-OFF-.YYYY.-.#####", + "creation": "2015-03-04 14:20:17.662207", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "job_applicant", + "applicant_name", + "applicant_email", + "column_break_3", + "status", + "offer_date", + "designation", + "company", + "section_break_4", + "offer_terms", + "section_break_14", + "select_terms", + "terms", + "printing_details", + "letter_head", + "column_break_16", + "select_print_heading", + "amended_from" + ], + "fields": [ + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "label": "Job Applicant", + "options": "Job Applicant", + "print_hide": 1, + "reqd": 1 + }, + { + "fetch_from": "job_applicant.applicant_name", + "fieldname": "applicant_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Applicant Name", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "job_applicant.email_id", + "fieldname": "applicant_email", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Applicant Email Address", + "options": "Email", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Awaiting Response\nAccepted\nRejected", + "print_hide": 1 + }, + { + "fieldname": "offer_date", + "fieldtype": "Date", + "label": "Offer Date", + "reqd": 1 + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation", + "options": "Designation", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "offer_terms", + "fieldtype": "Table", + "label": "Job Offer Terms", + "options": "Job Offer Term" + }, + { + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, + { + "fieldname": "select_terms", + "fieldtype": "Link", + "label": "Select Terms and Conditions", + "options": "Terms and Conditions", + "print_hide": 1 + }, + { + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions" + }, + { + "collapsible": 1, + "fieldname": "printing_details", + "fieldtype": "Section Break", + "label": "Printing Details" + }, + { + "allow_on_submit": 1, + "fetch_from": "company.default_letter_head", + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "print_hide": 1, + "width": "50%" + }, + { + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Job Offer", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-06-25 00:56:24.756395", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offer", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "applicant_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/job_offer/job_offer.py b/hrms/hr/doctype/job_offer/job_offer.py new file mode 100644 index 0000000..d9c2951 --- /dev/null +++ b/hrms/hr/doctype/job_offer/job_offer.py @@ -0,0 +1,125 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +from frappe.utils import cint, flt, get_link_to_form + + +class JobOffer(Document): + def onload(self): + employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") or "" + self.set_onload("employee", employee) + + def validate(self): + self.validate_vacancies() + job_offer = frappe.db.exists( + "Job Offer", {"job_applicant": self.job_applicant, "docstatus": ["!=", 2]} + ) + if job_offer and job_offer != self.name: + frappe.throw( + _("Job Offer: {0} is already for Job Applicant: {1}").format( + frappe.bold(job_offer), frappe.bold(self.job_applicant) + ) + ) + + def validate_vacancies(self): + staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date) + check_vacancies = frappe.get_single("HR Settings").check_vacancies + if staffing_plan and check_vacancies: + job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date) + if not staffing_plan.get("vacancies") or cint(staffing_plan.vacancies) - len(job_offers) <= 0: + error_variable = "for " + frappe.bold(self.designation) + if staffing_plan.get("parent"): + error_variable = frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent)) + + frappe.throw(_("There are no vacancies under staffing plan {0}").format(error_variable)) + + def on_change(self): + update_job_applicant(self.status, self.job_applicant) + + def get_job_offer(self, from_date, to_date): + """Returns job offer created during a time period""" + return frappe.get_all( + "Job Offer", + filters={ + "offer_date": ["between", (from_date, to_date)], + "designation": self.designation, + "company": self.company, + "docstatus": 1, + }, + fields=["name"], + ) + + +def update_job_applicant(status, job_applicant): + if status in ("Accepted", "Rejected"): + frappe.set_value("Job Applicant", job_applicant, "status", status) + + +def get_staffing_plan_detail(designation, company, offer_date): + detail = frappe.db.sql( + """ + SELECT DISTINCT spd.parent, + sp.from_date as from_date, + sp.to_date as to_date, + sp.name, + sum(spd.vacancies) as vacancies, + spd.designation + FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp + WHERE + sp.docstatus=1 + AND spd.designation=%s + AND sp.company=%s + AND spd.parent = sp.name + AND %s between sp.from_date and sp.to_date + """, + (designation, company, offer_date), + as_dict=1, + ) + + return frappe._dict(detail[0]) if (detail and detail[0].parent) else None + + +@frappe.whitelist() +def make_employee(source_name, target_doc=None): + def set_missing_values(source, target): + target.personal_email, target.first_name = frappe.db.get_value( + "Job Applicant", source.job_applicant, ["email_id", "applicant_name"] + ) + + doc = get_mapped_doc( + "Job Offer", + source_name, + { + "Job Offer": { + "doctype": "Employee", + "field_map": {"applicant_name": "employee_name", "offer_date": "scheduled_confirmation_date"}, + } + }, + target_doc, + set_missing_values, + ) + return doc + + +@frappe.whitelist() +def get_offer_acceptance_rate(company=None, department=None): + filters = {"docstatus": 1} + if company: + filters["company"] = company + if department: + filters["department"] = department + + total_offers = frappe.db.count("Job Offer", filters=filters) + + filters["status"] = "Accepted" + total_accepted = frappe.db.count("Job Offer", filters=filters) + + return { + "value": flt(total_accepted) / flt(total_offers) * 100 if total_offers else 0, + "fieldtype": "Percent", + } diff --git a/hrms/hr/doctype/job_offer/job_offer_list.js b/hrms/hr/doctype/job_offer/job_offer_list.js new file mode 100644 index 0000000..4fa5be7 --- /dev/null +++ b/hrms/hr/doctype/job_offer/job_offer_list.js @@ -0,0 +1,15 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +frappe.listview_settings['Job Offer'] = { + add_fields: ["company", "designation", "job_applicant", "status"], + get_indicator: function (doc) { + if (doc.status == "Accepted") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (doc.status == "Awaiting Response") { + return [__(doc.status), "orange", "status,=," + doc.status]; + } else if (doc.status == "Rejected") { + return [__(doc.status), "red", "status,=," + doc.status]; + } + } +}; diff --git a/hrms/hr/doctype/job_offer/test_job_offer.py b/hrms/hr/doctype/job_offer/test_job_offer.py new file mode 100644 index 0000000..49d9f7b --- /dev/null +++ b/hrms/hr/doctype/job_offer/test_job_offer.py @@ -0,0 +1,113 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, nowdate + +from erpnext.setup.doctype.designation.test_designation import create_designation + +from hrms.hr.doctype.job_applicant.job_applicant import get_applicant_to_hire_percentage +from hrms.hr.doctype.job_applicant.test_job_applicant import create_job_applicant +from hrms.hr.doctype.job_offer.job_offer import get_offer_acceptance_rate +from hrms.hr.doctype.staffing_plan.test_staffing_plan import make_company + + +class TestJobOffer(FrappeTestCase): + def setUp(self): + frappe.db.delete("Job Applicant") + frappe.db.delete("Job Offer") + + create_designation(designation_name="Researcher") + + def test_job_offer_creation_against_vacancies(self): + frappe.db.set_value("HR Settings", None, "check_vacancies", 1) + job_applicant = create_job_applicant(email_id="test_job_offer@example.com") + job_offer = create_job_offer(job_applicant=job_applicant.name, designation="UX Designer") + + create_staffing_plan( + name="Test No Vacancies", + staffing_details=[ + {"designation": "UX Designer", "vacancies": 0, "estimated_cost_per_position": 5000} + ], + ) + self.assertRaises(frappe.ValidationError, job_offer.submit) + + # test creation of job offer when vacancies are not present + frappe.db.set_value("HR Settings", None, "check_vacancies", 0) + job_offer.submit() + self.assertTrue(frappe.db.exists("Job Offer", job_offer.name)) + + def test_job_applicant_update(self): + frappe.db.set_value("HR Settings", None, "check_vacancies", 0) + create_staffing_plan() + job_applicant = create_job_applicant(email_id="test_job_applicants@example.com") + job_offer = create_job_offer(job_applicant=job_applicant.name) + job_offer.submit() + job_applicant.reload() + self.assertEqual(job_applicant.status, "Accepted") + + # status update after rejection + job_offer.status = "Rejected" + job_offer.submit() + job_applicant.reload() + self.assertEquals(job_applicant.status, "Rejected") + frappe.db.set_value("HR Settings", None, "check_vacancies", 1) + + def test_recruitment_metrics(self): + job_applicant1 = create_job_applicant(email_id="test_job_applicant1@example.com") + job_applicant2 = create_job_applicant(email_id="test_job_applicant2@example.com") + job_offer = create_job_offer(job_applicant=job_applicant1.name) + job_offer.status = "Accepted" + job_offer.submit() + + self.assertEqual(get_applicant_to_hire_percentage().get("value"), 50) + + job_offer = create_job_offer(job_applicant=job_applicant2.name) + job_offer.status = "Rejected" + job_offer.submit() + + self.assertEqual(get_offer_acceptance_rate().get("value"), 50) + + +def create_job_offer(**args): + args = frappe._dict(args) + if not args.job_applicant: + job_applicant = create_job_applicant() + + if not frappe.db.exists("Designation", args.designation): + designation = create_designation(designation_name=args.designation) + + job_offer = frappe.get_doc( + { + "doctype": "Job Offer", + "job_applicant": args.job_applicant or job_applicant.name, + "offer_date": args.offer_date or nowdate(), + "designation": args.designation or "Researcher", + "status": args.status or "Accepted", + } + ) + return job_offer + + +def create_staffing_plan(**args): + args = frappe._dict(args) + make_company() + frappe.db.set_value("Company", "_Test Company", "is_group", 1) + if frappe.db.exists("Staffing Plan", args.name or "Test"): + return + staffing_plan = frappe.get_doc( + { + "doctype": "Staffing Plan", + "name": args.name or "Test", + "from_date": args.from_date or nowdate(), + "to_date": args.to_date or add_days(nowdate(), 10), + "staffing_details": args.staffing_details + or [{"designation": "Researcher", "vacancies": 1, "estimated_cost_per_position": 50000}], + } + ) + staffing_plan.insert() + staffing_plan.submit() + return staffing_plan diff --git a/hrms/hr/doctype/job_offer_term/__init__.py b/hrms/hr/doctype/job_offer_term/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/job_offer_term/job_offer_term.json b/hrms/hr/doctype/job_offer_term/job_offer_term.json new file mode 100644 index 0000000..dcd723e --- /dev/null +++ b/hrms/hr/doctype/job_offer_term/job_offer_term.json @@ -0,0 +1,130 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2015-03-05 12:53:45.342292", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "offer_term", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Offer Term", + "length": 0, + "no_copy": 0, + "options": "Offer Term", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "value", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Value / Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-02-15 03:30:56.020668", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offer Term", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/job_offer_term/job_offer_term.py b/hrms/hr/doctype/job_offer_term/job_offer_term.py new file mode 100644 index 0000000..d2eae46 --- /dev/null +++ b/hrms/hr/doctype/job_offer_term/job_offer_term.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class JobOfferTerm(Document): + pass diff --git a/hrms/hr/doctype/job_opening/README.md b/hrms/hr/doctype/job_opening/README.md new file mode 100644 index 0000000..5a19f33 --- /dev/null +++ b/hrms/hr/doctype/job_opening/README.md @@ -0,0 +1 @@ +Open position for Job. \ No newline at end of file diff --git a/hrms/hr/doctype/job_opening/__init__.py b/hrms/hr/doctype/job_opening/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/job_opening/job_opening.js b/hrms/hr/doctype/job_opening/job_opening.js new file mode 100644 index 0000000..b97e2fe --- /dev/null +++ b/hrms/hr/doctype/job_opening/job_opening.js @@ -0,0 +1,46 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Job Opening', { + onload: function(frm) { + frm.set_query("department", function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); + }, + designation: function(frm) { + if(frm.doc.designation && frm.doc.company){ + frappe.call({ + "method": "hrms.hr.doctype.staffing_plan.staffing_plan.get_active_staffing_plan_details", + args: { + company: frm.doc.company, + designation: frm.doc.designation, + date: frappe.datetime.now_date() // ToDo - Date in Job Opening? + }, + callback: function (data) { + if(data.message){ + frm.set_value('staffing_plan', data.message[0].name); + frm.set_value('planned_vacancies', data.message[0].vacancies); + } else { + frm.set_value('staffing_plan', ""); + frm.set_value('planned_vacancies', 0); + frappe.show_alert({ + indicator: 'orange', + message: __('No Staffing Plans found for this Designation') + }); + } + } + }); + } + else{ + frm.set_value('staffing_plan', ""); + frm.set_value('planned_vacancies', 0); + } + }, + company: function(frm) { + frm.set_value('designation', ""); + } +}); diff --git a/hrms/hr/doctype/job_opening/job_opening.json b/hrms/hr/doctype/job_opening/job_opening.json new file mode 100644 index 0000000..b8f6df6 --- /dev/null +++ b/hrms/hr/doctype/job_opening/job_opening.json @@ -0,0 +1,188 @@ +{ + "actions": [], + "autoname": "field:route", + "creation": "2013-01-15 16:13:36", + "description": "Description of a Job Opening", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "job_title", + "company", + "status", + "column_break_5", + "designation", + "department", + "staffing_plan", + "planned_vacancies", + "section_break_6", + "publish", + "route", + "column_break_12", + "job_application_route", + "section_break_14", + "description", + "section_break_16", + "currency", + "lower_range", + "upper_range", + "column_break_20", + "publish_salary_range" + ], + "fields": [ + { + "fieldname": "job_title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Job Title", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nClosed" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation", + "reqd": 1 + }, + { + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department" + }, + { + "fieldname": "staffing_plan", + "fieldtype": "Link", + "label": "Staffing Plan", + "options": "Staffing Plan", + "read_only": 1 + }, + { + "depends_on": "staffing_plan", + "fieldname": "planned_vacancies", + "fieldtype": "Int", + "label": "Planned number of Positions", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "publish", + "fieldtype": "Check", + "label": "Publish on website" + }, + { + "depends_on": "publish", + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "unique": 1 + }, + { + "description": "Job profile, qualifications required etc.", + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Description" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "section_break_16", + "fieldtype": "Section Break" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + }, + { + "fieldname": "lower_range", + "fieldtype": "Currency", + "label": "Lower Range", + "options": "currency", + "precision": "0" + }, + { + "fieldname": "upper_range", + "fieldtype": "Currency", + "label": "Upper Range", + "options": "currency", + "precision": "0" + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, + { + "depends_on": "publish", + "description": "Route to the custom Job Application Webform", + "fieldname": "job_application_route", + "fieldtype": "Data", + "label": "Job Application Route" + }, + { + "default": "0", + "fieldname": "publish_salary_range", + "fieldtype": "Check", + "label": "Publish Salary Range" + } + ], + "icon": "fa fa-bookmark", + "idx": 1, + "links": [], + "modified": "2020-09-18 11:23:29.488923", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Opening", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Guest" + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/job_opening/job_opening.py b/hrms/hr/doctype/job_opening/job_opening.py new file mode 100644 index 0000000..8b481b4 --- /dev/null +++ b/hrms/hr/doctype/job_opening/job_opening.py @@ -0,0 +1,100 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.utils import get_link_to_form +from frappe.website.website_generator import WebsiteGenerator + +from hrms.hr.doctype.staffing_plan.staffing_plan import ( + get_active_staffing_plan_details, + get_designation_counts, +) + + +class JobOpening(WebsiteGenerator): + website = frappe._dict( + template="templates/generators/job_opening.html", + condition_field="publish", + page_title_field="job_title", + ) + + def validate(self): + if not self.route: + self.route = frappe.scrub(self.job_title).replace("_", "-") + self.validate_current_vacancies() + + def validate_current_vacancies(self): + if not self.staffing_plan: + staffing_plan = get_active_staffing_plan_details(self.company, self.designation) + if staffing_plan: + self.staffing_plan = staffing_plan[0].name + self.planned_vacancies = staffing_plan[0].vacancies + elif not self.planned_vacancies: + self.planned_vacancies = frappe.db.get_value( + "Staffing Plan Detail", + {"parent": self.staffing_plan, "designation": self.designation}, + "vacancies", + ) + + if self.staffing_plan and self.planned_vacancies: + staffing_plan_company = frappe.db.get_value("Staffing Plan", self.staffing_plan, "company") + + designation_counts = get_designation_counts(self.designation, self.company, self.name) + current_count = designation_counts["employee_count"] + designation_counts["job_openings"] + + number_of_positions = frappe.db.get_value( + "Staffing Plan Detail", + {"parent": self.staffing_plan, "designation": self.designation}, + "number_of_positions", + ) + + if number_of_positions <= current_count: + frappe.throw( + _( + "Job Openings for the designation {0} are already open or the hiring is complete as per the Staffing Plan {1}" + ).format( + frappe.bold(self.designation), get_link_to_form("Staffing Plan", self.staffing_plan) + ), + title=_("Vacancies fulfilled"), + ) + + def get_context(self, context): + context.parents = [{"route": "jobs", "title": _("All Jobs")}] + + +def get_list_context(context): + context.title = _("Jobs") + context.introduction = _("Current Job Openings") + context.get_list = get_job_openings + + +def get_job_openings( + doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None +): + fields = [ + "name", + "status", + "job_title", + "description", + "publish_salary_range", + "lower_range", + "upper_range", + "currency", + "job_application_route", + ] + + filters = filters or {} + filters.update({"status": "Open"}) + + if txt: + filters.update( + {"job_title": ["like", "%{0}%".format(txt)], "description": ["like", "%{0}%".format(txt)]} + ) + + return frappe.get_all( + doctype, filters, fields, start=limit_start, page_length=limit_page_length, order_by=order_by + ) diff --git a/hrms/hr/doctype/job_opening/job_opening_dashboard.py b/hrms/hr/doctype/job_opening/job_opening_dashboard.py new file mode 100644 index 0000000..a309328 --- /dev/null +++ b/hrms/hr/doctype/job_opening/job_opening_dashboard.py @@ -0,0 +1,5 @@ +def get_data(): + return { + "fieldname": "job_title", + "transactions": [{"items": ["Job Applicant"]}], + } diff --git a/hrms/hr/doctype/job_opening/templates/job_opening_row.html b/hrms/hr/doctype/job_opening/templates/job_opening_row.html new file mode 100644 index 0000000..cc8523e --- /dev/null +++ b/hrms/hr/doctype/job_opening/templates/job_opening_row.html @@ -0,0 +1,18 @@ +
+

{{ doc.job_title }}

+

{{ doc.description }}

+ {%- if doc.publish_salary_range -%} +

{{_("Salary range per month")}}: {{ frappe.format_value(frappe.utils.flt(doc.lower_range), currency=doc.currency) }} - {{ frappe.format_value(frappe.utils.flt(doc.upper_range), currency=doc.currency) }}

+ {% endif %} +
+ {%- if doc.job_application_route -%} + + {{ _("Apply Now") }} + {% else %} + + {{ _("Apply Now") }} + {% endif %} +
+
diff --git a/hrms/hr/doctype/job_opening/test_job_opening.py b/hrms/hr/doctype/job_opening/test_job_opening.py new file mode 100644 index 0000000..2689754 --- /dev/null +++ b/hrms/hr/doctype/job_opening/test_job_opening.py @@ -0,0 +1,80 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.staffing_plan.test_staffing_plan import make_company + + +class TestJobOpening(FrappeTestCase): + def setUp(self): + frappe.db.delete("Staffing Plan") + frappe.db.delete("Staffing Plan Detail") + frappe.db.delete("Job Opening") + + make_company("_Test Opening Company", "_TOC") + frappe.db.delete("Employee", {"company": "_Test Opening Company"}) + + def test_vacancies_fulfilled(self): + make_employee( + "test_job_opening@example.com", company="_Test Opening Company", designation="Designer" + ) + + staffing_plan = frappe.get_doc( + { + "doctype": "Staffing Plan", + "company": "_Test Opening Company", + "name": "Test", + "from_date": getdate(), + "to_date": add_days(getdate(), 10), + } + ) + + staffing_plan.append( + "staffing_details", + {"designation": "Designer", "vacancies": 1, "estimated_cost_per_position": 50000}, + ) + staffing_plan.insert() + staffing_plan.submit() + + self.assertEqual(staffing_plan.staffing_details[0].number_of_positions, 2) + + # allows creating 1 job opening as per vacancy + opening_1 = get_job_opening() + opening_1.insert() + + # vacancies as per staffing plan already fulfilled via job opening and existing employee count + opening_2 = get_job_opening(job_title="Designer New") + self.assertRaises(frappe.ValidationError, opening_2.insert) + + # allows updating existing job opening + opening_1.status = "Closed" + opening_1.save() + + +def get_job_opening(**args): + args = frappe._dict(args) + + opening = frappe.db.exists("Job Opening", {"job_title": args.job_title or "Designer"}) + if opening: + return frappe.get_doc("Job Opening", opening) + + opening = frappe.get_doc( + { + "doctype": "Job Opening", + "job_title": "Designer", + "designation": "Designer", + "company": "_Test Opening Company", + "status": "Open", + } + ) + + opening.update(args) + + return opening diff --git a/hrms/hr/doctype/leave_allocation/README.md b/hrms/hr/doctype/leave_allocation/README.md new file mode 100644 index 0000000..870f9a5 --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/README.md @@ -0,0 +1 @@ +Leave Allocated to an Employee at the beginning of the period. \ No newline at end of file diff --git a/hrms/hr/doctype/leave_allocation/__init__.py b/hrms/hr/doctype/leave_allocation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.js b/hrms/hr/doctype/leave_allocation/leave_allocation.js new file mode 100644 index 0000000..f48a45c --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.js @@ -0,0 +1,131 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); + +frappe.ui.form.on("Leave Allocation", { + onload: function(frm) { + // Ignore cancellation of doctype on cancel all. + frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; + + if (!frm.doc.from_date) frm.set_value("from_date", frappe.datetime.get_today()); + + frm.set_query("employee", function() { + return { + query: "erpnext.controllers.queries.employee_query" + }; + }); + frm.set_query("leave_type", function() { + return { + filters: { + is_lwp: 0 + } + }; + }); + }, + + refresh: function(frm) { + if (frm.doc.docstatus === 1 && frm.doc.expired) { + var valid_expiry = moment(frappe.datetime.get_today()).isBetween(frm.doc.from_date, frm.doc.to_date); + if (valid_expiry) { + // expire current allocation + frm.add_custom_button(__('Expire Allocation'), function() { + frm.trigger("expire_allocation"); + }); + } + } + }, + + expire_allocation: function(frm) { + frappe.call({ + method: 'hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry.expire_allocation', + args: { + 'allocation': frm.doc, + 'expiry_date': frappe.datetime.get_today() + }, + freeze: true, + callback: function(r) { + if (!r.exc) { + frappe.msgprint(__("Allocation Expired!")); + } + frm.refresh(); + } + }); + }, + + employee: function(frm) { + frm.trigger("calculate_total_leaves_allocated"); + }, + + leave_type: function(frm) { + frm.trigger("leave_policy"); + frm.trigger("calculate_total_leaves_allocated"); + }, + + carry_forward: function(frm) { + frm.trigger("calculate_total_leaves_allocated"); + }, + + unused_leaves: function(frm) { + frm.set_value("total_leaves_allocated", + flt(frm.doc.unused_leaves) + flt(frm.doc.new_leaves_allocated)); + }, + + new_leaves_allocated: function(frm) { + frm.set_value("total_leaves_allocated", + flt(frm.doc.unused_leaves) + flt(frm.doc.new_leaves_allocated)); + }, + + leave_policy: function(frm) { + if (frm.doc.leave_policy && frm.doc.leave_type) { + frappe.db.get_value("Leave Policy Detail", { + 'parent': frm.doc.leave_policy, + 'leave_type': frm.doc.leave_type + }, 'annual_allocation', (r) => { + if (r && !r.exc) frm.set_value("new_leaves_allocated", flt(r.annual_allocation)); + }, "Leave Policy"); + } + }, + calculate_total_leaves_allocated: function(frm) { + if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) { + return frappe.call({ + method: "set_total_leaves_allocated", + doc: frm.doc, + callback: function() { + frm.refresh_fields(); + } + }); + } else if (cint(frm.doc.carry_forward) == 0) { + frm.set_value("unused_leaves", 0); + frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); + } + } +}); + +frappe.tour["Leave Allocation"] = [ + { + fieldname: "employee", + title: "Employee", + description: __("Select the Employee for which you want to allocate leaves.") + }, + { + fieldname: "leave_type", + title: "Leave Type", + description: __("Select the Leave Type like Sick leave, Privilege Leave, Casual Leave, etc.") + }, + { + fieldname: "from_date", + title: "From Date", + description: __("Select the date from which this Leave Allocation will be valid.") + }, + { + fieldname: "to_date", + title: "To Date", + description: __("Select the date after which this Leave Allocation will expire.") + }, + { + fieldname: "new_leaves_allocated", + title: "New Leaves Allocated", + description: __("Enter the number of leaves you want to allocate for the period.") + } +]; diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.json b/hrms/hr/doctype/leave_allocation/leave_allocation.json new file mode 100644 index 0000000..9d1db9b --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.json @@ -0,0 +1,286 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-02-20 19:10:38", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "department", + "company", + "column_break1", + "leave_type", + "from_date", + "to_date", + "section_break_6", + "new_leaves_allocated", + "carry_forward", + "unused_leaves", + "total_leaves_allocated", + "total_leaves_encashed", + "column_break_10", + "compensatory_request", + "leave_period", + "leave_policy", + "leave_policy_assignment", + "carry_forwarded_leaves_count", + "expired", + "amended_from", + "notes", + "description" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-LAL-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "oldfieldname": "leave_type", + "oldfieldtype": "Link", + "options": "Leave Type", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Allocation" + }, + { + "allow_on_submit": 1, + "bold": 1, + "fieldname": "new_leaves_allocated", + "fieldtype": "Float", + "label": "New Leaves Allocated" + }, + { + "default": "0", + "fieldname": "carry_forward", + "fieldtype": "Check", + "label": "Add unused leaves from previous allocations" + }, + { + "depends_on": "carry_forward", + "fieldname": "unused_leaves", + "fieldtype": "Float", + "label": "Unused leaves", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "total_leaves_allocated", + "fieldtype": "Float", + "label": "Total Leaves Allocated", + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "eval:doc.total_leaves_encashed>0", + "fieldname": "total_leaves_encashed", + "fieldtype": "Float", + "label": "Total Leaves Encashed", + "read_only": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "compensatory_request", + "fieldtype": "Link", + "label": "Compensatory Leave Request", + "options": "Compensatory Leave Request", + "read_only": 1 + }, + { + "fieldname": "leave_period", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Leave Period", + "options": "Leave Period", + "read_only": 1 + }, + { + "fetch_from": "leave_policy_assignment.leave_policy", + "fieldname": "leave_policy", + "fieldtype": "Link", + "hidden": 1, + "in_standard_filter": 1, + "label": "Leave Policy", + "options": "Leave Policy", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "expired", + "fieldtype": "Check", + "hidden": 1, + "in_standard_filter": 1, + "label": "Expired", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Leave Allocation", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "notes", + "fieldtype": "Section Break", + "label": "Notes" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "reason", + "oldfieldtype": "Small Text", + "width": "300px" + }, + { + "depends_on": "carry_forwarded_leaves_count", + "fieldname": "carry_forwarded_leaves_count", + "fieldtype": "Float", + "label": "Carry Forwarded Leaves", + "read_only": 1 + }, + { + "fieldname": "leave_policy_assignment", + "fieldtype": "Link", + "label": "Leave Policy Assignment", + "options": "Leave Policy Assignment", + "read_only": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + } + ], + "icon": "fa fa-ok", + "idx": 1, + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-04-07 09:50:33.145825", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Allocation", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "timeline_field": "employee", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.py b/hrms/hr/doctype/leave_allocation/leave_allocation.py new file mode 100644 index 0000000..e2de402 --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.py @@ -0,0 +1,370 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import add_days, date_diff, flt, formatdate, getdate + +from hrms.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period +from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import ( + create_leave_ledger_entry, + expire_allocation, +) +from hrms.hr.utils import get_leave_period, set_employee_name + + +class OverlapError(frappe.ValidationError): + pass + + +class BackDatedAllocationError(frappe.ValidationError): + pass + + +class OverAllocationError(frappe.ValidationError): + pass + + +class LessAllocationError(frappe.ValidationError): + pass + + +class ValueMultiplierError(frappe.ValidationError): + pass + + +class LeaveAllocation(Document): + def validate(self): + self.validate_period() + self.validate_allocation_overlap() + self.validate_lwp() + set_employee_name(self) + self.set_total_leaves_allocated() + self.validate_leave_days_and_dates() + + def validate_leave_days_and_dates(self): + # all validations that should run on save as well as on update after submit + self.validate_back_dated_allocation() + self.validate_total_leaves_allocated() + self.validate_leave_allocation_days() + + def validate_leave_allocation_days(self): + company = frappe.db.get_value("Employee", self.employee, "company") + leave_period = get_leave_period(self.from_date, self.to_date, company) + max_leaves_allowed = flt( + frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + ) + if max_leaves_allowed > 0: + leave_allocated = 0 + if leave_period: + leave_allocated = get_leave_allocation_for_period( + self.employee, + self.leave_type, + leave_period[0].from_date, + leave_period[0].to_date, + exclude_allocation=self.name, + ) + leave_allocated += flt(self.new_leaves_allocated) + if leave_allocated > max_leaves_allowed: + frappe.throw( + _( + "Total allocated leaves are more than maximum allocation allowed for {0} leave type for employee {1} in the period" + ).format(self.leave_type, self.employee), + OverAllocationError, + ) + + def on_submit(self): + self.create_leave_ledger_entry() + + # expire all unused leaves in the ledger on creation of carry forward allocation + allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee) + if self.carry_forward and allocation: + expire_allocation(allocation) + + def on_cancel(self): + self.create_leave_ledger_entry(submit=False) + if self.leave_policy_assignment: + self.update_leave_policy_assignments_when_no_allocations_left() + if self.carry_forward: + self.set_carry_forwarded_leaves_in_previous_allocation(on_cancel=True) + + def on_update_after_submit(self): + if self.has_value_changed("new_leaves_allocated"): + self.validate_against_leave_applications() + + # recalculate total leaves allocated + self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated) + # run required validations again since total leaves are being updated + self.validate_leave_days_and_dates() + + leaves_to_be_added = self.new_leaves_allocated - self.get_existing_leave_count() + args = { + "leaves": leaves_to_be_added, + "from_date": self.from_date, + "to_date": self.to_date, + "is_carry_forward": 0, + } + create_leave_ledger_entry(self, args, True) + self.db_update() + + def get_existing_leave_count(self): + ledger_entries = frappe.get_all( + "Leave Ledger Entry", + filters={ + "transaction_type": "Leave Allocation", + "transaction_name": self.name, + "employee": self.employee, + "company": self.company, + "leave_type": self.leave_type, + }, + pluck="leaves", + ) + total_existing_leaves = 0 + for entry in ledger_entries: + total_existing_leaves += entry + + return total_existing_leaves + + def validate_against_leave_applications(self): + leaves_taken = get_approved_leaves_for_period( + self.employee, self.leave_type, self.from_date, self.to_date + ) + if flt(leaves_taken) > flt(self.total_leaves_allocated): + if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): + frappe.msgprint( + _( + "Note: Total allocated leaves {0} shouldn't be less than already approved leaves {1} for the period" + ).format(self.total_leaves_allocated, leaves_taken) + ) + else: + frappe.throw( + _( + "Total allocated leaves {0} cannot be less than already approved leaves {1} for the period" + ).format(self.total_leaves_allocated, leaves_taken), + LessAllocationError, + ) + + def update_leave_policy_assignments_when_no_allocations_left(self): + allocations = frappe.db.get_list( + "Leave Allocation", + filters={"docstatus": 1, "leave_policy_assignment": self.leave_policy_assignment}, + ) + if len(allocations) == 0: + frappe.db.set_value( + "Leave Policy Assignment", self.leave_policy_assignment, "leaves_allocated", 0 + ) + + def validate_period(self): + if date_diff(self.to_date, self.from_date) <= 0: + frappe.throw(_("To date cannot be before from date")) + + def validate_lwp(self): + if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): + frappe.throw( + _("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type) + ) + + def validate_allocation_overlap(self): + leave_allocation = frappe.db.sql( + """ + SELECT + name + FROM `tabLeave Allocation` + WHERE + employee=%s AND leave_type=%s + AND name <> %s AND docstatus=1 + AND to_date >= %s AND from_date <= %s""", + (self.employee, self.leave_type, self.name, self.from_date, self.to_date), + ) + + if leave_allocation: + frappe.msgprint( + _("{0} already allocated for Employee {1} for period {2} to {3}").format( + self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date) + ) + ) + + frappe.throw( + _("Reference") + + ': {0}'.format(leave_allocation[0][0]), + OverlapError, + ) + + def validate_back_dated_allocation(self): + future_allocation = frappe.db.sql( + """select name, from_date from `tabLeave Allocation` + where employee=%s and leave_type=%s and docstatus=1 and from_date > %s + and carry_forward=1""", + (self.employee, self.leave_type, self.to_date), + as_dict=1, + ) + + if future_allocation: + frappe.throw( + _( + "Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}" + ).format(formatdate(future_allocation[0].from_date), future_allocation[0].name), + BackDatedAllocationError, + ) + + @frappe.whitelist() + def set_total_leaves_allocated(self): + self.unused_leaves = get_carry_forwarded_leaves( + self.employee, self.leave_type, self.from_date, self.carry_forward + ) + + self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated) + + self.limit_carry_forward_based_on_max_allowed_leaves() + + if self.carry_forward: + self.set_carry_forwarded_leaves_in_previous_allocation() + + if ( + not self.total_leaves_allocated + and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") + and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory") + ): + frappe.throw( + _("Total leaves allocated is mandatory for Leave Type {0}").format(self.leave_type) + ) + + def limit_carry_forward_based_on_max_allowed_leaves(self): + max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + if max_leaves_allowed and self.total_leaves_allocated > flt(max_leaves_allowed): + self.total_leaves_allocated = flt(max_leaves_allowed) + self.unused_leaves = max_leaves_allowed - flt(self.new_leaves_allocated) + + def set_carry_forwarded_leaves_in_previous_allocation(self, on_cancel=False): + """Set carry forwarded leaves in previous allocation""" + previous_allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee) + if on_cancel: + self.unused_leaves = 0.0 + if previous_allocation: + frappe.db.set_value( + "Leave Allocation", + previous_allocation.name, + "carry_forwarded_leaves_count", + self.unused_leaves, + ) + + def validate_total_leaves_allocated(self): + # Adding a day to include To Date in the difference + date_difference = date_diff(self.to_date, self.from_date) + 1 + if date_difference < self.total_leaves_allocated: + if frappe.db.get_value("Leave Type", self.leave_type, "allow_over_allocation"): + frappe.msgprint( + _("Total Leaves Allocated are more than the number of days in the allocation period"), + indicator="orange", + alert=True, + ) + else: + frappe.throw( + _("Total Leaves Allocated are more than the number of days in the allocation period"), + exc=OverAllocationError, + title=_("Over Allocation"), + ) + + def create_leave_ledger_entry(self, submit=True): + if self.unused_leaves: + expiry_days = frappe.db.get_value( + "Leave Type", self.leave_type, "expire_carry_forwarded_leaves_after_days" + ) + end_date = add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date + args = dict( + leaves=self.unused_leaves, + from_date=self.from_date, + to_date=min(getdate(end_date), getdate(self.to_date)), + is_carry_forward=1, + ) + create_leave_ledger_entry(self, args, submit) + + args = dict( + leaves=self.new_leaves_allocated, + from_date=self.from_date, + to_date=self.to_date, + is_carry_forward=0, + ) + create_leave_ledger_entry(self, args, submit) + + +def get_previous_allocation(from_date, leave_type, employee): + """Returns document properties of previous allocation""" + return frappe.db.get_value( + "Leave Allocation", + filters={ + "to_date": ("<", from_date), + "leave_type": leave_type, + "employee": employee, + "docstatus": 1, + }, + order_by="to_date DESC", + fieldname=["name", "from_date", "to_date", "employee", "leave_type"], + as_dict=1, + ) + + +def get_leave_allocation_for_period( + employee, leave_type, from_date, to_date, exclude_allocation=None +): + from frappe.query_builder.functions import Sum + + Allocation = frappe.qb.DocType("Leave Allocation") + return ( + frappe.qb.from_(Allocation) + .select(Sum(Allocation.total_leaves_allocated).as_("total_allocated_leaves")) + .where( + (Allocation.employee == employee) + & (Allocation.leave_type == leave_type) + & (Allocation.docstatus == 1) + & (Allocation.name != exclude_allocation) + & ( + (Allocation.from_date.between(from_date, to_date)) + | (Allocation.to_date.between(from_date, to_date)) + | ((Allocation.from_date < from_date) & (Allocation.to_date > to_date)) + ) + ) + ).run()[0][0] or 0.0 + + +@frappe.whitelist() +def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): + """Returns carry forwarded leaves for the given employee""" + unused_leaves = 0.0 + previous_allocation = get_previous_allocation(date, leave_type, employee) + if carry_forward and previous_allocation: + validate_carry_forward(leave_type) + unused_leaves = get_unused_leaves( + employee, leave_type, previous_allocation.from_date, previous_allocation.to_date + ) + if unused_leaves: + max_carry_forwarded_leaves = frappe.db.get_value( + "Leave Type", leave_type, "maximum_carry_forwarded_leaves" + ) + if max_carry_forwarded_leaves and unused_leaves > flt(max_carry_forwarded_leaves): + unused_leaves = flt(max_carry_forwarded_leaves) + + return unused_leaves + + +def get_unused_leaves(employee, leave_type, from_date, to_date): + """Returns unused leaves between the given period while skipping leave allocation expiry""" + leaves = frappe.get_all( + "Leave Ledger Entry", + filters={ + "employee": employee, + "leave_type": leave_type, + "from_date": (">=", from_date), + "to_date": ("<=", to_date), + }, + or_filters={"is_expired": 0, "is_carry_forward": 1}, + fields=["sum(leaves) as leaves"], + ) + return flt(leaves[0]["leaves"]) + + +def validate_carry_forward(leave_type): + if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): + frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation_dashboard.py b/hrms/hr/doctype/leave_allocation/leave_allocation_dashboard.py new file mode 100644 index 0000000..96e81db --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/leave_allocation_dashboard.py @@ -0,0 +1,6 @@ +def get_data(): + return { + "fieldname": "leave_allocation", + "transactions": [{"items": ["Compensatory Leave Request"]}, {"items": ["Leave Encashment"]}], + "reports": [{"items": ["Employee Leave Balance"]}], + } diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation_list.js b/hrms/hr/doctype/leave_allocation/leave_allocation_list.js new file mode 100644 index 0000000..3ab176f --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/leave_allocation_list.js @@ -0,0 +1,11 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +// render +frappe.listview_settings['Leave Allocation'] = { + get_indicator: function(doc) { + if(doc.status==="Expired") { + return [__("Expired"), "gray", "expired, =, 1"]; + } + }, +}; diff --git a/hrms/hr/doctype/leave_allocation/test_leave_allocation.py b/hrms/hr/doctype/leave_allocation/test_leave_allocation.py new file mode 100644 index 0000000..d1cadbb --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/test_leave_allocation.py @@ -0,0 +1,434 @@ +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, add_months, getdate, nowdate + +import erpnext +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.leave_allocation.leave_allocation import ( + BackDatedAllocationError, + OverAllocationError, +) +from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation +from hrms.hr.doctype.leave_type.test_leave_type import create_leave_type + + +class TestLeaveAllocation(FrappeTestCase): + def setUp(self): + frappe.db.delete("Leave Period") + frappe.db.delete("Leave Allocation") + frappe.db.delete("Leave Ledger Entry") + + emp_id = make_employee("test_emp_leave_allocation@salary.com", company="_Test Company") + self.employee = frappe.get_doc("Employee", emp_id) + + def test_overlapping_allocation(self): + leaves = [ + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, + "leave_type": "_Test Leave Type", + "from_date": getdate("2015-10-01"), + "to_date": getdate("2015-10-31"), + "new_leaves_allocated": 5, + "docstatus": 1, + }, + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, + "leave_type": "_Test Leave Type", + "from_date": getdate("2015-09-01"), + "to_date": getdate("2015-11-30"), + "new_leaves_allocated": 5, + }, + ] + + frappe.get_doc(leaves[0]).save() + self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save) + + def test_invalid_period(self): + doc = frappe.get_doc( + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, + "leave_type": "_Test Leave Type", + "from_date": getdate("2015-09-30"), + "to_date": getdate("2015-09-1"), + "new_leaves_allocated": 5, + } + ) + + # invalid period + self.assertRaises(frappe.ValidationError, doc.save) + + def test_validation_for_over_allocation(self): + leave_type = create_leave_type(leave_type_name="Test Over Allocation", is_carry_forward=1) + leave_type.save() + + doc = frappe.get_doc( + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, + "leave_type": leave_type.name, + "from_date": getdate("2015-09-1"), + "to_date": getdate("2015-09-30"), + "new_leaves_allocated": 35, + "carry_forward": 1, + } + ) + + # allocated leave more than period + self.assertRaises(OverAllocationError, doc.save) + + leave_type.allow_over_allocation = 1 + leave_type.save() + + # allows creating a leave allocation with more leave days than period days + doc = frappe.get_doc( + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, + "leave_type": leave_type.name, + "from_date": getdate("2015-09-1"), + "to_date": getdate("2015-09-30"), + "new_leaves_allocated": 35, + "carry_forward": 1, + } + ).insert() + + def test_validation_for_over_allocation_post_submission(self): + allocation = frappe.get_doc( + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, + "leave_type": "_Test Leave Type", + "from_date": getdate("2015-09-1"), + "to_date": getdate("2015-09-30"), + "new_leaves_allocated": 15, + } + ).submit() + allocation.reload() + # allocated leaves more than period after submission + allocation.new_leaves_allocated = 35 + self.assertRaises(OverAllocationError, allocation.save) + + def test_validation_for_over_allocation_based_on_leave_setup(self): + frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period") + leave_period = frappe.get_doc( + dict( + name="Test Allocation Period", + doctype="Leave Period", + from_date=add_months(nowdate(), -6), + to_date=add_months(nowdate(), 6), + company="_Test Company", + is_active=1, + ) + ).insert() + + leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1) + leave_type.max_leaves_allowed = 25 + leave_type.save() + + # 15 leaves allocated in this period + allocation = create_leave_allocation( + leave_type=leave_type.name, + employee=self.employee.name, + employee_name=self.employee.employee_name, + from_date=leave_period.from_date, + to_date=nowdate(), + ) + allocation.submit() + + # trying to allocate additional 15 leaves + allocation = create_leave_allocation( + leave_type=leave_type.name, + employee=self.employee.name, + employee_name=self.employee.employee_name, + from_date=add_days(nowdate(), 1), + to_date=leave_period.to_date, + ) + self.assertRaises(OverAllocationError, allocation.save) + + def test_validation_for_over_allocation_based_on_leave_setup_post_submission(self): + frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period") + leave_period = frappe.get_doc( + dict( + name="Test Allocation Period", + doctype="Leave Period", + from_date=add_months(nowdate(), -6), + to_date=add_months(nowdate(), 6), + company="_Test Company", + is_active=1, + ) + ).insert() + + leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1) + leave_type.max_leaves_allowed = 30 + leave_type.save() + + # 15 leaves allocated + allocation = create_leave_allocation( + leave_type=leave_type.name, + employee=self.employee.name, + employee_name=self.employee.employee_name, + from_date=leave_period.from_date, + to_date=nowdate(), + ) + allocation.submit() + allocation.reload() + + # allocate additional 15 leaves + allocation = create_leave_allocation( + leave_type=leave_type.name, + employee=self.employee.name, + employee_name=self.employee.employee_name, + from_date=add_days(nowdate(), 1), + to_date=leave_period.to_date, + ) + allocation.submit() + allocation.reload() + + # trying to allocate 25 leaves in 2nd alloc within leave period + # total leaves = 40 which is more than `max_leaves_allowed` setting i.e. 30 + allocation.new_leaves_allocated = 25 + self.assertRaises(OverAllocationError, allocation.save) + + def test_validate_back_dated_allocation_update(self): + leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1) + leave_type.save() + + # initial leave allocation = 15 + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave", + from_date=add_months(nowdate(), -12), + to_date=add_months(nowdate(), -1), + carry_forward=0, + ) + leave_allocation.submit() + + # new_leaves = 15, carry_forwarded = 10 + leave_allocation_1 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave", + carry_forward=1, + ) + leave_allocation_1.submit() + + # try updating initial leave allocation + leave_allocation.reload() + leave_allocation.new_leaves_allocated = 20 + self.assertRaises(BackDatedAllocationError, leave_allocation.save) + + def test_carry_forward_calculation(self): + leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1) + leave_type.maximum_carry_forwarded_leaves = 10 + leave_type.max_leaves_allowed = 30 + leave_type.save() + + # initial leave allocation = 15 + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave", + from_date=add_months(nowdate(), -12), + to_date=add_months(nowdate(), -1), + carry_forward=0, + ) + leave_allocation.submit() + + # carry forwarded leaves considering maximum_carry_forwarded_leaves + # new_leaves = 15, carry_forwarded = 10 + leave_allocation_1 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave", + carry_forward=1, + ) + leave_allocation_1.submit() + leave_allocation_1.reload() + + self.assertEqual(leave_allocation_1.unused_leaves, 10) + self.assertEqual(leave_allocation_1.total_leaves_allocated, 25) + + leave_allocation_1.cancel() + + # carry forwarded leaves considering max_leave_allowed + # max_leave_allowed = 30, new_leaves = 25, carry_forwarded = 5 + leave_allocation_2 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave", + carry_forward=1, + new_leaves_allocated=25, + ) + leave_allocation_2.submit() + + self.assertEqual(leave_allocation_2.unused_leaves, 5) + + def test_carry_forward_leaves_expiry(self): + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90, + ) + leave_type.save() + + # initial leave allocation + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave_expiry", + from_date=add_months(nowdate(), -24), + to_date=add_months(nowdate(), -12), + carry_forward=0, + ) + leave_allocation.submit() + + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave_expiry", + from_date=add_days(nowdate(), -90), + to_date=add_days(nowdate(), 100), + carry_forward=1, + ) + leave_allocation.submit() + + # expires all the carry forwarded leaves after 90 days + process_expired_allocation() + + # leave allocation with carry forward of only new leaves allocated + leave_allocation_1 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="_Test_CF_leave_expiry", + carry_forward=1, + from_date=add_months(nowdate(), 6), + to_date=add_months(nowdate(), 12), + ) + leave_allocation_1.submit() + + self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) + + def test_creation_of_leave_ledger_entry_on_submit(self): + leave_allocation = create_leave_allocation( + employee=self.employee.name, employee_name=self.employee.employee_name + ) + leave_allocation.submit() + + leave_ledger_entry = frappe.get_all( + "Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_allocation.name) + ) + + self.assertEqual(len(leave_ledger_entry), 1) + self.assertEqual(leave_ledger_entry[0].employee, leave_allocation.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_allocation.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated) + + # check if leave ledger entry is deleted on cancellation + leave_allocation.cancel() + self.assertFalse( + frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_allocation.name}) + ) + + def test_leave_addition_after_submit(self): + leave_allocation = create_leave_allocation( + employee=self.employee.name, employee_name=self.employee.employee_name + ) + leave_allocation.submit() + leave_allocation.reload() + self.assertTrue(leave_allocation.total_leaves_allocated, 15) + + leave_allocation.new_leaves_allocated = 40 + leave_allocation.submit() + leave_allocation.reload() + self.assertTrue(leave_allocation.total_leaves_allocated, 40) + + def test_leave_subtraction_after_submit(self): + leave_allocation = create_leave_allocation( + employee=self.employee.name, employee_name=self.employee.employee_name + ) + leave_allocation.submit() + leave_allocation.reload() + self.assertTrue(leave_allocation.total_leaves_allocated, 15) + + leave_allocation.new_leaves_allocated = 10 + leave_allocation.submit() + leave_allocation.reload() + self.assertTrue(leave_allocation.total_leaves_allocated, 10) + + def test_validation_against_leave_application_after_submit(self): + from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + make_holiday_list() + frappe.db.set_value( + "Company", self.employee.company, "default_holiday_list", "Salary Slip Test Holiday List" + ) + + leave_allocation = create_leave_allocation( + employee=self.employee.name, employee_name=self.employee.employee_name + ) + leave_allocation.submit() + self.assertTrue(leave_allocation.total_leaves_allocated, 15) + + leave_application = frappe.get_doc( + { + "doctype": "Leave Application", + "employee": self.employee.name, + "leave_type": "_Test Leave Type", + "from_date": add_months(nowdate(), 2), + "to_date": add_months(add_days(nowdate(), 10), 2), + "company": self.employee.company, + "docstatus": 1, + "status": "Approved", + "leave_approver": "test@example.com", + } + ) + leave_application.submit() + leave_application.reload() + + # allocate less leaves than the ones which are already approved + leave_allocation.new_leaves_allocated = leave_application.total_leave_days - 1 + leave_allocation.total_leaves_allocated = leave_application.total_leave_days - 1 + self.assertRaises(frappe.ValidationError, leave_allocation.submit) + + +def create_leave_allocation(**args): + args = frappe._dict(args) + + emp_id = make_employee("test_emp_leave_allocation@salary.com") + employee = frappe.get_doc("Employee", emp_id) + + return frappe.get_doc( + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": args.employee or employee.name, + "employee_name": args.employee_name or employee.employee_name, + "leave_type": args.leave_type or "_Test Leave Type", + "from_date": args.from_date or nowdate(), + "new_leaves_allocated": args.new_leaves_allocated or 15, + "carry_forward": args.carry_forward or 0, + "to_date": args.to_date or add_months(nowdate(), 12), + } + ) + + +test_dependencies = ["Employee", "Leave Type"] diff --git a/hrms/hr/doctype/leave_allocation/test_records.json b/hrms/hr/doctype/leave_allocation/test_records.json new file mode 100644 index 0000000..23acbb0 --- /dev/null +++ b/hrms/hr/doctype/leave_allocation/test_records.json @@ -0,0 +1,20 @@ +[ + { + "docstatus": 1, + "doctype": "Leave Allocation", + "employee": "_T-Employee-00001", + "from_date": "2013-01-01", + "to_date": "2013-12-31", + "leave_type": "_Test Leave Type", + "new_leaves_allocated": 15 + }, + { + "docstatus": 1, + "doctype": "Leave Allocation", + "employee": "_T-Employee-00002", + "from_date": "2013-01-01", + "to_date": "2013-12-31", + "leave_type": "_Test Leave Type", + "new_leaves_allocated": 15 + } +] \ No newline at end of file diff --git a/hrms/hr/doctype/leave_application/README.md b/hrms/hr/doctype/leave_application/README.md new file mode 100644 index 0000000..e78e37a --- /dev/null +++ b/hrms/hr/doctype/leave_application/README.md @@ -0,0 +1 @@ +Application for Leave by an Employee. \ No newline at end of file diff --git a/hrms/hr/doctype/leave_application/__init__.py b/hrms/hr/doctype/leave_application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_application/leave_application.js b/hrms/hr/doctype/leave_application/leave_application.js new file mode 100644 index 0000000..5f16ee7 --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application.js @@ -0,0 +1,270 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); +cur_frm.add_fetch('employee', 'company', 'company'); + +frappe.ui.form.on("Leave Application", { + setup: function(frm) { + frm.set_query("leave_approver", function() { + return { + query: "hrms.hr.doctype.department_approver.department_approver.get_approvers", + filters: { + employee: frm.doc.employee, + doctype: frm.doc.doctype + } + }; + }); + + frm.set_query("employee", erpnext.queries.employee); + }, + onload: function(frm) { + // Ignore cancellation of doctype on cancel all. + frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; + + if (!frm.doc.posting_date) { + frm.set_value("posting_date", frappe.datetime.get_today()); + } + if (frm.doc.docstatus == 0) { + return frappe.call({ + method: "hrms.hr.doctype.leave_application.leave_application.get_mandatory_approval", + args: { + doctype: frm.doc.doctype, + }, + callback: function(r) { + if (!r.exc && r.message) { + frm.toggle_reqd("leave_approver", true); + } + } + }); + } + }, + + validate: function(frm) { + if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1) { + frm.doc.half_day_date = frm.doc.from_date; + } else if (frm.doc.half_day == 0) { + frm.doc.half_day_date = ""; + } + frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); + }, + + make_dashboard: function(frm) { + var leave_details; + let lwps; + if (frm.doc.employee) { + frappe.call({ + method: "hrms.hr.doctype.leave_application.leave_application.get_leave_details", + async: false, + args: { + employee: frm.doc.employee, + date: frm.doc.from_date || frm.doc.posting_date + }, + callback: function(r) { + if (!r.exc && r.message['leave_allocation']) { + leave_details = r.message['leave_allocation']; + } + if (!r.exc && r.message['leave_approver']) { + frm.set_value('leave_approver', r.message['leave_approver']); + } + lwps = r.message["lwps"]; + } + }); + $("div").remove(".form-dashboard-section.custom"); + frm.dashboard.add_section( + frappe.render_template('leave_application_dashboard', { + data: leave_details + }), + __("Allocated Leaves") + ); + frm.dashboard.show(); + let allowed_leave_types = Object.keys(leave_details); + + // lwps should be allowed, lwps don't have any allocation + allowed_leave_types = allowed_leave_types.concat(lwps); + + frm.set_query('leave_type', function() { + return { + filters: [ + ['leave_type_name', 'in', allowed_leave_types] + ] + }; + }); + } + }, + + refresh: function(frm) { + if (frm.is_new()) { + frm.trigger("calculate_total_days"); + } + cur_frm.set_intro(""); + if (frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) { + frm.set_intro(__("Fill the form and save it")); + } + + if (!frm.doc.employee && frappe.defaults.get_user_permissions()) { + const perm = frappe.defaults.get_user_permissions(); + if (perm && perm['Employee']) { + frm.set_value('employee', perm['Employee'].map(perm_doc => perm_doc.doc)[0]); + } + } + }, + + employee: function(frm) { + frm.trigger("make_dashboard"); + frm.trigger("get_leave_balance"); + frm.trigger("set_leave_approver"); + }, + + leave_approver: function(frm) { + if (frm.doc.leave_approver) { + frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver)); + } + }, + + leave_type: function(frm) { + frm.trigger("get_leave_balance"); + }, + + half_day: function(frm) { + if (frm.doc.half_day) { + if (frm.doc.from_date == frm.doc.to_date) { + frm.set_value("half_day_date", frm.doc.from_date); + } else { + frm.trigger("half_day_datepicker"); + } + } else { + frm.set_value("half_day_date", ""); + } + frm.trigger("calculate_total_days"); + }, + + from_date: function(frm) { + frm.trigger("make_dashboard"); + frm.trigger("half_day_datepicker"); + frm.trigger("calculate_total_days"); + }, + + to_date: function(frm) { + frm.trigger("make_dashboard"); + frm.trigger("half_day_datepicker"); + frm.trigger("calculate_total_days"); + }, + + half_day_date(frm) { + frm.trigger("calculate_total_days"); + }, + + half_day_datepicker: function(frm) { + frm.set_value('half_day_date', ''); + var half_day_datepicker = frm.fields_dict.half_day_date.datepicker; + half_day_datepicker.update({ + minDate: frappe.datetime.str_to_obj(frm.doc.from_date), + maxDate: frappe.datetime.str_to_obj(frm.doc.to_date) + }); + }, + + get_leave_balance: function(frm) { + if (frm.doc.docstatus === 0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) { + return frappe.call({ + method: "hrms.hr.doctype.leave_application.leave_application.get_leave_balance_on", + args: { + employee: frm.doc.employee, + date: frm.doc.from_date, + to_date: frm.doc.to_date, + leave_type: frm.doc.leave_type, + consider_all_leaves_in_the_allocation_period: 1 + }, + callback: function (r) { + if (!r.exc && r.message) { + frm.set_value('leave_balance', r.message); + } else { + frm.set_value('leave_balance', "0"); + } + } + }); + } + }, + + calculate_total_days: function(frm) { + if (frm.doc.from_date && frm.doc.to_date && frm.doc.employee && frm.doc.leave_type) { + + var from_date = Date.parse(frm.doc.from_date); + var to_date = Date.parse(frm.doc.to_date); + + if (to_date < from_date) { + frappe.msgprint(__("To Date cannot be less than From Date")); + frm.set_value('to_date', ''); + return; + } + // server call is done to include holidays in leave days calculations + return frappe.call({ + method: 'hrms.hr.doctype.leave_application.leave_application.get_number_of_leave_days', + args: { + "employee": frm.doc.employee, + "leave_type": frm.doc.leave_type, + "from_date": frm.doc.from_date, + "to_date": frm.doc.to_date, + "half_day": frm.doc.half_day, + "half_day_date": frm.doc.half_day_date, + }, + callback: function(r) { + if (r && r.message) { + frm.set_value('total_leave_days', r.message); + frm.trigger("get_leave_balance"); + } + } + }); + } + }, + + set_leave_approver: function(frm) { + if (frm.doc.employee) { + // server call is done to include holidays in leave days calculations + return frappe.call({ + method: 'hrms.hr.doctype.leave_application.leave_application.get_leave_approver', + args: { + "employee": frm.doc.employee, + }, + callback: function(r) { + if (r && r.message) { + frm.set_value('leave_approver', r.message); + } + } + }); + } + } +}); + +frappe.tour["Leave Application"] = [ + { + fieldname: "employee", + title: "Employee", + description: __("Select the Employee.") + }, + { + fieldname: "leave_type", + title: "Leave Type", + description: __("Select type of leave the employee wants to apply for, like Sick Leave, Privilege Leave, Casual Leave, etc.") + }, + { + fieldname: "from_date", + title: "From Date", + description: __("Select the start date for your Leave Application.") + }, + { + fieldname: "to_date", + title: "To Date", + description: __("Select the end date for your Leave Application.") + }, + { + fieldname: "half_day", + title: "Half Day", + description: __("To apply for a Half Day check 'Half Day' and select the Half Day Date") + }, + { + fieldname: "leave_approver", + title: "Leave Approver", + description: __("Select your Leave Approver i.e. the person who approves or rejects your leaves.") + } +]; diff --git a/hrms/hr/doctype/leave_application/leave_application.json b/hrms/hr/doctype/leave_application/leave_application.json new file mode 100644 index 0000000..7f50ace --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application.json @@ -0,0 +1,338 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-02-20 11:18:11", + "description": "Apply / Approve Leaves", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "column_break_4", + "leave_type", + "department", + "leave_balance", + "section_break_5", + "from_date", + "to_date", + "half_day", + "half_day_date", + "total_leave_days", + "column_break1", + "description", + "section_break_7", + "leave_approver", + "leave_approver_name", + "column_break_18", + "status", + "salary_slip", + "sb10", + "posting_date", + "follow_via_email", + "color", + "column_break_17", + "company", + "letter_head", + "amended_from" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-LAP-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "options": "Leave Type", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "leave_balance", + "fieldtype": "Float", + "label": "Leave Balance Before Application", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "half_day", + "fieldtype": "Check", + "label": "Half Day" + }, + { + "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", + "fieldname": "half_day_date", + "fieldtype": "Date", + "label": "Half Day Date" + }, + { + "fieldname": "total_leave_days", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Total Leave Days", + "no_copy": 1, + "precision": "1", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Reason" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User" + }, + { + "fieldname": "leave_approver_name", + "fieldtype": "Data", + "label": "Leave Approver Name", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Open\nApproved\nRejected\nCancelled", + "permlevel": 1, + "reqd": 1 + }, + { + "fieldname": "sb10", + "fieldtype": "Section Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "no_copy": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "1", + "fieldname": "follow_via_email", + "fieldtype": "Check", + "label": "Follow via Email", + "print_hide": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "color", + "fieldtype": "Color", + "label": "Color", + "print_hide": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "options": "Leave Application", + "print_hide": 1, + "read_only": 1 + } + ], + "icon": "fa fa-calendar", + "idx": 1, + "is_submittable": 1, + "links": [], + "max_attachments": 3, + "modified": "2020-05-18 13:00:41.577327", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Application", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "All" + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "HR User", + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "write": 1 + } + ], + "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_application/leave_application.py b/hrms/hr/doctype/leave_application/leave_application.py new file mode 100644 index 0000000..e9dfa96 --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application.py @@ -0,0 +1,1247 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from typing import Dict, Optional, Tuple + +import frappe +from frappe import _ +from frappe.query_builder.functions import Max, Min, Sum +from frappe.utils import ( + add_days, + cint, + cstr, + date_diff, + flt, + formatdate, + get_fullname, + get_link_to_form, + getdate, + nowdate, +) + +from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange +from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee + +from hrms.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates +from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry +from hrms.hr.utils import ( + get_holiday_dates_for_employee, + get_leave_period, + set_employee_name, + share_doc_with_approver, + validate_active_employee, +) + + +class LeaveDayBlockedError(frappe.ValidationError): + pass + + +class OverlapError(frappe.ValidationError): + pass + + +class AttendanceAlreadyMarkedError(frappe.ValidationError): + pass + + +class NotAnOptionalHoliday(frappe.ValidationError): + pass + + +class InsufficientLeaveBalanceError(frappe.ValidationError): + pass + + +class LeaveAcrossAllocationsError(frappe.ValidationError): + pass + + +from frappe.model.document import Document + + +class LeaveApplication(Document): + def get_feed(self): + return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) + + def validate(self): + validate_active_employee(self.employee) + set_employee_name(self) + self.validate_dates() + self.validate_balance_leaves() + self.validate_leave_overlap() + self.validate_max_days() + self.show_block_day_warning() + self.validate_block_days() + self.validate_salary_processed_days() + self.validate_attendance() + self.set_half_day_date() + if frappe.db.get_value("Leave Type", self.leave_type, "is_optional_leave"): + self.validate_optional_leave() + self.validate_applicable_after() + + def on_update(self): + if self.status == "Open" and self.docstatus < 1: + # notify leave approver about creation + if frappe.db.get_single_value("HR Settings", "send_leave_notification"): + self.notify_leave_approver() + + share_doc_with_approver(self, self.leave_approver) + + def on_submit(self): + if self.status in ["Open", "Cancelled"]: + frappe.throw( + _("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted") + ) + + self.validate_back_dated_application() + self.update_attendance() + + # notify leave applier about approval + if frappe.db.get_single_value("HR Settings", "send_leave_notification"): + self.notify_employee() + + self.create_leave_ledger_entry() + self.reload() + + def before_cancel(self): + self.status = "Cancelled" + + def on_cancel(self): + self.create_leave_ledger_entry(submit=False) + # notify leave applier about cancellation + if frappe.db.get_single_value("HR Settings", "send_leave_notification"): + self.notify_employee() + self.cancel_attendance() + + def validate_applicable_after(self): + if self.leave_type: + leave_type = frappe.get_doc("Leave Type", self.leave_type) + if leave_type.applicable_after > 0: + date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") + leave_days = get_approved_leaves_for_period( + self.employee, False, date_of_joining, self.from_date + ) + number_of_days = date_diff(getdate(self.from_date), date_of_joining) + if number_of_days >= 0: + holidays = 0 + if not frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): + holidays = get_holidays(self.employee, date_of_joining, self.from_date) + number_of_days = number_of_days - leave_days - holidays + if number_of_days < leave_type.applicable_after: + frappe.throw( + _("{0} applicable after {1} working days").format( + self.leave_type, leave_type.applicable_after + ) + ) + + def validate_dates(self): + if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"): + if self.from_date and getdate(self.from_date) < getdate(): + allowed_role = frappe.db.get_single_value( + "HR Settings", "role_allowed_to_create_backdated_leave_application" + ) + user = frappe.get_doc("User", frappe.session.user) + user_roles = [d.role for d in user.roles] + if not allowed_role: + frappe.throw( + _("Backdated Leave Application is restricted. Please set the {} in {}").format( + frappe.bold("Role Allowed to Create Backdated Leave Application"), + get_link_to_form("HR Settings", "HR Settings"), + ) + ) + + if allowed_role and allowed_role not in user_roles: + frappe.throw( + _("Only users with the {0} role can create backdated leave applications").format( + allowed_role + ) + ) + + if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): + frappe.throw(_("To date cannot be before from date")) + + if ( + self.half_day + and self.half_day_date + and ( + getdate(self.half_day_date) < getdate(self.from_date) + or getdate(self.half_day_date) > getdate(self.to_date) + ) + ): + + frappe.throw(_("Half Day Date should be between From Date and To Date")) + + if not is_lwp(self.leave_type): + self.validate_dates_across_allocation() + self.validate_back_dated_application() + + def validate_dates_across_allocation(self): + if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): + return + + alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates() + + if not (alloc_on_from_date or alloc_on_to_date): + frappe.throw(_("Application period cannot be outside leave allocation period")) + elif self.is_separate_ledger_entry_required(alloc_on_from_date, alloc_on_to_date): + frappe.throw( + _("Application period cannot be across two allocation records"), + exc=LeaveAcrossAllocationsError, + ) + + def get_allocation_based_on_application_dates(self) -> Tuple[Dict, Dict]: + """Returns allocation name, from and to dates for application dates""" + + def _get_leave_allocation_record(date): + LeaveAllocation = frappe.qb.DocType("Leave Allocation") + allocation = ( + frappe.qb.from_(LeaveAllocation) + .select(LeaveAllocation.name, LeaveAllocation.from_date, LeaveAllocation.to_date) + .where( + (LeaveAllocation.employee == self.employee) + & (LeaveAllocation.leave_type == self.leave_type) + & (LeaveAllocation.docstatus == 1) + & ((date >= LeaveAllocation.from_date) & (date <= LeaveAllocation.to_date)) + ) + ).run(as_dict=True) + + return allocation and allocation[0] + + allocation_based_on_from_date = _get_leave_allocation_record(self.from_date) + allocation_based_on_to_date = _get_leave_allocation_record(self.to_date) + + return allocation_based_on_from_date, allocation_based_on_to_date + + def validate_back_dated_application(self): + future_allocation = frappe.db.sql( + """select name, from_date from `tabLeave Allocation` + where employee=%s and leave_type=%s and docstatus=1 and from_date > %s + and carry_forward=1""", + (self.employee, self.leave_type, self.to_date), + as_dict=1, + ) + + if future_allocation: + frappe.throw( + _( + "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}" + ).format(formatdate(future_allocation[0].from_date), future_allocation[0].name) + ) + + def update_attendance(self): + if self.status != "Approved": + return + + holiday_dates = [] + if not frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): + holiday_dates = get_holiday_dates_for_employee(self.employee, self.from_date, self.to_date) + + for dt in daterange(getdate(self.from_date), getdate(self.to_date)): + date = dt.strftime("%Y-%m-%d") + attendance_name = frappe.db.exists( + "Attendance", dict(employee=self.employee, attendance_date=date, docstatus=("!=", 2)) + ) + + # don't mark attendance for holidays + # if leave type does not include holidays within leaves as leaves + if date in holiday_dates: + if attendance_name: + # cancel and delete existing attendance for holidays + attendance = frappe.get_doc("Attendance", attendance_name) + attendance.flags.ignore_permissions = True + if attendance.docstatus == 1: + attendance.cancel() + frappe.delete_doc("Attendance", attendance_name, force=1) + continue + + self.create_or_update_attendance(attendance_name, date) + + def create_or_update_attendance(self, attendance_name, date): + status = ( + "Half Day" + if self.half_day_date and getdate(date) == getdate(self.half_day_date) + else "On Leave" + ) + + if attendance_name: + # update existing attendance, change absent to on leave + doc = frappe.get_doc("Attendance", attendance_name) + if doc.status != status: + doc.db_set({"status": status, "leave_type": self.leave_type, "leave_application": self.name}) + else: + # make new attendance and submit it + doc = frappe.new_doc("Attendance") + doc.employee = self.employee + doc.employee_name = self.employee_name + doc.attendance_date = date + doc.company = self.company + doc.leave_type = self.leave_type + doc.leave_application = self.name + doc.status = status + doc.flags.ignore_validate = True + doc.insert(ignore_permissions=True) + doc.submit() + + def cancel_attendance(self): + if self.docstatus == 2: + attendance = frappe.db.sql( + """select name from `tabAttendance` where employee = %s\ + and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""", + (self.employee, self.from_date, self.to_date), + as_dict=1, + ) + for name in attendance: + frappe.db.set_value("Attendance", name, "docstatus", 2) + + def validate_salary_processed_days(self): + if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): + return + + last_processed_pay_slip = frappe.db.sql( + """ + select start_date, end_date from `tabSalary Slip` + where docstatus = 1 and employee = %s + and ((%s between start_date and end_date) or (%s between start_date and end_date)) + order by modified desc limit 1 + """, + (self.employee, self.to_date, self.from_date), + ) + + if last_processed_pay_slip: + frappe.throw( + _( + "Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range." + ).format( + formatdate(last_processed_pay_slip[0][0]), formatdate(last_processed_pay_slip[0][1]) + ) + ) + + def show_block_day_warning(self): + block_dates = get_applicable_block_dates( + self.from_date, self.to_date, self.employee, self.company, all_lists=True + ) + + if block_dates: + frappe.msgprint(_("Warning: Leave application contains following block dates") + ":") + for d in block_dates: + frappe.msgprint(formatdate(d.block_date) + ": " + d.reason) + + def validate_block_days(self): + block_dates = get_applicable_block_dates( + self.from_date, self.to_date, self.employee, self.company + ) + + if block_dates and self.status == "Approved": + frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError) + + def validate_balance_leaves(self): + if self.from_date and self.to_date: + self.total_leave_days = get_number_of_leave_days( + self.employee, self.leave_type, self.from_date, self.to_date, self.half_day, self.half_day_date + ) + + if self.total_leave_days <= 0: + frappe.throw( + _( + "The day(s) on which you are applying for leave are holidays. You need not apply for leave." + ) + ) + + if not is_lwp(self.leave_type): + leave_balance = get_leave_balance_on( + self.employee, + self.leave_type, + self.from_date, + self.to_date, + consider_all_leaves_in_the_allocation_period=True, + for_consumption=True, + ) + self.leave_balance = leave_balance.get("leave_balance") + leave_balance_for_consumption = leave_balance.get("leave_balance_for_consumption") + + if self.status != "Rejected" and ( + leave_balance_for_consumption < self.total_leave_days or not leave_balance_for_consumption + ): + self.show_insufficient_balance_message(leave_balance_for_consumption) + + def show_insufficient_balance_message(self, leave_balance_for_consumption: float) -> None: + alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates() + + if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): + if leave_balance_for_consumption != self.leave_balance: + msg = _("Warning: Insufficient leave balance for Leave Type {0} in this allocation.").format( + frappe.bold(self.leave_type) + ) + msg += "

" + msg += _( + "Actual balances aren't available because the leave application spans over different leave allocations. You can still apply for leaves which would be compensated during the next allocation." + ) + else: + msg = _("Warning: Insufficient leave balance for Leave Type {0}.").format( + frappe.bold(self.leave_type) + ) + + frappe.msgprint(msg, title=_("Warning"), indicator="orange") + else: + frappe.throw( + _("Insufficient leave balance for Leave Type {0}").format(frappe.bold(self.leave_type)), + exc=InsufficientLeaveBalanceError, + title=_("Insufficient Balance"), + ) + + def validate_leave_overlap(self): + if not self.name: + # hack! if name is null, it could cause problems with != + self.name = "New Leave Application" + + for d in frappe.db.sql( + """ + select + name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day_date + from `tabLeave Application` + where employee = %(employee)s and docstatus < 2 and status in ('Open', 'Approved') + and to_date >= %(from_date)s and from_date <= %(to_date)s + and name != %(name)s""", + { + "employee": self.employee, + "from_date": self.from_date, + "to_date": self.to_date, + "name": self.name, + }, + as_dict=1, + ): + + if ( + cint(self.half_day) == 1 + and getdate(self.half_day_date) == getdate(d.half_day_date) + and ( + flt(self.total_leave_days) == 0.5 + or getdate(self.from_date) == getdate(d.to_date) + or getdate(self.to_date) == getdate(d.from_date) + ) + ): + + total_leaves_on_half_day = self.get_total_leaves_on_half_day() + if total_leaves_on_half_day >= 1: + self.throw_overlap_error(d) + else: + self.throw_overlap_error(d) + + def throw_overlap_error(self, d): + form_link = get_link_to_form("Leave Application", d.name) + msg = _("Employee {0} has already applied for {1} between {2} and {3} : {4}").format( + self.employee, d["leave_type"], formatdate(d["from_date"]), formatdate(d["to_date"]), form_link + ) + frappe.throw(msg, OverlapError) + + def get_total_leaves_on_half_day(self): + leave_count_on_half_day_date = frappe.db.sql( + """select count(name) from `tabLeave Application` + where employee = %(employee)s + and docstatus < 2 + and status in ('Open', 'Approved') + and half_day = 1 + and half_day_date = %(half_day_date)s + and name != %(name)s""", + {"employee": self.employee, "half_day_date": self.half_day_date, "name": self.name}, + )[0][0] + + return leave_count_on_half_day_date * 0.5 + + def validate_max_days(self): + max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_continuous_days_allowed") + if max_days and self.total_leave_days > cint(max_days): + frappe.throw(_("Leave of type {0} cannot be longer than {1}").format(self.leave_type, max_days)) + + def validate_attendance(self): + attendance = frappe.db.sql( + """select name from `tabAttendance` where employee = %s and (attendance_date between %s and %s) + and status = 'Present' and docstatus = 1""", + (self.employee, self.from_date, self.to_date), + ) + if attendance: + frappe.throw( + _("Attendance for employee {0} is already marked for this day").format(self.employee), + AttendanceAlreadyMarkedError, + ) + + def validate_optional_leave(self): + leave_period = get_leave_period(self.from_date, self.to_date, self.company) + if not leave_period: + frappe.throw(_("Cannot find active Leave Period")) + optional_holiday_list = frappe.db.get_value( + "Leave Period", leave_period[0]["name"], "optional_holiday_list" + ) + if not optional_holiday_list: + frappe.throw( + _("Optional Holiday List not set for leave period {0}").format(leave_period[0]["name"]) + ) + day = getdate(self.from_date) + while day <= getdate(self.to_date): + if not frappe.db.exists( + {"doctype": "Holiday", "parent": optional_holiday_list, "holiday_date": day} + ): + frappe.throw( + _("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday + ) + day = add_days(day, 1) + + def set_half_day_date(self): + if self.from_date == self.to_date and self.half_day == 1: + self.half_day_date = self.from_date + + if self.half_day == 0: + self.half_day_date = None + + def notify_employee(self): + employee = frappe.get_doc("Employee", self.employee) + if not employee.user_id: + return + + parent_doc = frappe.get_doc("Leave Application", self.name) + args = parent_doc.as_dict() + + template = frappe.db.get_single_value("HR Settings", "leave_status_notification_template") + if not template: + frappe.msgprint(_("Please set default template for Leave Status Notification in HR Settings.")) + return + email_template = frappe.get_doc("Email Template", template) + message = frappe.render_template(email_template.response, args) + + self.notify( + { + # for post in messages + "message": message, + "message_to": employee.user_id, + # for email + "subject": email_template.subject, + "notify": "employee", + } + ) + + def notify_leave_approver(self): + if self.leave_approver: + parent_doc = frappe.get_doc("Leave Application", self.name) + args = parent_doc.as_dict() + + template = frappe.db.get_single_value("HR Settings", "leave_approval_notification_template") + if not template: + frappe.msgprint( + _("Please set default template for Leave Approval Notification in HR Settings.") + ) + return + email_template = frappe.get_doc("Email Template", template) + message = frappe.render_template(email_template.response, args) + + self.notify( + { + # for post in messages + "message": message, + "message_to": self.leave_approver, + # for email + "subject": email_template.subject, + } + ) + + def notify(self, args): + args = frappe._dict(args) + # args -> message, message_to, subject + if cint(self.follow_via_email): + contact = args.message_to + if not isinstance(contact, list): + if not args.notify == "employee": + contact = frappe.get_doc("User", contact).email or contact + + sender = dict() + sender["email"] = frappe.get_doc("User", frappe.session.user).email + sender["full_name"] = get_fullname(sender["email"]) + + try: + frappe.sendmail( + recipients=contact, + sender=sender["email"], + subject=args.subject, + message=args.message, + ) + frappe.msgprint(_("Email sent to {0}").format(contact)) + except frappe.OutgoingEmailError: + pass + + def create_leave_ledger_entry(self, submit=True): + if self.status != "Approved" and submit: + return + + expiry_date = get_allocation_expiry_for_cf_leaves( + self.employee, self.leave_type, self.to_date, self.from_date + ) + lwp = frappe.db.get_value("Leave Type", self.leave_type, "is_lwp") + + if expiry_date: + self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp) + else: + alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates() + if self.is_separate_ledger_entry_required(alloc_on_from_date, alloc_on_to_date): + # required only if negative balance is allowed for leave type + # else will be stopped in validation itself + self.create_separate_ledger_entries(alloc_on_from_date, alloc_on_to_date, submit, lwp) + else: + raise_exception = False if frappe.flags.in_patch else True + args = dict( + leaves=self.total_leave_days * -1, + from_date=self.from_date, + to_date=self.to_date, + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) + or "", + ) + create_leave_ledger_entry(self, args, submit) + + def is_separate_ledger_entry_required( + self, alloc_on_from_date: Optional[Dict] = None, alloc_on_to_date: Optional[Dict] = None + ) -> bool: + """Checks if application dates fall in separate allocations""" + if ( + (alloc_on_from_date and not alloc_on_to_date) + or (not alloc_on_from_date and alloc_on_to_date) + or ( + alloc_on_from_date and alloc_on_to_date and alloc_on_from_date.name != alloc_on_to_date.name + ) + ): + return True + return False + + def create_separate_ledger_entries(self, alloc_on_from_date, alloc_on_to_date, submit, lwp): + """Creates separate ledger entries for application period falling into separate allocations""" + # for creating separate ledger entries existing allocation periods should be consecutive + if ( + submit + and alloc_on_from_date + and alloc_on_to_date + and add_days(alloc_on_from_date.to_date, 1) != alloc_on_to_date.from_date + ): + frappe.throw( + _( + "Leave Application period cannot be across two non-consecutive leave allocations {0} and {1}." + ).format( + get_link_to_form("Leave Allocation", alloc_on_from_date.name), + get_link_to_form("Leave Allocation", alloc_on_to_date), + ) + ) + + raise_exception = False if frappe.flags.in_patch else True + + if alloc_on_from_date: + first_alloc_end = alloc_on_from_date.to_date + second_alloc_start = add_days(alloc_on_from_date.to_date, 1) + else: + first_alloc_end = add_days(alloc_on_to_date.from_date, -1) + second_alloc_start = alloc_on_to_date.from_date + + leaves_in_first_alloc = get_number_of_leave_days( + self.employee, + self.leave_type, + self.from_date, + first_alloc_end, + self.half_day, + self.half_day_date, + ) + leaves_in_second_alloc = get_number_of_leave_days( + self.employee, + self.leave_type, + second_alloc_start, + self.to_date, + self.half_day, + self.half_day_date, + ) + + args = dict( + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) + or "", + ) + + if leaves_in_first_alloc: + args.update( + dict(from_date=self.from_date, to_date=first_alloc_end, leaves=leaves_in_first_alloc * -1) + ) + create_leave_ledger_entry(self, args, submit) + + if leaves_in_second_alloc: + args.update( + dict(from_date=second_alloc_start, to_date=self.to_date, leaves=leaves_in_second_alloc * -1) + ) + create_leave_ledger_entry(self, args, submit) + + def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp): + """Splits leave application into two ledger entries to consider expiry of allocation""" + raise_exception = False if frappe.flags.in_patch else True + + leaves = get_number_of_leave_days( + self.employee, self.leave_type, self.from_date, expiry_date, self.half_day, self.half_day_date + ) + + if leaves: + args = dict( + from_date=self.from_date, + to_date=expiry_date, + leaves=leaves * -1, + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) + or "", + ) + create_leave_ledger_entry(self, args, submit) + + if getdate(expiry_date) != getdate(self.to_date): + start_date = add_days(expiry_date, 1) + leaves = get_number_of_leave_days( + self.employee, self.leave_type, start_date, self.to_date, self.half_day, self.half_day_date + ) + + if leaves: + args.update(dict(from_date=start_date, to_date=self.to_date, leaves=leaves * -1)) + create_leave_ledger_entry(self, args, submit) + + +def get_allocation_expiry_for_cf_leaves( + employee: str, leave_type: str, to_date: str, from_date: str +) -> str: + """Returns expiry of carry forward allocation in leave ledger entry""" + expiry = frappe.get_all( + "Leave Ledger Entry", + filters={ + "employee": employee, + "leave_type": leave_type, + "is_carry_forward": 1, + "transaction_type": "Leave Allocation", + "to_date": ["between", (from_date, to_date)], + "docstatus": 1, + }, + fields=["to_date"], + ) + return expiry[0]["to_date"] if expiry else "" + + +@frappe.whitelist() +def get_number_of_leave_days( + employee: str, + leave_type: str, + from_date: str, + to_date: str, + half_day: Optional[int] = None, + half_day_date: Optional[str] = None, + holiday_list: Optional[str] = None, +) -> float: + """Returns number of leave days between 2 dates after considering half day and holidays + (Based on the include_holiday setting in Leave Type)""" + number_of_days = 0 + if cint(half_day) == 1: + if getdate(from_date) == getdate(to_date): + number_of_days = 0.5 + elif half_day_date and getdate(from_date) <= getdate(half_day_date) <= getdate(to_date): + number_of_days = date_diff(to_date, from_date) + 0.5 + else: + number_of_days = date_diff(to_date, from_date) + 1 + else: + number_of_days = date_diff(to_date, from_date) + 1 + + if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"): + number_of_days = flt(number_of_days) - flt( + get_holidays(employee, from_date, to_date, holiday_list=holiday_list) + ) + return number_of_days + + +@frappe.whitelist() +def get_leave_details(employee, date): + allocation_records = get_leave_allocation_records(employee, date) + leave_allocation = {} + for d in allocation_records: + allocation = allocation_records.get(d, frappe._dict()) + remaining_leaves = get_leave_balance_on( + employee, d, date, to_date=allocation.to_date, consider_all_leaves_in_the_allocation_period=True + ) + + end_date = allocation.to_date + leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 + leaves_pending = get_leaves_pending_approval_for_period( + employee, d, allocation.from_date, end_date + ) + expired_leaves = allocation.total_leaves_allocated - (remaining_leaves + leaves_taken) + + leave_allocation[d] = { + "total_leaves": allocation.total_leaves_allocated, + "expired_leaves": expired_leaves if expired_leaves > 0 else 0, + "leaves_taken": leaves_taken, + "leaves_pending_approval": leaves_pending, + "remaining_leaves": remaining_leaves, + } + + # is used in set query + lwp = frappe.get_list("Leave Type", filters={"is_lwp": 1}, pluck="name") + + return { + "leave_allocation": leave_allocation, + "leave_approver": get_leave_approver(employee), + "lwps": lwp, + } + + +@frappe.whitelist() +def get_leave_balance_on( + employee: str, + leave_type: str, + date: str, + to_date: str = None, + consider_all_leaves_in_the_allocation_period: bool = False, + for_consumption: bool = False, +): + """ + Returns leave balance till date + :param employee: employee name + :param leave_type: leave type + :param date: date to check balance on + :param to_date: future date to check for allocation expiry + :param consider_all_leaves_in_the_allocation_period: consider all leaves taken till the allocation end date + :param for_consumption: flag to check if leave balance is required for consumption or display + eg: employee has leave balance = 10 but allocation is expiring in 1 day so employee can only consume 1 leave + in this case leave_balance = 10 but leave_balance_for_consumption = 1 + if True, returns a dict eg: {'leave_balance': 10, 'leave_balance_for_consumption': 1} + else, returns leave_balance (in this case 10) + """ + + if not to_date: + to_date = nowdate() + + allocation_records = get_leave_allocation_records(employee, date, leave_type) + allocation = allocation_records.get(leave_type, frappe._dict()) + + end_date = allocation.to_date if cint(consider_all_leaves_in_the_allocation_period) else date + cf_expiry = get_allocation_expiry_for_cf_leaves(employee, leave_type, to_date, date) + + leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, end_date) + + remaining_leaves = get_remaining_leaves(allocation, leaves_taken, date, cf_expiry) + + if for_consumption: + return remaining_leaves + else: + return remaining_leaves.get("leave_balance") + + +def get_leave_allocation_records(employee, date, leave_type=None): + """Returns the total allocated leaves and carry forwarded leaves based on ledger entries""" + Ledger = frappe.qb.DocType("Leave Ledger Entry") + + cf_leave_case = ( + frappe.qb.terms.Case().when(Ledger.is_carry_forward == "1", Ledger.leaves).else_(0) + ) + sum_cf_leaves = Sum(cf_leave_case).as_("cf_leaves") + + new_leaves_case = ( + frappe.qb.terms.Case().when(Ledger.is_carry_forward == "0", Ledger.leaves).else_(0) + ) + sum_new_leaves = Sum(new_leaves_case).as_("new_leaves") + + query = ( + frappe.qb.from_(Ledger) + .select( + sum_cf_leaves, + sum_new_leaves, + Min(Ledger.from_date).as_("from_date"), + Max(Ledger.to_date).as_("to_date"), + Ledger.leave_type, + ) + .where( + (Ledger.from_date <= date) + & (Ledger.to_date >= date) + & (Ledger.docstatus == 1) + & (Ledger.transaction_type == "Leave Allocation") + & (Ledger.employee == employee) + & (Ledger.is_expired == 0) + & (Ledger.is_lwp == 0) + ) + ) + + if leave_type: + query = query.where((Ledger.leave_type == leave_type)) + query = query.groupby(Ledger.employee, Ledger.leave_type) + + allocation_details = query.run(as_dict=True) + + allocated_leaves = frappe._dict() + for d in allocation_details: + allocated_leaves.setdefault( + d.leave_type, + frappe._dict( + { + "from_date": d.from_date, + "to_date": d.to_date, + "total_leaves_allocated": flt(d.cf_leaves) + flt(d.new_leaves), + "unused_leaves": d.cf_leaves, + "new_leaves_allocated": d.new_leaves, + "leave_type": d.leave_type, + } + ), + ) + return allocated_leaves + + +def get_leaves_pending_approval_for_period( + employee: str, leave_type: str, from_date: str, to_date: str +) -> float: + """Returns leaves that are pending for approval""" + leaves = frappe.get_all( + "Leave Application", + filters={"employee": employee, "leave_type": leave_type, "status": "Open"}, + or_filters={ + "from_date": ["between", (from_date, to_date)], + "to_date": ["between", (from_date, to_date)], + }, + fields=["SUM(total_leave_days) as leaves"], + )[0] + return leaves["leaves"] if leaves["leaves"] else 0.0 + + +def get_remaining_leaves( + allocation: Dict, leaves_taken: float, date: str, cf_expiry: str +) -> Dict[str, float]: + """Returns a dict of leave_balance and leave_balance_for_consumption + leave_balance returns the available leave balance + leave_balance_for_consumption returns the minimum leaves remaining after comparing with remaining days for allocation expiry + """ + + def _get_remaining_leaves(remaining_leaves, end_date): + """Returns minimum leaves remaining after comparing with remaining days for allocation expiry""" + if remaining_leaves > 0: + remaining_days = date_diff(end_date, date) + 1 + remaining_leaves = min(remaining_days, remaining_leaves) + + return remaining_leaves + + leave_balance = leave_balance_for_consumption = flt(allocation.total_leaves_allocated) + flt( + leaves_taken + ) + + # balance for carry forwarded leaves + if cf_expiry and allocation.unused_leaves: + cf_leaves = flt(allocation.unused_leaves) + flt(leaves_taken) + remaining_cf_leaves = _get_remaining_leaves(cf_leaves, cf_expiry) + + leave_balance = flt(allocation.new_leaves_allocated) + flt(cf_leaves) + leave_balance_for_consumption = flt(allocation.new_leaves_allocated) + flt(remaining_cf_leaves) + + remaining_leaves = _get_remaining_leaves(leave_balance_for_consumption, allocation.to_date) + return frappe._dict(leave_balance=leave_balance, leave_balance_for_consumption=remaining_leaves) + + +def get_leaves_for_period( + employee: str, leave_type: str, from_date: str, to_date: str, skip_expired_leaves: bool = True +) -> float: + leave_entries = get_leave_entries(employee, leave_type, from_date, to_date) + leave_days = 0 + + for leave_entry in leave_entries: + inclusive_period = leave_entry.from_date >= getdate( + from_date + ) and leave_entry.to_date <= getdate(to_date) + + if inclusive_period and leave_entry.transaction_type == "Leave Encashment": + leave_days += leave_entry.leaves + + elif ( + inclusive_period + and leave_entry.transaction_type == "Leave Allocation" + and leave_entry.is_expired + and not skip_expired_leaves + ): + leave_days += leave_entry.leaves + + elif leave_entry.transaction_type == "Leave Application": + if leave_entry.from_date < getdate(from_date): + leave_entry.from_date = from_date + if leave_entry.to_date > getdate(to_date): + leave_entry.to_date = to_date + + half_day = 0 + half_day_date = None + # fetch half day date for leaves with half days + if leave_entry.leaves % 1: + half_day = 1 + half_day_date = frappe.db.get_value( + "Leave Application", {"name": leave_entry.transaction_name}, ["half_day_date"] + ) + + leave_days += ( + get_number_of_leave_days( + employee, + leave_type, + leave_entry.from_date, + leave_entry.to_date, + half_day, + half_day_date, + holiday_list=leave_entry.holiday_list, + ) + * -1 + ) + + return leave_days + + +def get_leave_entries(employee, leave_type, from_date, to_date): + """Returns leave entries between from_date and to_date.""" + return frappe.db.sql( + """ + SELECT + employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list, + is_carry_forward, is_expired + FROM `tabLeave Ledger Entry` + WHERE employee=%(employee)s AND leave_type=%(leave_type)s + AND docstatus=1 + AND (leaves<0 + OR is_expired=1) + AND (from_date between %(from_date)s AND %(to_date)s + OR to_date between %(from_date)s AND %(to_date)s + OR (from_date < %(from_date)s AND to_date > %(to_date)s)) + """, + {"from_date": from_date, "to_date": to_date, "employee": employee, "leave_type": leave_type}, + as_dict=1, + ) + + +@frappe.whitelist() +def get_holidays(employee, from_date, to_date, holiday_list=None): + """get holidays between two dates for the given employee""" + if not holiday_list: + holiday_list = get_holiday_list_for_employee(employee) + + holidays = frappe.db.sql( + """select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2 + where h1.parent = h2.name and h1.holiday_date between %s and %s + and h2.name = %s""", + (from_date, to_date, holiday_list), + )[0][0] + + return holidays + + +def is_lwp(leave_type): + lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type) + return lwp and cint(lwp[0][0]) or 0 + + +@frappe.whitelist() +def get_events(start, end, filters=None): + from frappe.desk.reportview import get_filters_cond + + events = [] + + employee = frappe.db.get_value( + "Employee", filters={"user_id": frappe.session.user}, fieldname=["name", "company"], as_dict=True + ) + + if employee: + employee, company = employee.name, employee.company + else: + employee = "" + company = frappe.db.get_value("Global Defaults", None, "default_company") + + conditions = get_filters_cond("Leave Application", filters, []) + # show department leaves for employee + if "Employee" in frappe.get_roles(): + add_department_leaves(events, start, end, employee, company) + + add_leaves(events, start, end, conditions) + add_block_dates(events, start, end, employee, company) + add_holidays(events, start, end, employee, company) + + return events + + +def add_department_leaves(events, start, end, employee, company): + department = frappe.db.get_value("Employee", employee, "department") + + if not department: + return + + # department leaves + department_employees = frappe.db.sql_list( + """select name from tabEmployee where department=%s + and company=%s""", + (department, company), + ) + + filter_conditions = ' and employee in ("%s")' % '", "'.join(department_employees) + add_leaves(events, start, end, filter_conditions=filter_conditions) + + +def add_leaves(events, start, end, filter_conditions=None): + from frappe.desk.reportview import build_match_conditions + + conditions = [] + + if not cint( + frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar") + ): + match_conditions = build_match_conditions("Leave Application") + + if match_conditions: + conditions.append(match_conditions) + + query = """SELECT + docstatus, + name, + employee, + employee_name, + leave_type, + from_date, + to_date, + half_day, + status, + color + FROM `tabLeave Application` + WHERE + from_date <= %(end)s AND to_date >= %(start)s <= to_date + AND docstatus < 2 + AND status in ('Approved', 'Open') + """ + + if conditions: + query += " AND " + " AND ".join(conditions) + + if filter_conditions: + query += filter_conditions + + for d in frappe.db.sql(query, {"start": start, "end": end}, as_dict=True): + e = { + "name": d.name, + "doctype": "Leave Application", + "from_date": d.from_date, + "to_date": d.to_date, + "docstatus": d.docstatus, + "color": d.color, + "all_day": int(not d.half_day), + "title": cstr(d.employee_name) + + f" ({cstr(d.leave_type)})" + + (" " + _("(Half Day)") if d.half_day else ""), + } + if e not in events: + events.append(e) + + +def add_block_dates(events, start, end, employee, company): + # block days + from hrms.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates + + cnt = 0 + block_dates = get_applicable_block_dates(start, end, employee, company, all_lists=True) + + for block_date in block_dates: + events.append( + { + "doctype": "Leave Block List Date", + "from_date": block_date.block_date, + "to_date": block_date.block_date, + "title": _("Leave Blocked") + ": " + block_date.reason, + "name": "_" + str(cnt), + } + ) + cnt += 1 + + +def add_holidays(events, start, end, employee, company): + applicable_holiday_list = get_holiday_list_for_employee(employee, company) + if not applicable_holiday_list: + return + + for holiday in frappe.db.sql( + """select name, holiday_date, description + from `tabHoliday` where parent=%s and holiday_date between %s and %s""", + (applicable_holiday_list, start, end), + as_dict=True, + ): + events.append( + { + "doctype": "Holiday", + "from_date": holiday.holiday_date, + "to_date": holiday.holiday_date, + "title": _("Holiday") + ": " + cstr(holiday.description), + "name": holiday.name, + } + ) + + +@frappe.whitelist() +def get_mandatory_approval(doctype): + mandatory = "" + if doctype == "Leave Application": + mandatory = frappe.db.get_single_value( + "HR Settings", "leave_approver_mandatory_in_leave_application" + ) + else: + mandatory = frappe.db.get_single_value( + "HR Settings", "expense_approver_mandatory_in_expense_claim" + ) + + return mandatory + + +def get_approved_leaves_for_period(employee, leave_type, from_date, to_date): + LeaveApplication = frappe.qb.DocType("Leave Application") + query = ( + frappe.qb.from_(LeaveApplication) + .select( + LeaveApplication.employee, + LeaveApplication.leave_type, + LeaveApplication.from_date, + LeaveApplication.to_date, + LeaveApplication.total_leave_days, + ) + .where( + (LeaveApplication.employee == employee) + & (LeaveApplication.docstatus == 1) + & (LeaveApplication.status == "Approved") + & ( + (LeaveApplication.from_date.between(from_date, to_date)) + | (LeaveApplication.to_date.between(from_date, to_date)) + | ((LeaveApplication.from_date < from_date) & (LeaveApplication.to_date > to_date)) + ) + ) + ) + + if leave_type: + query = query.where(LeaveApplication.leave_type == leave_type) + + leave_applications = query.run(as_dict=True) + + leave_days = 0 + for leave_app in leave_applications: + if leave_app.from_date >= getdate(from_date) and leave_app.to_date <= getdate(to_date): + leave_days += leave_app.total_leave_days + else: + if leave_app.from_date < getdate(from_date): + leave_app.from_date = from_date + if leave_app.to_date > getdate(to_date): + leave_app.to_date = to_date + + leave_days += get_number_of_leave_days( + employee, leave_type, leave_app.from_date, leave_app.to_date + ) + + return leave_days + + +@frappe.whitelist() +def get_leave_approver(employee): + leave_approver, department = frappe.db.get_value( + "Employee", employee, ["leave_approver", "department"] + ) + + if not leave_approver and department: + leave_approver = frappe.db.get_value( + "Department Approver", + {"parent": department, "parentfield": "leave_approvers", "idx": 1}, + "approver", + ) + + return leave_approver diff --git a/hrms/hr/doctype/leave_application/leave_application_calendar.js b/hrms/hr/doctype/leave_application/leave_application_calendar.js new file mode 100644 index 0000000..3eca7d2 --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application_calendar.js @@ -0,0 +1,22 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.views.calendar["Leave Application"] = { + field_map: { + "start": "from_date", + "end": "to_date", + "id": "name", + "title": "title", + "docstatus": 1, + "color": "color", + "allDay": "all_day" + }, + options: { + header: { + left: 'prev,next today', + center: 'title', + right: 'month' + } + }, + get_events_method: "hrms.hr.doctype.leave_application.leave_application.get_events" +} diff --git a/hrms/hr/doctype/leave_application/leave_application_dashboard.html b/hrms/hr/doctype/leave_application/leave_application_dashboard.html new file mode 100644 index 0000000..e755322 --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application_dashboard.html @@ -0,0 +1,29 @@ + +{% if not jQuery.isEmptyObject(data) %} + + + + + + + + + + + + + {% for(const [key, value] of Object.entries(data)) { %} + + + + + + + + + {% } %} + +
{{ __("Leave Type") }}{{ __("Total Allocated Leave(s)") }}{{ __("Expired Leave(s)") }}{{ __("Used Leave(s)") }}{{ __("Leave(s) Pending Approval") }}{{ __("Available Leave(s)") }}
{%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["leaves_pending_approval"] %} {%= value["remaining_leaves"] %}
+{% else %} +

No Leave has been allocated.

+{% endif %} diff --git a/hrms/hr/doctype/leave_application/leave_application_dashboard.py b/hrms/hr/doctype/leave_application/leave_application_dashboard.py new file mode 100644 index 0000000..ee5cbe9 --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application_dashboard.py @@ -0,0 +1,9 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "leave_application", + "transactions": [{"items": ["Attendance"]}], + "reports": [{"label": _("Reports"), "items": ["Employee Leave Balance"]}], + } diff --git a/hrms/hr/doctype/leave_application/leave_application_email_template.html b/hrms/hr/doctype/leave_application/leave_application_email_template.html new file mode 100644 index 0000000..dae9084 --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application_email_template.html @@ -0,0 +1,30 @@ +

Leave Application Notification

+

Details:

+ + + + + + + + + + + + + + + + + + + + + + +
Employee{{employee_name}}
Leave Type{{leave_type}}
From Date{{from_date}}
To Date{{to_date}}
Status{{status}}
+ + {% set doc_link = frappe.utils.get_url_to_form('Leave Application', name) %} + +

+ {{ _('Open Now') }} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_application/leave_application_list.js b/hrms/hr/doctype/leave_application/leave_application_list.js new file mode 100644 index 0000000..157271a --- /dev/null +++ b/hrms/hr/doctype/leave_application/leave_application_list.js @@ -0,0 +1,14 @@ +frappe.listview_settings["Leave Application"] = { + add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"], + has_indicator_for_draft: 1, + get_indicator: function (doc) { + let status_color = { + "Approved": "green", + "Rejected": "red", + "Open": "orange", + "Cancelled": "red", + "Submitted": "blue" + }; + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; + } +}; diff --git a/hrms/hr/doctype/leave_application/test_leave_application.py b/hrms/hr/doctype/leave_application/test_leave_application.py new file mode 100644 index 0000000..5d8c661 --- /dev/null +++ b/hrms/hr/doctype/leave_application/test_leave_application.py @@ -0,0 +1,1140 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import unittest + +import frappe +from frappe.permissions import clear_user_permissions_for_doctype +from frappe.utils import ( + add_days, + add_months, + get_first_day, + get_last_day, + get_year_ending, + get_year_start, + getdate, + nowdate, +) + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation +from hrms.hr.doctype.leave_application.leave_application import ( + InsufficientLeaveBalanceError, + LeaveAcrossAllocationsError, + LeaveDayBlockedError, + NotAnOptionalHoliday, + OverlapError, + get_leave_allocation_records, + get_leave_balance_on, + get_leave_details, +) +from hrms.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( + create_assignment_for_multiple_employees, +) +from hrms.hr.doctype.leave_type.test_leave_type import create_leave_type +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_holiday_list, + make_leave_application, +) +from hrms.tests.test_utils import get_first_sunday + +test_dependencies = ["Leave Type", "Leave Allocation", "Leave Block List", "Employee"] + +_test_records = [ + { + "company": "_Test Company", + "doctype": "Leave Application", + "employee": "_T-Employee-00001", + "from_date": "2013-05-01", + "description": "_Test Reason", + "leave_type": "_Test Leave Type", + "posting_date": "2013-01-02", + "to_date": "2013-05-05", + }, + { + "company": "_Test Company", + "doctype": "Leave Application", + "employee": "_T-Employee-00002", + "from_date": "2013-05-01", + "description": "_Test Reason", + "leave_type": "_Test Leave Type", + "posting_date": "2013-01-02", + "to_date": "2013-05-05", + }, + { + "company": "_Test Company", + "doctype": "Leave Application", + "employee": "_T-Employee-00001", + "from_date": "2013-01-15", + "description": "_Test Reason", + "leave_type": "_Test Leave Type LWP", + "posting_date": "2013-01-02", + "to_date": "2013-01-15", + }, +] + + +class TestLeaveApplication(unittest.TestCase): + def setUp(self): + for dt in [ + "Leave Application", + "Leave Allocation", + "Salary Slip", + "Leave Ledger Entry", + "Leave Period", + "Leave Policy Assignment", + ]: + frappe.db.delete(dt) + + frappe.set_user("Administrator") + set_leave_approver() + + frappe.db.delete("Attendance", {"employee": "_T-Employee-00001"}) + frappe.db.set_value("Employee", "_T-Employee-00001", "holiday_list", "") + + from_date = get_year_start(getdate()) + to_date = get_year_ending(getdate()) + self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) + + if not frappe.db.exists("Leave Type", "_Test Leave Type"): + frappe.get_doc( + dict(leave_type_name="_Test Leave Type", doctype="Leave Type", include_holiday=True) + ).insert() + + def tearDown(self): + frappe.db.rollback() + frappe.set_user("Administrator") + + def _clear_roles(self): + frappe.db.sql( + """delete from `tabHas Role` where parent in + ('test@example.com', 'test1@example.com', 'test2@example.com')""" + ) + + def _clear_applications(self): + frappe.db.sql("""delete from `tabLeave Application`""") + + def get_application(self, doc): + application = frappe.copy_doc(doc) + application.from_date = "2013-01-01" + application.to_date = "2013-01-05" + return application + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_validate_application_across_allocations(self): + # Test validation for application dates when negative balance is disabled + frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1) + leave_type = frappe.get_doc( + dict(leave_type_name="Test Leave Validation", doctype="Leave Type", allow_negative=False) + ).insert() + + employee = get_employee() + date = getdate() + first_sunday = get_first_sunday(self.holiday_list, for_date=get_year_start(date)) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + from_date=add_days(first_sunday, 1), + to_date=add_days(first_sunday, 4), + company="_Test Company", + status="Approved", + leave_approver="test@example.com", + ) + ) + # Application period cannot be outside leave allocation period + self.assertRaises(frappe.ValidationError, leave_application.insert) + + make_allocation_record( + leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date) + ) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + from_date=add_days(first_sunday, -10), + to_date=add_days(first_sunday, 1), + company="_Test Company", + status="Approved", + leave_approver="test@example.com", + ) + ) + + # Application period cannot be across two allocation records + self.assertRaises(LeaveAcrossAllocationsError, leave_application.insert) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_insufficient_leave_balance_validation(self): + # CASE 1: Validation when allow negative is disabled + frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1) + leave_type = frappe.get_doc( + dict(leave_type_name="Test Leave Validation", doctype="Leave Type", allow_negative=False) + ).insert() + + employee = get_employee() + date = getdate() + first_sunday = get_first_sunday(self.holiday_list, for_date=get_year_start(date)) + + # allocate 2 leaves, apply for more + make_allocation_record( + leave_type=leave_type.name, + from_date=get_year_start(date), + to_date=get_year_ending(date), + leaves=2, + ) + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + from_date=add_days(first_sunday, 1), + to_date=add_days(first_sunday, 3), + company="_Test Company", + status="Approved", + leave_approver="test@example.com", + ) + ) + self.assertRaises(InsufficientLeaveBalanceError, leave_application.insert) + + # CASE 2: Allows creating application with a warning message when allow negative is enabled + frappe.db.set_value("Leave Type", "Test Leave Validation", "allow_negative", True) + make_leave_application( + employee.name, add_days(first_sunday, 1), add_days(first_sunday, 3), leave_type.name + ) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_separate_leave_ledger_entry_for_boundary_applications(self): + # When application falls in 2 different allocations and Allow Negative is enabled + # creates separate leave ledger entries + frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1) + leave_type = frappe.get_doc( + dict( + leave_type_name="Test Leave Validation", + doctype="Leave Type", + allow_negative=True, + include_holiday=True, + ) + ).insert() + + employee = get_employee() + date = getdate() + year_start = getdate(get_year_start(date)) + year_end = getdate(get_year_ending(date)) + + make_allocation_record(leave_type=leave_type.name, from_date=year_start, to_date=year_end) + # application across allocations + + # CASE 1: from date has no allocation, to date has an allocation / both dates have allocation + start_date = add_days(year_start, -10) + application = make_leave_application( + employee.name, + start_date, + add_days(year_start, 3), + leave_type.name, + half_day=1, + half_day_date=start_date, + ) + + # 2 separate leave ledger entries + ledgers = frappe.db.get_all( + "Leave Ledger Entry", + {"transaction_type": "Leave Application", "transaction_name": application.name}, + ["leaves", "from_date", "to_date"], + order_by="from_date", + ) + self.assertEqual(len(ledgers), 2) + + self.assertEqual(ledgers[0].from_date, application.from_date) + self.assertEqual(ledgers[0].to_date, add_days(year_start, -1)) + + self.assertEqual(ledgers[1].from_date, year_start) + self.assertEqual(ledgers[1].to_date, application.to_date) + + # CASE 2: from date has an allocation, to date has no allocation + application = make_leave_application( + employee.name, add_days(year_end, -3), add_days(year_end, 5), leave_type.name + ) + + # 2 separate leave ledger entries + ledgers = frappe.db.get_all( + "Leave Ledger Entry", + {"transaction_type": "Leave Application", "transaction_name": application.name}, + ["leaves", "from_date", "to_date"], + order_by="from_date", + ) + self.assertEqual(len(ledgers), 2) + + self.assertEqual(ledgers[0].from_date, application.from_date) + self.assertEqual(ledgers[0].to_date, year_end) + + self.assertEqual(ledgers[1].from_date, add_days(year_end, 1)) + self.assertEqual(ledgers[1].to_date, application.to_date) + + def test_overwrite_attendance(self): + """check attendance is automatically created on leave approval""" + make_allocation_record() + application = self.get_application(_test_records[0]) + application.status = "Approved" + application.from_date = "2018-01-01" + application.to_date = "2018-01-03" + application.insert() + application.submit() + + attendance = frappe.get_all( + "Attendance", + ["name", "status", "attendance_date"], + dict(attendance_date=("between", ["2018-01-01", "2018-01-03"]), docstatus=("!=", 2)), + ) + + # attendance created for all 3 days + self.assertEqual(len(attendance), 3) + + # all on leave + self.assertTrue(all([d.status == "On Leave" for d in attendance])) + + # dates + dates = [d.attendance_date for d in attendance] + for d in ("2018-01-01", "2018-01-02", "2018-01-03"): + self.assertTrue(getdate(d) in dates) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_attendance_for_include_holidays(self): + # Case 1: leave type with 'Include holidays within leaves as leaves' enabled + frappe.delete_doc_if_exists("Leave Type", "Test Include Holidays", force=1) + leave_type = frappe.get_doc( + dict(leave_type_name="Test Include Holidays", doctype="Leave Type", include_holiday=True) + ).insert() + + date = getdate() + make_allocation_record( + leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date) + ) + + employee = get_employee() + first_sunday = get_first_sunday(self.holiday_list) + + leave_application = make_leave_application( + employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name + ) + leave_application.reload() + self.assertEqual(leave_application.total_leave_days, 4) + self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 4) + + leave_application.cancel() + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_attendance_update_for_exclude_holidays(self): + # Case 2: leave type with 'Include holidays within leaves as leaves' disabled + frappe.delete_doc_if_exists("Leave Type", "Test Do Not Include Holidays", force=1) + leave_type = frappe.get_doc( + dict( + leave_type_name="Test Do Not Include Holidays", doctype="Leave Type", include_holiday=False + ) + ).insert() + + date = getdate() + make_allocation_record( + leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date) + ) + + employee = get_employee() + first_sunday = get_first_sunday(self.holiday_list) + + # already marked attendance on a holiday should be deleted in this case + config = {"doctype": "Attendance", "employee": employee.name, "status": "Present"} + attendance_on_holiday = frappe.get_doc(config) + attendance_on_holiday.attendance_date = first_sunday + attendance_on_holiday.flags.ignore_validate = True + attendance_on_holiday.save() + + # already marked attendance on a non-holiday should be updated + attendance = frappe.get_doc(config) + attendance.attendance_date = add_days(first_sunday, 3) + attendance.flags.ignore_validate = True + attendance.save() + + leave_application = make_leave_application( + employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name, employee.company + ) + leave_application.reload() + + # holiday should be excluded while marking attendance + self.assertEqual(leave_application.total_leave_days, 3) + self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 3) + + # attendance on holiday deleted + self.assertFalse(frappe.db.exists("Attendance", attendance_on_holiday.name)) + + # attendance on non-holiday updated + self.assertEqual(frappe.db.get_value("Attendance", attendance.name, "status"), "On Leave") + + def test_block_list(self): + self._clear_roles() + + from frappe.utils.user import add_role + + add_role("test@example.com", "HR User") + clear_user_permissions_for_doctype("Employee") + + frappe.db.set_value( + "Department", "_Test Department - _TC", "leave_block_list", "_Test Leave Block List" + ) + + make_allocation_record() + + application = self.get_application(_test_records[0]) + application.insert() + application.reload() + application.status = "Approved" + self.assertRaises(LeaveDayBlockedError, application.submit) + + frappe.set_user("test@example.com") + + # clear other applications + frappe.db.sql("delete from `tabLeave Application`") + + application = self.get_application(_test_records[0]) + self.assertTrue(application.insert()) + + def test_overlap(self): + self._clear_roles() + self._clear_applications() + + from frappe.utils.user import add_role + + add_role("test@example.com", "Employee") + frappe.set_user("test@example.com") + + make_allocation_record() + + application = self.get_application(_test_records[0]) + application.insert() + + application = self.get_application(_test_records[0]) + self.assertRaises(OverlapError, application.insert) + + def test_overlap_with_half_day_1(self): + self._clear_roles() + self._clear_applications() + + from frappe.utils.user import add_role + + add_role("test@example.com", "Employee") + frappe.set_user("test@example.com") + + make_allocation_record() + + # leave from 1-5, half day on 3rd + application = self.get_application(_test_records[0]) + application.half_day = 1 + application.half_day_date = "2013-01-03" + application.insert() + + # Apply again for a half day leave on 3rd + application = self.get_application(_test_records[0]) + application.from_date = "2013-01-03" + application.to_date = "2013-01-03" + application.half_day = 1 + application.half_day_date = "2013-01-03" + application.insert() + + # Apply again for a half day leave on 3rd + application = self.get_application(_test_records[0]) + application.from_date = "2013-01-03" + application.to_date = "2013-01-03" + application.half_day = 1 + application.half_day_date = "2013-01-03" + + self.assertRaises(OverlapError, application.insert) + + def test_overlap_with_half_day_2(self): + self._clear_roles() + self._clear_applications() + + from frappe.utils.user import add_role + + add_role("test@example.com", "Employee") + + frappe.set_user("test@example.com") + + make_allocation_record() + + # leave from 1-5, no half day + application = self.get_application(_test_records[0]) + application.insert() + + # Apply again for a half day leave on 1st + application = self.get_application(_test_records[0]) + application.half_day = 1 + application.half_day_date = application.from_date + + self.assertRaises(OverlapError, application.insert) + + def test_overlap_with_half_day_3(self): + self._clear_roles() + self._clear_applications() + + from frappe.utils.user import add_role + + add_role("test@example.com", "Employee") + + frappe.set_user("test@example.com") + + make_allocation_record() + + # leave from 1-5, half day on 5th + application = self.get_application(_test_records[0]) + application.half_day = 1 + application.half_day_date = "2013-01-05" + application.insert() + + # Apply leave from 4-7, half day on 5th + application = self.get_application(_test_records[0]) + application.from_date = "2013-01-04" + application.to_date = "2013-01-07" + application.half_day = 1 + application.half_day_date = "2013-01-05" + + self.assertRaises(OverlapError, application.insert) + + # Apply leave from 5-7, half day on 5th + application = self.get_application(_test_records[0]) + application.from_date = "2013-01-05" + application.to_date = "2013-01-07" + application.half_day = 1 + application.half_day_date = "2013-01-05" + application.insert() + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_optional_leave(self): + leave_period = get_leave_period() + today = nowdate() + holiday_list = "Test Holiday List for Optional Holiday" + employee = get_employee() + + first_sunday = get_first_sunday(self.holiday_list) + optional_leave_date = add_days(first_sunday, 1) + + if not frappe.db.exists("Holiday List", holiday_list): + frappe.get_doc( + dict( + doctype="Holiday List", + holiday_list_name=holiday_list, + from_date=add_months(today, -6), + to_date=add_months(today, 6), + holidays=[dict(holiday_date=optional_leave_date, description="Test")], + ) + ).insert() + + frappe.db.set_value("Leave Period", leave_period.name, "optional_holiday_list", holiday_list) + leave_type = "Test Optional Type" + if not frappe.db.exists("Leave Type", leave_type): + frappe.get_doc( + dict(leave_type_name=leave_type, doctype="Leave Type", is_optional_leave=1) + ).insert() + + allocate_leaves(employee, leave_period, leave_type, 10) + + date = add_days(first_sunday, 2) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + company="_Test Company", + description="_Test Reason", + leave_type=leave_type, + from_date=date, + to_date=date, + ) + ) + + # can only apply on optional holidays + self.assertRaises(NotAnOptionalHoliday, leave_application.insert) + + leave_application.from_date = optional_leave_date + leave_application.to_date = optional_leave_date + leave_application.status = "Approved" + leave_application.insert() + leave_application.submit() + + # check leave balance is reduced + self.assertEqual(get_leave_balance_on(employee.name, leave_type, optional_leave_date), 9) + + def test_leaves_allowed(self): + employee = get_employee() + leave_period = get_leave_period() + frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) + leave_type = frappe.get_doc( + dict(leave_type_name="Test Leave Type", doctype="Leave Type", max_leaves_allowed=5) + ).insert() + + date = add_days(nowdate(), -7) + + allocate_leaves(employee, leave_period, leave_type.name, 5) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + description="_Test Reason", + from_date=date, + to_date=add_days(date, 2), + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + leave_application.submit() + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + description="_Test Reason", + from_date=add_days(date, 4), + to_date=add_days(date, 8), + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + self.assertRaises(frappe.ValidationError, leave_application.insert) + + def test_applicable_after(self): + employee = get_employee() + leave_period = get_leave_period() + frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) + leave_type = frappe.get_doc( + dict(leave_type_name="Test Leave Type", doctype="Leave Type", applicable_after=15) + ).insert() + date = add_days(nowdate(), -7) + frappe.db.set_value("Employee", employee.name, "date_of_joining", date) + allocate_leaves(employee, leave_period, leave_type.name, 10) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + description="_Test Reason", + from_date=date, + to_date=add_days(date, 4), + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + + self.assertRaises(frappe.ValidationError, leave_application.insert) + + frappe.delete_doc_if_exists("Leave Type", "Test Leave Type 1", force=1) + leave_type_1 = frappe.get_doc( + dict(leave_type_name="Test Leave Type 1", doctype="Leave Type") + ).insert() + + allocate_leaves(employee, leave_period, leave_type_1.name, 10) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type_1.name, + description="_Test Reason", + from_date=date, + to_date=add_days(date, 4), + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + + self.assertTrue(leave_application.insert()) + frappe.db.set_value("Employee", employee.name, "date_of_joining", "2010-01-01") + + def test_max_continuous_leaves(self): + employee = get_employee() + leave_period = get_leave_period() + frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) + leave_type = frappe.get_doc( + dict( + leave_type_name="Test Leave Type", + doctype="Leave Type", + max_leaves_allowed=15, + max_continuous_days_allowed=3, + ) + ).insert() + + date = add_days(nowdate(), -7) + + allocate_leaves(employee, leave_period, leave_type.name, 10) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + description="_Test Reason", + from_date=date, + to_date=add_days(date, 4), + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + + self.assertRaises(frappe.ValidationError, leave_application.insert) + + def test_leave_balance_near_allocaton_expiry(self): + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90, + ) + leave_type.insert() + + create_carry_forwarded_allocation(employee, leave_type) + details = get_leave_balance_on( + employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8), for_consumption=True + ) + + self.assertEqual(details.leave_balance_for_consumption, 21) + self.assertEqual(details.leave_balance, 30) + + def test_earned_leaves_creation(self): + from hrms.hr.utils import allocate_earned_leaves + + leave_period = get_leave_period() + employee = get_employee() + leave_type = "Test Earned Leave Type" + make_policy_assignment(employee, leave_type, leave_period) + + for i in range(0, 14): + allocate_earned_leaves() + + self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6) + + # validate earned leaves creation without maximum leaves + frappe.db.set_value("Leave Type", leave_type, "max_leaves_allowed", 0) + + for i in range(0, 6): + allocate_earned_leaves() + + self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9) + + # test to not consider current leave in leave balance while submitting + def test_current_leave_on_submit(self): + employee = get_employee() + + leave_type = "Sick Leave" + if not frappe.db.exists("Leave Type", leave_type): + frappe.get_doc(dict(leave_type_name=leave_type, doctype="Leave Type")).insert() + + allocation = frappe.get_doc( + dict( + doctype="Leave Allocation", + employee=employee.name, + leave_type=leave_type, + from_date="2018-10-01", + to_date="2018-10-10", + new_leaves_allocated=1, + ) + ) + allocation.insert(ignore_permissions=True) + allocation.submit() + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type, + description="_Test Reason", + from_date="2018-10-02", + to_date="2018-10-02", + company="_Test Company", + status="Approved", + leave_approver="test@example.com", + ) + ) + self.assertTrue(leave_application.insert()) + leave_application.submit() + self.assertEqual(leave_application.docstatus, 1) + + def test_creation_of_leave_ledger_entry_on_submit(self): + employee = get_employee() + + leave_type = create_leave_type(leave_type_name="Test Leave Type 1") + leave_type.save() + + leave_allocation = create_leave_allocation( + employee=employee.name, employee_name=employee.employee_name, leave_type=leave_type.name + ) + leave_allocation.submit() + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + from_date=add_days(nowdate(), 1), + to_date=add_days(nowdate(), 4), + description="_Test Reason", + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + leave_application.submit() + leave_ledger_entry = frappe.get_all( + "Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_application.name) + ) + + self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1) + + # check if leave ledger entry is deleted on cancellation + leave_application.cancel() + self.assertFalse( + frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_application.name}) + ) + + def test_ledger_entry_creation_on_intermediate_allocation_expiry(self): + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90, + include_holiday=True, + ) + leave_type.submit() + + create_carry_forwarded_allocation(employee, leave_type) + + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee.name, + leave_type=leave_type.name, + from_date=add_days(nowdate(), -3), + to_date=add_days(nowdate(), 7), + half_day=1, + half_day_date=add_days(nowdate(), -3), + description="_Test Reason", + company="_Test Company", + docstatus=1, + status="Approved", + ) + ) + leave_application.submit() + + leave_ledger_entry = frappe.get_all( + "Leave Ledger Entry", "*", filters=dict(transaction_name=leave_application.name) + ) + + self.assertEqual(len(leave_ledger_entry), 2) + self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, -8.5) + self.assertEqual(leave_ledger_entry[1].leaves, -2) + + def test_leave_application_creation_after_expiry(self): + # test leave balance for carry forwarded allocation + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90, + ) + leave_type.submit() + + create_carry_forwarded_allocation(employee, leave_type) + + self.assertEqual( + get_leave_balance_on( + employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84) + ), + 0, + ) + + def test_leave_approver_perms(self): + employee = get_employee() + user = "test_approver_perm_emp@example.com" + make_employee(user, "_Test Company") + + # set approver for employee + employee.reload() + employee.leave_approver = user + employee.save() + self.assertTrue("Leave Approver" in frappe.get_roles(user)) + + make_allocation_record(employee.name) + + application = self.get_application(_test_records[0]) + application.from_date = "2018-01-01" + application.to_date = "2018-01-03" + application.leave_approver = user + application.insert() + self.assertTrue(application.name in frappe.share.get_shared("Leave Application", user)) + + # check shared doc revoked + application.reload() + application.leave_approver = "test@example.com" + application.save() + self.assertTrue(application.name not in frappe.share.get_shared("Leave Application", user)) + + application.reload() + application.leave_approver = user + application.save() + + frappe.set_user(user) + application.reload() + application.status = "Approved" + application.submit() + + # unset leave approver + frappe.set_user("Administrator") + employee.reload() + employee.leave_approver = "" + employee.save() + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_get_leave_details_for_dashboard(self): + employee = get_employee() + date = getdate() + year_start = getdate(get_year_start(date)) + year_end = getdate(get_year_ending(date)) + + # ALLOCATION = 30 + allocation = make_allocation_record( + employee=employee.name, from_date=year_start, to_date=year_end + ) + + # USED LEAVES = 4 + first_sunday = get_first_sunday(self.holiday_list) + leave_application = make_leave_application( + employee.name, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" + ) + leave_application.reload() + + # LEAVES PENDING APPROVAL = 1 + leave_application = make_leave_application( + employee.name, + add_days(first_sunday, 5), + add_days(first_sunday, 5), + "_Test Leave Type", + submit=False, + ) + leave_application.status = "Open" + leave_application.save() + + details = get_leave_details(employee.name, allocation.from_date) + leave_allocation = details["leave_allocation"]["_Test Leave Type"] + self.assertEqual(leave_allocation["total_leaves"], 30) + self.assertEqual(leave_allocation["leaves_taken"], 4) + self.assertEqual(leave_allocation["expired_leaves"], 0) + self.assertEqual(leave_allocation["leaves_pending_approval"], 1) + self.assertEqual(leave_allocation["remaining_leaves"], 26) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_get_earned_leave_details_for_dashboard(self): + from hrms.hr.utils import allocate_earned_leaves + + leave_period = get_leave_period() + employee = get_employee() + leave_type = "Test Earned Leave Type" + leave_policy_assignments = make_policy_assignment(employee, leave_type, leave_period) + allocation = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "name", + ) + allocation = frappe.get_doc("Leave Allocation", allocation) + allocation.new_leaves_allocated = 2 + allocation.save() + + for i in range(0, 6): + allocate_earned_leaves() + + first_sunday = get_first_sunday(self.holiday_list) + make_leave_application( + employee.name, add_days(first_sunday, 1), add_days(first_sunday, 1), leave_type + ) + + details = get_leave_details(employee.name, allocation.from_date) + leave_allocation = details["leave_allocation"][leave_type] + expected = { + "total_leaves": 2.0, + "expired_leaves": 0.0, + "leaves_taken": 1.0, + "leaves_pending_approval": 0.0, + "remaining_leaves": 1.0, + } + self.assertEqual(leave_allocation, expected) + + details = get_leave_details(employee.name, getdate()) + leave_allocation = details["leave_allocation"][leave_type] + + expected = { + "total_leaves": 5.0, + "expired_leaves": 0.0, + "leaves_taken": 1.0, + "leaves_pending_approval": 0.0, + "remaining_leaves": 4.0, + } + self.assertEqual(leave_allocation, expected) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_get_leave_allocation_records(self): + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90, + ) + leave_type.insert() + + leave_alloc = create_carry_forwarded_allocation(employee, leave_type) + details = get_leave_allocation_records(employee.name, getdate(), leave_type.name) + expected_data = { + "from_date": getdate(leave_alloc.from_date), + "to_date": getdate(leave_alloc.to_date), + "total_leaves_allocated": 30.0, + "unused_leaves": 15.0, + "new_leaves_allocated": 15.0, + "leave_type": leave_type.name, + } + self.assertEqual(details.get(leave_type.name), expected_data) + + +def create_carry_forwarded_allocation(employee, leave_type): + # initial leave allocation + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + employee=employee.name, + employee_name=employee.employee_name, + from_date=add_months(nowdate(), -24), + to_date=add_months(nowdate(), -12), + carry_forward=0, + ) + leave_allocation.submit() + + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + employee=employee.name, + employee_name=employee.employee_name, + from_date=add_days(nowdate(), -84), + to_date=add_days(nowdate(), 100), + carry_forward=1, + ) + leave_allocation.submit() + + return leave_allocation + + +def make_allocation_record( + employee=None, leave_type=None, from_date=None, to_date=None, carry_forward=False, leaves=None +): + allocation = frappe.get_doc( + { + "doctype": "Leave Allocation", + "employee": employee or "_T-Employee-00001", + "leave_type": leave_type or "_Test Leave Type", + "from_date": from_date or "2013-01-01", + "to_date": to_date or "2019-12-31", + "new_leaves_allocated": leaves or 30, + "carry_forward": carry_forward, + } + ) + + allocation.insert(ignore_permissions=True) + allocation.submit() + + return allocation + + +def get_employee(): + return frappe.get_doc("Employee", "_T-Employee-00001") + + +def set_leave_approver(): + employee = get_employee() + dept_doc = frappe.get_doc("Department", employee.department) + dept_doc.append("leave_approvers", {"approver": "test@example.com"}) + dept_doc.save(ignore_permissions=True) + + +def get_leave_period(): + leave_period_name = frappe.db.get_value("Leave Period", {"company": "_Test Company"}) + if leave_period_name: + return frappe.get_doc("Leave Period", leave_period_name) + else: + return frappe.get_doc( + dict( + name="Test Leave Period", + doctype="Leave Period", + from_date=add_months(nowdate(), -6), + to_date=add_months(nowdate(), 6), + company="_Test Company", + is_active=1, + ) + ).insert() + + +def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, eligible_leaves=0): + allocate_leave = frappe.get_doc( + { + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": employee.name, + "employee_name": employee.employee_name, + "leave_type": leave_type, + "from_date": leave_period.from_date, + "to_date": leave_period.to_date, + "new_leaves_allocated": new_leaves_allocated, + "docstatus": 1, + } + ).insert() + + allocate_leave.submit() + + +def make_policy_assignment(employee, leave_type, leave_period): + frappe.delete_doc_if_exists("Leave Type", leave_type, force=1) + frappe.get_doc( + dict( + leave_type_name=leave_type, + doctype="Leave Type", + is_earned_leave=1, + earned_leave_frequency="Monthly", + rounding=0.5, + max_leaves_allowed=6, + ) + ).insert() + + leave_policy = frappe.get_doc( + { + "doctype": "Leave Policy", + "title": "Test Leave Policy", + "leave_policy_details": [{"leave_type": leave_type, "annual_allocation": 6}], + } + ).insert() + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + + leave_policy_assignments = create_assignment_for_multiple_employees( + [employee.name], frappe._dict(data) + ) + return leave_policy_assignments diff --git a/hrms/hr/doctype/leave_application/test_records.json b/hrms/hr/doctype/leave_application/test_records.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/hrms/hr/doctype/leave_application/test_records.json @@ -0,0 +1 @@ +[] diff --git a/hrms/hr/doctype/leave_block_list/README.md b/hrms/hr/doctype/leave_block_list/README.md new file mode 100644 index 0000000..b23396c --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/README.md @@ -0,0 +1 @@ +List of days on which leaves can only be approved by special users. \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list/__init__.py b/hrms/hr/doctype/leave_block_list/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_block_list/leave_block_list.js b/hrms/hr/doctype/leave_block_list/leave_block_list.js new file mode 100644 index 0000000..7ccf59d --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/leave_block_list.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Block List', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/leave_block_list/leave_block_list.json b/hrms/hr/doctype/leave_block_list/leave_block_list.json new file mode 100644 index 0000000..fdb975b --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/leave_block_list.json @@ -0,0 +1,248 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:leave_block_list_name", + "beta": 0, + "creation": "2013-02-18 17:43:12", + "custom": 0, + "description": "Block Holidays on important days.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_block_list_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Leave Block List Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 1, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "If not checked, the list will have to be added to each Department where it has to be applied.", + "fieldname": "applies_to_all_departments", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Applies to Company", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Stop users from making Leave Applications on following days.", + "fieldname": "block_days", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Block Days", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_block_list_dates", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Leave Block List Dates", + "length": 0, + "no_copy": 0, + "options": "Leave Block List Date", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Allow the following users to approve Leave Applications for block days.", + "fieldname": "allow_list", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Allow Users", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_block_list_allowed", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Leave Block List Allowed", + "length": 0, + "no_copy": 0, + "options": "Leave Block List Allow", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-calendar", + "idx": 1, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-11-03 16:01:42.171113", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Block List", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "is_custom": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "sort_order": "ASC", + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list/leave_block_list.py b/hrms/hr/doctype/leave_block_list/leave_block_list.py new file mode 100644 index 0000000..a57ba84 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/leave_block_list.py @@ -0,0 +1,78 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class LeaveBlockList(Document): + def validate(self): + dates = [] + for d in self.get("leave_block_list_dates"): + + # date is not repeated + if d.block_date in dates: + frappe.msgprint(_("Date is repeated") + ":" + d.block_date, raise_exception=1) + dates.append(d.block_date) + + +@frappe.whitelist() +def get_applicable_block_dates(from_date, to_date, employee=None, company=None, all_lists=False): + block_dates = [] + for block_list in get_applicable_block_lists(employee, company, all_lists): + block_dates.extend( + frappe.db.sql( + """select block_date, reason + from `tabLeave Block List Date` where parent=%s + and block_date between %s and %s""", + (block_list, from_date, to_date), + as_dict=1, + ) + ) + + return block_dates + + +def get_applicable_block_lists(employee=None, company=None, all_lists=False): + block_lists = [] + + if not employee: + employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}) + if not employee: + return [] + + if not company: + company = frappe.db.get_value("Employee", employee, "company") + + def add_block_list(block_list): + if block_list: + if all_lists or not is_user_in_allow_list(block_list): + block_lists.append(block_list) + + # per department + department = frappe.db.get_value("Employee", employee, "department") + if department: + block_list = frappe.db.get_value("Department", department, "leave_block_list") + add_block_list(block_list) + + # global + for block_list in frappe.db.sql_list( + """select name from `tabLeave Block List` + where applies_to_all_departments=1 and company=%s""", + company, + ): + add_block_list(block_list) + + return list(set(block_lists)) + + +def is_user_in_allow_list(block_list): + return frappe.session.user in frappe.db.sql_list( + """select allow_user + from `tabLeave Block List Allow` where parent=%s""", + block_list, + ) diff --git a/hrms/hr/doctype/leave_block_list/leave_block_list_dashboard.py b/hrms/hr/doctype/leave_block_list/leave_block_list_dashboard.py new file mode 100644 index 0000000..afeb5de --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/leave_block_list_dashboard.py @@ -0,0 +1,2 @@ +def get_data(): + return {"fieldname": "leave_block_list", "transactions": [{"items": ["Department"]}]} diff --git a/hrms/hr/doctype/leave_block_list/test_leave_block_list.py b/hrms/hr/doctype/leave_block_list/test_leave_block_list.py new file mode 100644 index 0000000..475c184 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/test_leave_block_list.py @@ -0,0 +1,50 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import unittest + +import frappe +from frappe.utils import getdate + +from hrms.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates + + +class TestLeaveBlockList(unittest.TestCase): + def tearDown(self): + frappe.set_user("Administrator") + + def test_get_applicable_block_dates(self): + frappe.set_user("test@example.com") + frappe.db.set_value( + "Department", "_Test Department - _TC", "leave_block_list", "_Test Leave Block List" + ) + self.assertTrue( + getdate("2013-01-02") + in [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")] + ) + + def test_get_applicable_block_dates_for_allowed_user(self): + frappe.set_user("test1@example.com") + frappe.db.set_value( + "Department", "_Test Department 1 - _TC", "leave_block_list", "_Test Leave Block List" + ) + self.assertEqual( + [], [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")] + ) + + def test_get_applicable_block_dates_all_lists(self): + frappe.set_user("test1@example.com") + frappe.db.set_value( + "Department", "_Test Department 1 - _TC", "leave_block_list", "_Test Leave Block List" + ) + self.assertTrue( + getdate("2013-01-02") + in [ + d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03", all_lists=True) + ] + ) + + +test_dependencies = ["Employee"] + +test_records = frappe.get_test_records("Leave Block List") diff --git a/hrms/hr/doctype/leave_block_list/test_records.json b/hrms/hr/doctype/leave_block_list/test_records.json new file mode 100644 index 0000000..0456511 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list/test_records.json @@ -0,0 +1,27 @@ +[ + { + "company": "_Test Company", + "doctype": "Leave Block List", + "leave_block_list_allowed": [ + { + "allow_user": "test1@example.com", + "doctype": "Leave Block List Allow", + "parent": "_Test Leave Block List", + "parentfield": "leave_block_list_allowed", + "parenttype": "Leave Block List" + } + ], + "leave_block_list_dates": [ + { + "block_date": "2013-01-02", + "doctype": "Leave Block List Date", + "parent": "_Test Leave Block List", + "parentfield": "leave_block_list_dates", + "parenttype": "Leave Block List", + "reason": "First work day" + } + ], + "leave_block_list_name": "_Test Leave Block List", + "year": "_Test Fiscal Year 2013" + } +] \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list_allow/README.md b/hrms/hr/doctype/leave_block_list_allow/README.md new file mode 100644 index 0000000..a735039 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list_allow/README.md @@ -0,0 +1 @@ +User allowed to approve leave on blocked date. \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list_allow/__init__.py b/hrms/hr/doctype/leave_block_list_allow/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.json b/hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.json new file mode 100644 index 0000000..fe10c78 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.json @@ -0,0 +1,60 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2013-02-22 01:27:47", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "editable_grid": 1, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "allow_user", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Allow User", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "200px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "200px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-07-11 03:28:02.032277", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Block List Allow", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.py b/hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.py new file mode 100644 index 0000000..50dc125 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list_allow/leave_block_list_allow.py @@ -0,0 +1,11 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class LeaveBlockListAllow(Document): + pass diff --git a/hrms/hr/doctype/leave_block_list_date/README.md b/hrms/hr/doctype/leave_block_list_date/README.md new file mode 100644 index 0000000..658f359 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list_date/README.md @@ -0,0 +1 @@ +Date blocked on parent Leave Block List. \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list_date/__init__.py b/hrms/hr/doctype/leave_block_list_date/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_block_list_date/leave_block_list_date.json b/hrms/hr/doctype/leave_block_list_date/leave_block_list_date.json new file mode 100644 index 0000000..dbb9039 --- /dev/null +++ b/hrms/hr/doctype/leave_block_list_date/leave_block_list_date.json @@ -0,0 +1,85 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2013-02-22 01:27:47", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "editable_grid": 1, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "block_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Block Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "200px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "200px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "reason", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Reason", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "200px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "200px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 1, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-07-11 03:28:02.099296", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Block List Date", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_block_list_date/leave_block_list_date.py b/hrms/hr/doctype/leave_block_list_date/leave_block_list_date.py new file mode 100644 index 0000000..36550cc --- /dev/null +++ b/hrms/hr/doctype/leave_block_list_date/leave_block_list_date.py @@ -0,0 +1,11 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class LeaveBlockListDate(Document): + pass diff --git a/hrms/hr/doctype/leave_control_panel/README.md b/hrms/hr/doctype/leave_control_panel/README.md new file mode 100644 index 0000000..f7d3357 --- /dev/null +++ b/hrms/hr/doctype/leave_control_panel/README.md @@ -0,0 +1 @@ +Tool to allocate leaves in bulk. \ No newline at end of file diff --git a/hrms/hr/doctype/leave_control_panel/__init__.py b/hrms/hr/doctype/leave_control_panel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_control_panel/leave_control_panel.js b/hrms/hr/doctype/leave_control_panel/leave_control_panel.js new file mode 100644 index 0000000..4a45080 --- /dev/null +++ b/hrms/hr/doctype/leave_control_panel/leave_control_panel.js @@ -0,0 +1,24 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on("Leave Control Panel", { + onload: function(frm) { + if (!frm.doc.from_date) { + frm.set_value('from_date', frappe.datetime.get_today()); + } + }, + refresh: function(frm) { + frm.disable_save(); + }, + company: function(frm) { + if(frm.doc.company) { + frm.set_query("department", function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); + } + } +}); diff --git a/hrms/hr/doctype/leave_control_panel/leave_control_panel.json b/hrms/hr/doctype/leave_control_panel/leave_control_panel.json new file mode 100644 index 0000000..d9c592a --- /dev/null +++ b/hrms/hr/doctype/leave_control_panel/leave_control_panel.json @@ -0,0 +1,158 @@ +{ + "actions": [], + "allow_copy": 1, + "creation": "2013-01-10 16:34:15", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "select_employees_section", + "company", + "employment_type", + "branch", + "department", + "column_break1", + "designation", + "employee_grade", + "employee", + "allocate_leaves_section", + "from_date", + "to_date", + "carry_forward", + "no_of_days", + "allocate", + "column_break_16", + "leave_policy", + "leave_type" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "employment_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employment Type (optional)", + "options": "Employment Type" + }, + { + "fieldname": "branch", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Branch (optional)", + "options": "Branch" + }, + { + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department (optional)", + "options": "Department" + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation (optional)", + "options": "Designation" + }, + { + "fieldname": "employee_grade", + "fieldtype": "Link", + "label": "Employee Grade (optional)", + "options": "Employee Grade" + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee (optional)", + "options": "Employee" + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1 + }, + { + "fieldname": "leave_policy", + "fieldtype": "Link", + "label": "Leave Policy", + "options": "Leave Policy" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "label": "Leave Type", + "options": "Leave Type" + }, + { + "default": "0", + "description": "Please select Carry Forward if you also want to include previous fiscal year's balance leaves to this fiscal year", + "fieldname": "carry_forward", + "fieldtype": "Check", + "label": "Carry Forward" + }, + { + "fieldname": "no_of_days", + "fieldtype": "Float", + "label": "New Leaves Allocated (In Days)", + "reqd": 1 + }, + { + "fieldname": "allocate", + "fieldtype": "Button", + "label": "Allocate", + "options": "allocate_leave" + }, + { + "fieldname": "select_employees_section", + "fieldtype": "Section Break", + "label": "Select Employees" + }, + { + "fieldname": "allocate_leaves_section", + "fieldtype": "Section Break", + "label": "Allocate Leaves" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + } + ], + "hide_toolbar": 1, + "icon": "fa fa-cog", + "idx": 1, + "issingle": 1, + "links": [], + "modified": "2019-12-12 18:51:41.573349", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Control Panel", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "read": 1, + "role": "HR User", + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_control_panel/leave_control_panel.py b/hrms/hr/doctype/leave_control_panel/leave_control_panel.py new file mode 100644 index 0000000..c57f8ae --- /dev/null +++ b/hrms/hr/doctype/leave_control_panel/leave_control_panel.py @@ -0,0 +1,61 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _, msgprint +from frappe.model.document import Document +from frappe.utils import cint, comma_and, cstr, flt + + +class LeaveControlPanel(Document): + def get_employees(self): + conditions, values = [], [] + for field in ["company", "employment_type", "branch", "designation", "department"]: + if self.get(field): + conditions.append("{0}=%s".format(field)) + values.append(self.get(field)) + + condition_str = " and " + " and ".join(conditions) if len(conditions) else "" + + e = frappe.db.sql( + "select name from tabEmployee where status='Active' {condition}".format( + condition=condition_str + ), + tuple(values), + ) + + return e + + def validate_values(self): + for f in ["from_date", "to_date", "leave_type", "no_of_days"]: + if not self.get(f): + frappe.throw(_("{0} is required").format(self.meta.get_label(f))) + self.validate_from_to_dates("from_date", "to_date") + + @frappe.whitelist() + def allocate_leave(self): + self.validate_values() + leave_allocated_for = [] + employees = self.get_employees() + if not employees: + frappe.throw(_("No employee found")) + + for d in self.get_employees(): + try: + la = frappe.new_doc("Leave Allocation") + la.set("__islocal", 1) + la.employee = cstr(d[0]) + la.employee_name = frappe.db.get_value("Employee", cstr(d[0]), "employee_name") + la.leave_type = self.leave_type + la.from_date = self.from_date + la.to_date = self.to_date + la.carry_forward = cint(self.carry_forward) + la.new_leaves_allocated = flt(self.no_of_days) + la.docstatus = 1 + la.save() + leave_allocated_for.append(d[0]) + except Exception: + pass + if leave_allocated_for: + msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for))) diff --git a/hrms/hr/doctype/leave_control_panel/test_leave_control_panel.py b/hrms/hr/doctype/leave_control_panel/test_leave_control_panel.py new file mode 100644 index 0000000..d5a9bc0 --- /dev/null +++ b/hrms/hr/doctype/leave_control_panel/test_leave_control_panel.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestLeaveControlPanel(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/leave_encashment/__init__.py b/hrms/hr/doctype/leave_encashment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_encashment/leave_encashment.js b/hrms/hr/doctype/leave_encashment/leave_encashment.js new file mode 100644 index 0000000..45e4144 --- /dev/null +++ b/hrms/hr/doctype/leave_encashment/leave_encashment.js @@ -0,0 +1,64 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Encashment', { + onload: function(frm) { + // Ignore cancellation of doctype on cancel all. + frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; + }, + setup: function(frm) { + frm.set_query("leave_type", function() { + return { + filters: { + allow_encashment: 1 + } + } + }) + }, + refresh: function(frm) { + cur_frm.set_intro(""); + if(frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) { + frm.set_intro(__("Fill the form and save it")); + } + }, + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('get_leave_details_for_encashment') + ]); + } + }, + leave_type: function(frm) { + frm.trigger("get_leave_details_for_encashment"); + }, + encashment_date: function(frm) { + frm.trigger("get_leave_details_for_encashment"); + }, + get_leave_details_for_encashment: function(frm) { + if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type) { + return frappe.call({ + method: "get_leave_details_for_encashment", + doc: frm.doc, + callback: function(r) { + frm.refresh_fields(); + } + }); + } + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, +}); diff --git a/hrms/hr/doctype/leave_encashment/leave_encashment.json b/hrms/hr/doctype/leave_encashment/leave_encashment.json new file mode 100644 index 0000000..cc4e53e --- /dev/null +++ b/hrms/hr/doctype/leave_encashment/leave_encashment.json @@ -0,0 +1,228 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-ENC-.YYYY.-.#####", + "creation": "2018-04-13 15:31:51.197046", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "leave_period", + "employee", + "employee_name", + "department", + "company", + "column_break_4", + "leave_type", + "leave_allocation", + "leave_balance", + "encashable_days", + "amended_from", + "payroll", + "encashment_date", + "additional_salary", + "column_break_14", + "currency", + "encashment_amount" + ], + "fields": [ + { + "fieldname": "leave_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Leave Period", + "options": "Leave Period", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Leave Type", + "options": "Leave Type", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "leave_allocation", + "fieldtype": "Link", + "label": "Leave Allocation", + "no_copy": 1, + "options": "Leave Allocation", + "read_only": 1 + }, + { + "fieldname": "leave_balance", + "fieldtype": "Float", + "label": "Leave Balance", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "encashable_days", + "fieldtype": "Float", + "label": "Encashable days", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Encashment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "payroll", + "fieldtype": "Section Break", + "label": "Payroll" + }, + { + "fieldname": "encashment_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Encashment Amount", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "default": "Today", + "fieldname": "encashment_date", + "fieldtype": "Date", + "label": "Encashment Date" + }, + { + "fieldname": "additional_salary", + "fieldtype": "Link", + "label": "Additional Salary", + "no_copy": 1, + "options": "Additional Salary", + "read_only": 1 + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-18 19:16:52.414356", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Encashment", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_encashment/leave_encashment.py b/hrms/hr/doctype/leave_encashment/leave_encashment.py new file mode 100644 index 0000000..dad89fc --- /dev/null +++ b/hrms/hr/doctype/leave_encashment/leave_encashment.py @@ -0,0 +1,190 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate, nowdate + +from hrms.hr.doctype.leave_application.leave_application import get_leaves_for_period +from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry +from hrms.hr.utils import set_employee_name, validate_active_employee +from hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( + get_assigned_salary_structure, +) + + +class LeaveEncashment(Document): + def validate(self): + set_employee_name(self) + validate_active_employee(self.employee) + self.get_leave_details_for_encashment() + self.validate_salary_structure() + + if not self.encashment_date: + self.encashment_date = getdate(nowdate()) + + def validate_salary_structure(self): + if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): + frappe.throw( + _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( + self.employee + ) + ) + + def before_submit(self): + if self.encashment_amount <= 0: + frappe.throw(_("You can only submit Leave Encashment for a valid encashment amount")) + + def on_submit(self): + if not self.leave_allocation: + self.leave_allocation = self.get_leave_allocation().get("name") + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.company = frappe.get_value("Employee", self.employee, "company") + additional_salary.employee = self.employee + additional_salary.currency = self.currency + earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component") + if not earning_component: + frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type)) + additional_salary.salary_component = earning_component + additional_salary.payroll_date = self.encashment_date + additional_salary.amount = self.encashment_amount + additional_salary.ref_doctype = self.doctype + additional_salary.ref_docname = self.name + additional_salary.submit() + + # Set encashed leaves in Allocation + frappe.db.set_value( + "Leave Allocation", + self.leave_allocation, + "total_leaves_encashed", + frappe.db.get_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed") + + self.encashable_days, + ) + + self.create_leave_ledger_entry() + + def on_cancel(self): + if self.additional_salary: + frappe.get_doc("Additional Salary", self.additional_salary).cancel() + self.db_set("additional_salary", "") + + if self.leave_allocation: + frappe.db.set_value( + "Leave Allocation", + self.leave_allocation, + "total_leaves_encashed", + frappe.db.get_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed") + - self.encashable_days, + ) + self.create_leave_ledger_entry(submit=False) + + @frappe.whitelist() + def get_leave_details_for_encashment(self): + salary_structure = get_assigned_salary_structure( + self.employee, self.encashment_date or getdate(nowdate()) + ) + if not salary_structure: + frappe.throw( + _("No Salary Structure assigned for Employee {0} on given date {1}").format( + self.employee, self.encashment_date + ) + ) + + if not frappe.db.get_value("Leave Type", self.leave_type, "allow_encashment"): + frappe.throw(_("Leave Type {0} is not encashable").format(self.leave_type)) + + allocation = self.get_leave_allocation() + + if not allocation: + frappe.throw( + _("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format( + self.employee, self.leave_type + ) + ) + + self.leave_balance = ( + allocation.total_leaves_allocated + - allocation.carry_forwarded_leaves_count + # adding this because the function returns a -ve number + + get_leaves_for_period( + self.employee, self.leave_type, allocation.from_date, self.encashment_date + ) + ) + + encashable_days = self.leave_balance - frappe.db.get_value( + "Leave Type", self.leave_type, "encashment_threshold_days" + ) + self.encashable_days = encashable_days if encashable_days > 0 else 0 + + per_day_encashment = frappe.db.get_value( + "Salary Structure", salary_structure, "leave_encashment_amount_per_day" + ) + self.encashment_amount = ( + self.encashable_days * per_day_encashment if per_day_encashment > 0 else 0 + ) + + self.leave_allocation = allocation.name + return True + + def get_leave_allocation(self): + date = self.encashment_date or getdate() + + LeaveAllocation = frappe.qb.DocType("Leave Allocation") + leave_allocation = ( + frappe.qb.from_(LeaveAllocation) + .select( + LeaveAllocation.name, + LeaveAllocation.from_date, + LeaveAllocation.to_date, + LeaveAllocation.total_leaves_allocated, + LeaveAllocation.carry_forwarded_leaves_count, + ) + .where( + ((LeaveAllocation.from_date <= date) & (date <= LeaveAllocation.to_date)) + & (LeaveAllocation.docstatus == 1) + & (LeaveAllocation.leave_type == self.leave_type) + & (LeaveAllocation.employee == self.employee) + ) + ).run(as_dict=True) + + return leave_allocation[0] if leave_allocation else None + + def create_leave_ledger_entry(self, submit=True): + args = frappe._dict( + leaves=self.encashable_days * -1, + from_date=self.encashment_date, + to_date=self.encashment_date, + is_carry_forward=0, + ) + create_leave_ledger_entry(self, args, submit) + + # create reverse entry for expired leaves + leave_allocation = self.get_leave_allocation() + if not leave_allocation: + return + + to_date = leave_allocation.get("to_date") + if to_date < getdate(nowdate()): + args = frappe._dict( + leaves=self.encashable_days, from_date=to_date, to_date=to_date, is_carry_forward=0 + ) + create_leave_ledger_entry(self, args, submit) + + +def create_leave_encashment(leave_allocation): + """Creates leave encashment for the given allocations""" + for allocation in leave_allocation: + if not get_assigned_salary_structure(allocation.employee, allocation.to_date): + continue + leave_encashment = frappe.get_doc( + dict( + doctype="Leave Encashment", + leave_period=allocation.leave_period, + employee=allocation.employee, + leave_type=allocation.leave_type, + encashment_date=allocation.to_date, + ) + ) + leave_encashment.insert(ignore_permissions=True) diff --git a/hrms/hr/doctype/leave_encashment/test_leave_encashment.py b/hrms/hr/doctype/leave_encashment/test_leave_encashment.py new file mode 100644 index 0000000..4cc6268 --- /dev/null +++ b/hrms/hr/doctype/leave_encashment/test_leave_encashment.py @@ -0,0 +1,160 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, get_year_ending, get_year_start, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.leave_period.test_leave_period import create_leave_period +from hrms.hr.doctype.leave_policy.test_leave_policy import create_leave_policy +from hrms.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( + create_assignment_for_multiple_employees, +) +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_holiday_list, + make_leave_application, +) +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + +test_records = frappe.get_test_records("Leave Type") + + +class TestLeaveEncashment(FrappeTestCase): + def setUp(self): + frappe.db.delete("Leave Period") + frappe.db.delete("Leave Policy Assignment") + frappe.db.delete("Leave Allocation") + frappe.db.delete("Leave Ledger Entry") + frappe.db.delete("Additional Salary") + frappe.db.delete("Leave Encashment") + + if not frappe.db.exists("Leave Type", "_Test Leave Type Encashment"): + frappe.get_doc(test_records[2]).insert() + + date = getdate() + year_start = getdate(get_year_start(date)) + year_end = getdate(get_year_ending(date)) + + make_holiday_list("_Test Leave Encashment", year_start, year_end) + + # create the leave policy + leave_policy = create_leave_policy( + leave_type="_Test Leave Type Encashment", annual_allocation=10 + ) + leave_policy.submit() + + # create employee, salary structure and assignment + self.employee = make_employee("test_employee_encashment@example.com", company="_Test Company") + + self.leave_period = create_leave_period(year_start, year_end, "_Test Company") + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": self.leave_period.name, + } + + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee], frappe._dict(data) + ) + + salary_structure = make_salary_structure( + "Salary Structure for Encashment", + "Monthly", + self.employee, + other_details={"leave_encashment_amount_per_day": 50}, + ) + + @set_holiday_list("_Test Leave Encashment", "_Test Company") + def test_leave_balance_value_and_amount(self): + leave_encashment = frappe.get_doc( + dict( + doctype="Leave Encashment", + employee=self.employee, + leave_type="_Test Leave Type Encashment", + leave_period=self.leave_period.name, + encashment_date=self.leave_period.to_date, + currency="INR", + ) + ).insert() + + self.assertEqual(leave_encashment.leave_balance, 10) + self.assertEqual(leave_encashment.encashable_days, 5) + self.assertEqual(leave_encashment.encashment_amount, 250) + + leave_encashment.submit() + + # assert links + add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0] + self.assertTrue(add_sal) + + @set_holiday_list("_Test Leave Encashment", "_Test Company") + def test_leave_balance_value_with_leaves_and_amount(self): + date = self.leave_period.from_date + leave_application = make_leave_application( + self.employee, date, add_days(date, 3), "_Test Leave Type Encashment" + ) + leave_application.reload() + + leave_encashment = frappe.get_doc( + dict( + doctype="Leave Encashment", + employee=self.employee, + leave_type="_Test Leave Type Encashment", + leave_period=self.leave_period.name, + encashment_date=self.leave_period.to_date, + currency="INR", + ) + ).insert() + + self.assertEqual(leave_encashment.leave_balance, 10 - leave_application.total_leave_days) + # encashable days threshold is 5, total leaves are 6, so encashable days = 6-5 = 1 + # with charge of 50 per day + self.assertEqual(leave_encashment.encashable_days, leave_encashment.leave_balance - 5) + self.assertEqual(leave_encashment.encashment_amount, 50) + + leave_encashment.submit() + + # assert links + add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0] + self.assertTrue(add_sal) + + @set_holiday_list("_Test Leave Encashment", "_Test Company") + def test_creation_of_leave_ledger_entry_on_submit(self): + leave_encashment = frappe.get_doc( + dict( + doctype="Leave Encashment", + employee=self.employee, + leave_type="_Test Leave Type Encashment", + leave_period=self.leave_period.name, + encashment_date=self.leave_period.to_date, + currency="INR", + ) + ).insert() + + leave_encashment.submit() + + leave_ledger_entry = frappe.get_all( + "Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_encashment.name) + ) + + self.assertEqual(len(leave_ledger_entry), 1) + self.assertEqual(leave_ledger_entry[0].employee, leave_encashment.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_encashment.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1) + + # check if leave ledger entry is deleted on cancellation + + frappe.db.sql( + "Delete from `tabAdditional Salary` WHERE ref_docname = %s", (leave_encashment.name) + ) + + leave_encashment.cancel() + self.assertFalse( + frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_encashment.name}) + ) diff --git a/hrms/hr/doctype/leave_ledger_entry/__init__.py b/hrms/hr/doctype/leave_ledger_entry/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.js b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.js new file mode 100644 index 0000000..c68d518 --- /dev/null +++ b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Ledger Entry', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.json new file mode 100644 index 0000000..d74760a --- /dev/null +++ b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -0,0 +1,190 @@ +{ + "actions": [], + "creation": "2019-05-09 15:47:39.760406", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "leave_type", + "transaction_type", + "transaction_name", + "company", + "leaves", + "column_break_7", + "from_date", + "to_date", + "holiday_list", + "is_carry_forward", + "is_expired", + "is_lwp", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee" + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "options": "Leave Type" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Ledger Entry", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "transaction_type", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Transaction Type", + "options": "DocType" + }, + { + "fieldname": "transaction_name", + "fieldtype": "Dynamic Link", + "label": "Transaction Name", + "options": "transaction_type" + }, + { + "fieldname": "leaves", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Leaves" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" + }, + { + "default": "0", + "fieldname": "is_carry_forward", + "fieldtype": "Check", + "label": "Is Carry Forward" + }, + { + "default": "0", + "fieldname": "is_expired", + "fieldtype": "Check", + "label": "Is Expired" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_lwp", + "fieldtype": "Check", + "label": "Is Leave Without Pay" + }, + { + "fieldname": "holiday_list", + "fieldtype": "Link", + "label": "Holiday List", + "options": "Holiday List" + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-01-04 18:47:45.146652", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Ledger Entry", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "employee" +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py new file mode 100644 index 0000000..f7a23a5 --- /dev/null +++ b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -0,0 +1,227 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import DATE_FORMAT, flt, getdate, today + + +class LeaveLedgerEntry(Document): + def validate(self): + if getdate(self.from_date) > getdate(self.to_date): + frappe.throw(_("To date needs to be before from date")) + + def on_cancel(self): + # allow cancellation of expiry leaves + if self.is_expired: + frappe.db.set_value("Leave Allocation", self.transaction_name, "expired", 0) + else: + frappe.throw(_("Only expired allocation can be cancelled")) + + +def validate_leave_allocation_against_leave_application(ledger): + """Checks that leave allocation has no leave application against it""" + leave_application_records = frappe.db.sql_list( + """ + SELECT transaction_name + FROM `tabLeave Ledger Entry` + WHERE + employee=%s + AND leave_type=%s + AND transaction_type='Leave Application' + AND from_date>=%s + AND to_date<=%s + """, + (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date), + ) + + if leave_application_records: + frappe.throw( + _("Leave allocation {0} is linked with the Leave Application {1}").format( + ledger.transaction_name, ", ".join(leave_application_records) + ) + ) + + +def create_leave_ledger_entry(ref_doc, args, submit=True): + ledger = frappe._dict( + doctype="Leave Ledger Entry", + employee=ref_doc.employee, + employee_name=ref_doc.employee_name, + leave_type=ref_doc.leave_type, + transaction_type=ref_doc.doctype, + transaction_name=ref_doc.name, + is_carry_forward=0, + is_expired=0, + is_lwp=0, + ) + ledger.update(args) + + if submit: + doc = frappe.get_doc(ledger) + doc.flags.ignore_permissions = 1 + doc.submit() + else: + delete_ledger_entry(ledger) + + +def delete_ledger_entry(ledger): + """Delete ledger entry on cancel of leave application/allocation/encashment""" + if ledger.transaction_type == "Leave Allocation": + validate_leave_allocation_against_leave_application(ledger) + + expired_entry = get_previous_expiry_ledger_entry(ledger) + frappe.db.sql( + """DELETE + FROM `tabLeave Ledger Entry` + WHERE + `transaction_name`=%s + OR `name`=%s""", + (ledger.transaction_name, expired_entry), + ) + + +def get_previous_expiry_ledger_entry(ledger): + """Returns the expiry ledger entry having same creation date as the ledger entry to be cancelled""" + creation_date = frappe.db.get_value( + "Leave Ledger Entry", + filters={ + "transaction_name": ledger.transaction_name, + "is_expired": 0, + "transaction_type": "Leave Allocation", + }, + fieldname=["creation"], + ) + + creation_date = creation_date.strftime(DATE_FORMAT) if creation_date else "" + + return frappe.db.get_value( + "Leave Ledger Entry", + filters={ + "creation": ("like", creation_date + "%"), + "employee": ledger.employee, + "leave_type": ledger.leave_type, + "is_expired": 1, + "docstatus": 1, + "is_carry_forward": 0, + }, + fieldname=["name"], + ) + + +def process_expired_allocation(): + """Check if a carry forwarded allocation has expired and create a expiry ledger entry + Case 1: carry forwarded expiry period is set for the leave type, + create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves + Case 2: leave type has no specific expiry period for carry forwarded leaves + and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves. + """ + + # fetch leave type records that has carry forwarded leaves expiry + leave_type_records = frappe.db.get_values( + "Leave Type", filters={"expire_carry_forwarded_leaves_after_days": (">", 0)}, fieldname=["name"] + ) + + leave_type = [record[0] for record in leave_type_records] or [""] + + # fetch non expired leave ledger entry of transaction_type allocation + expire_allocation = frappe.db.sql( + """ + SELECT + leaves, to_date, employee, leave_type, + is_carry_forward, transaction_name as name, transaction_type + FROM `tabLeave Ledger Entry` l + WHERE (NOT EXISTS + (SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_name = l.transaction_name + AND transaction_type = 'Leave Allocation' + AND name<>l.name + AND docstatus = 1 + AND ( + is_carry_forward=l.is_carry_forward + OR (is_carry_forward = 0 AND leave_type not in %s) + ))) + AND transaction_type = 'Leave Allocation' + AND to_date < %s""", + (leave_type, today()), + as_dict=1, + ) + + if expire_allocation: + create_expiry_ledger_entry(expire_allocation) + + +def create_expiry_ledger_entry(allocations): + """Create ledger entry for expired allocation""" + for allocation in allocations: + if allocation.is_carry_forward: + expire_carried_forward_allocation(allocation) + else: + expire_allocation(allocation) + + +def get_remaining_leaves(allocation): + """Returns remaining leaves from the given allocation""" + return frappe.db.get_value( + "Leave Ledger Entry", + filters={ + "employee": allocation.employee, + "leave_type": allocation.leave_type, + "to_date": ("<=", allocation.to_date), + "docstatus": 1, + }, + fieldname=["SUM(leaves)"], + ) + + +@frappe.whitelist() +def expire_allocation(allocation, expiry_date=None): + """expires non-carry forwarded allocation""" + leaves = get_remaining_leaves(allocation) + expiry_date = expiry_date if expiry_date else allocation.to_date + + # allows expired leaves entry to be created/reverted + if leaves: + args = dict( + leaves=flt(leaves) * -1, + transaction_name=allocation.name, + transaction_type="Leave Allocation", + from_date=expiry_date, + to_date=expiry_date, + is_carry_forward=0, + is_expired=1, + ) + create_leave_ledger_entry(allocation, args) + + frappe.db.set_value("Leave Allocation", allocation.name, "expired", 1) + + +def expire_carried_forward_allocation(allocation): + """Expires remaining leaves in the on carried forward allocation""" + from hrms.hr.doctype.leave_application.leave_application import get_leaves_for_period + + leaves_taken = get_leaves_for_period( + allocation.employee, + allocation.leave_type, + allocation.from_date, + allocation.to_date, + skip_expired_leaves=False, + ) + leaves = flt(allocation.leaves) + flt(leaves_taken) + + # allow expired leaves entry to be created + if leaves > 0: + args = frappe._dict( + transaction_name=allocation.name, + transaction_type="Leave Allocation", + leaves=allocation.leaves * -1, + is_carry_forward=allocation.is_carry_forward, + is_expired=1, + from_date=allocation.to_date, + to_date=allocation.to_date, + ) + create_leave_ledger_entry(allocation, args) diff --git a/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js new file mode 100644 index 0000000..889325b --- /dev/null +++ b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js @@ -0,0 +1,13 @@ +frappe.listview_settings['Leave Ledger Entry'] = { + onload: function(listview) { + if(listview.page.fields_dict.transaction_type) { + listview.page.fields_dict.transaction_type.get_query = function() { + return { + "filters": { + "name": ["in", ["Leave Allocation", "Leave Application", "Leave Encashment"]], + } + }; + }; + } + } +}; diff --git a/hrms/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py b/hrms/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py new file mode 100644 index 0000000..3121109 --- /dev/null +++ b/hrms/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestLeaveLedgerEntry(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/leave_period/__init__.py b/hrms/hr/doctype/leave_period/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_period/leave_period.js b/hrms/hr/doctype/leave_period/leave_period.js new file mode 100644 index 0000000..0e88bc1 --- /dev/null +++ b/hrms/hr/doctype/leave_period/leave_period.js @@ -0,0 +1,20 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Period', { + from_date: (frm)=>{ + if (frm.doc.from_date && !frm.doc.to_date) { + var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); + frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); + } + }, + onload: (frm) => { + frm.set_query("department", function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); + }, +}); diff --git a/hrms/hr/doctype/leave_period/leave_period.json b/hrms/hr/doctype/leave_period/leave_period.json new file mode 100644 index 0000000..84ce114 --- /dev/null +++ b/hrms/hr/doctype/leave_period/leave_period.json @@ -0,0 +1,108 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-LPR-.YYYY.-.#####", + "creation": "2018-04-13 15:20:52.864288", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_date", + "to_date", + "is_active", + "column_break_3", + "company", + "optional_holiday_list" + ], + "fields": [ + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "To Date", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "optional_holiday_list", + "fieldtype": "Link", + "label": "Holiday List for Optional Leave", + "options": "Holiday List" + } + ], + "links": [], + "modified": "2022-01-13 13:28:12.951025", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Period", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "search_fields": "from_date, to_date, company", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_period/leave_period.py b/hrms/hr/doctype/leave_period/leave_period.py new file mode 100644 index 0000000..25a7784 --- /dev/null +++ b/hrms/hr/doctype/leave_period/leave_period.py @@ -0,0 +1,20 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate + +from hrms.hr.utils import validate_overlap + + +class LeavePeriod(Document): + def validate(self): + self.validate_dates() + validate_overlap(self, self.from_date, self.to_date, self.company) + + def validate_dates(self): + if getdate(self.from_date) >= getdate(self.to_date): + frappe.throw(_("To date can not be equal or less than from date")) diff --git a/hrms/hr/doctype/leave_period/leave_period_dashboard.py b/hrms/hr/doctype/leave_period/leave_period_dashboard.py new file mode 100644 index 0000000..854f988 --- /dev/null +++ b/hrms/hr/doctype/leave_period/leave_period_dashboard.py @@ -0,0 +1,8 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "leave_period", + "transactions": [{"label": _("Transactions"), "items": ["Leave Allocation"]}], + } diff --git a/hrms/hr/doctype/leave_period/test_leave_period.py b/hrms/hr/doctype/leave_period/test_leave_period.py new file mode 100644 index 0000000..0923574 --- /dev/null +++ b/hrms/hr/doctype/leave_period/test_leave_period.py @@ -0,0 +1,40 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe + +import erpnext + +test_dependencies = ["Employee", "Leave Type", "Leave Policy"] + + +class TestLeavePeriod(unittest.TestCase): + pass + + +def create_leave_period(from_date, to_date, company=None): + leave_period = frappe.db.get_value( + "Leave Period", + dict( + company=company or erpnext.get_default_company(), + from_date=from_date, + to_date=to_date, + is_active=1, + ), + "name", + ) + if leave_period: + return frappe.get_doc("Leave Period", leave_period) + + leave_period = frappe.get_doc( + { + "doctype": "Leave Period", + "company": company or erpnext.get_default_company(), + "from_date": from_date, + "to_date": to_date, + "is_active": 1, + } + ).insert() + return leave_period diff --git a/hrms/hr/doctype/leave_policy/__init__.py b/hrms/hr/doctype/leave_policy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_policy/leave_policy.js b/hrms/hr/doctype/leave_policy/leave_policy.js new file mode 100644 index 0000000..fdf8e0c --- /dev/null +++ b/hrms/hr/doctype/leave_policy/leave_policy.js @@ -0,0 +1,31 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Policy', { +}); + +frappe.ui.form.on('Leave Policy Detail',{ + leave_type: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.leave_type){ + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Leave Type", + fieldname: "max_leaves_allowed", + filters: { name: child.leave_type } + }, + callback: function(r) { + if (r.message) { + child.annual_allocation = r.message.max_leaves_allowed; + refresh_field("leave_policy_details"); + } + } + }); + } + else{ + child.annual_allocation = ""; + refresh_field("leave_policy_details"); + } + } +}); diff --git a/hrms/hr/doctype/leave_policy/leave_policy.json b/hrms/hr/doctype/leave_policy/leave_policy.json new file mode 100644 index 0000000..6ac8f20 --- /dev/null +++ b/hrms/hr/doctype/leave_policy/leave_policy.json @@ -0,0 +1,107 @@ +{ + "actions": [], + "autoname": "HR-LPOL-.YYYY.-.#####", + "creation": "2018-04-13 16:06:19.507624", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "leave_allocations_section", + "leave_policy_details", + "amended_from" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "leave_allocations_section", + "fieldtype": "Section Break", + "label": "Leave Allocations" + }, + { + "fieldname": "leave_policy_details", + "fieldtype": "Table", + "label": "Leave Policy Details", + "options": "Leave Policy Detail", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Policy", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 13:07:40.556500", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Policy", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "title", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "title", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_policy/leave_policy.py b/hrms/hr/doctype/leave_policy/leave_policy.py new file mode 100644 index 0000000..33c9493 --- /dev/null +++ b/hrms/hr/doctype/leave_policy/leave_policy.py @@ -0,0 +1,22 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class LeavePolicy(Document): + def validate(self): + if self.leave_policy_details: + for lp_detail in self.leave_policy_details: + max_leaves_allowed = frappe.db.get_value( + "Leave Type", lp_detail.leave_type, "max_leaves_allowed" + ) + if max_leaves_allowed > 0 and lp_detail.annual_allocation > max_leaves_allowed: + frappe.throw( + _("Maximum leave allowed in the leave type {0} is {1}").format( + lp_detail.leave_type, max_leaves_allowed + ) + ) diff --git a/hrms/hr/doctype/leave_policy/leave_policy_dashboard.py b/hrms/hr/doctype/leave_policy/leave_policy_dashboard.py new file mode 100644 index 0000000..57ea93e --- /dev/null +++ b/hrms/hr/doctype/leave_policy/leave_policy_dashboard.py @@ -0,0 +1,10 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "leave_policy", + "transactions": [ + {"label": _("Leaves"), "items": ["Leave Policy Assignment", "Leave Allocation"]}, + ], + } diff --git a/hrms/hr/doctype/leave_policy/test_leave_policy.py b/hrms/hr/doctype/leave_policy/test_leave_policy.py new file mode 100644 index 0000000..33d5508 --- /dev/null +++ b/hrms/hr/doctype/leave_policy/test_leave_policy.py @@ -0,0 +1,39 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe + + +class TestLeavePolicy(unittest.TestCase): + def test_max_leave_allowed(self): + random_leave_type = frappe.get_all("Leave Type", fields=["name", "max_leaves_allowed"]) + if random_leave_type: + random_leave_type = random_leave_type[0] + leave_type = frappe.get_doc("Leave Type", random_leave_type.name) + leave_type.max_leaves_allowed = 2 + leave_type.save() + + leave_policy = create_leave_policy( + leave_type=leave_type.name, annual_allocation=leave_type.max_leaves_allowed + 1 + ) + + self.assertRaises(frappe.ValidationError, leave_policy.insert) + + +def create_leave_policy(**args): + """Returns an object of leave policy""" + args = frappe._dict(args) + return frappe.get_doc( + { + "doctype": "Leave Policy", + "title": "Test Leave Policy", + "leave_policy_details": [ + { + "leave_type": args.leave_type or "_Test Leave Type", + "annual_allocation": args.annual_allocation or 10, + } + ], + } + ) diff --git a/hrms/hr/doctype/leave_policy_assignment/__init__.py b/hrms/hr/doctype/leave_policy_assignment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.js b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.js new file mode 100644 index 0000000..0aaf4cf --- /dev/null +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.js @@ -0,0 +1,59 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Policy Assignment', { + onload: function(frm) { + frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; + + frm.set_query('leave_policy', function() { + return { + filters: { + "docstatus": 1 + } + }; + }); + frm.set_query('leave_period', function() { + return { + filters: { + "is_active": 1, + "company": frm.doc.company + } + }; + }); + }, + + assignment_based_on: function(frm) { + if (frm.doc.assignment_based_on) { + frm.events.set_effective_date(frm); + } else { + frm.set_value("effective_from", ''); + frm.set_value("effective_to", ''); + } + }, + + leave_period: function(frm) { + if (frm.doc.leave_period) { + frm.events.set_effective_date(frm); + } + }, + + set_effective_date: function(frm) { + if (frm.doc.assignment_based_on == "Leave Period" && frm.doc.leave_period) { + frappe.model.with_doc("Leave Period", frm.doc.leave_period, function () { + let from_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "from_date"); + let to_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "to_date"); + frm.set_value("effective_from", from_date); + frm.set_value("effective_to", to_date); + + }); + } else if (frm.doc.assignment_based_on == "Joining Date" && frm.doc.employee) { + frappe.model.with_doc("Employee", frm.doc.employee, function () { + let from_date = frappe.model.get_value("Employee", frm.doc.employee, "date_of_joining"); + frm.set_value("effective_from", from_date); + frm.set_value("effective_to", frappe.datetime.add_months(frm.doc.effective_from, 12)); + }); + } + frm.refresh(); + } + +}); diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.json new file mode 100644 index 0000000..27f0540 --- /dev/null +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.json @@ -0,0 +1,171 @@ +{ + "actions": [], + "autoname": "HR-LPOL-ASSGN-.#####", + "creation": "2020-08-19 13:02:43.343666", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "company", + "leave_policy", + "carry_forward", + "column_break_5", + "assignment_based_on", + "leave_period", + "effective_from", + "effective_to", + "leaves_allocated", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee name", + "read_only": 1 + }, + { + "fieldname": "leave_policy", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Leave Policy", + "options": "Leave Policy", + "reqd": 1 + }, + { + "fieldname": "assignment_based_on", + "fieldtype": "Select", + "label": "Assignment based on", + "options": "\nLeave Period\nJoining Date" + }, + { + "depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", + "fieldname": "leave_period", + "fieldtype": "Link", + "label": "Leave Period", + "mandatory_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", + "options": "Leave Period" + }, + { + "fieldname": "effective_from", + "fieldtype": "Date", + "label": "Effective From", + "read_only_depends_on": "eval:doc.assignment_based_on", + "reqd": 1 + }, + { + "fieldname": "effective_to", + "fieldtype": "Date", + "label": "Effective To", + "read_only_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Policy Assignment", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "carry_forward", + "fieldtype": "Check", + "label": "Add unused leaves from previous allocations" + }, + { + "default": "0", + "fieldname": "leaves_allocated", + "fieldtype": "Check", + "hidden": 1, + "label": "Leaves Allocated", + "no_copy": 1, + "print_hide": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-13 13:37:11.218882", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Policy Assignment", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py new file mode 100644 index 0000000..d25fdd1 --- /dev/null +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -0,0 +1,257 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import json +from math import ceil + +import frappe +from frappe import _, bold +from frappe.model.document import Document +from frappe.utils import date_diff, flt, formatdate, get_last_day, get_link_to_form, getdate + + +class LeavePolicyAssignment(Document): + def validate(self): + self.set_dates() + self.validate_policy_assignment_overlap() + self.warn_about_carry_forwarding() + + def on_submit(self): + self.grant_leave_alloc_for_employee() + + def set_dates(self): + if self.assignment_based_on == "Leave Period": + self.effective_from, self.effective_to = frappe.db.get_value( + "Leave Period", self.leave_period, ["from_date", "to_date"] + ) + elif self.assignment_based_on == "Joining Date": + self.effective_from = frappe.db.get_value("Employee", self.employee, "date_of_joining") + + def validate_policy_assignment_overlap(self): + leave_policy_assignments = frappe.get_all( + "Leave Policy Assignment", + filters={ + "employee": self.employee, + "name": ("!=", self.name), + "docstatus": 1, + "effective_to": (">=", self.effective_from), + "effective_from": ("<=", self.effective_to), + }, + ) + + if len(leave_policy_assignments): + frappe.throw( + _("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}").format( + bold(self.leave_policy), + bold(self.employee), + bold(formatdate(self.effective_from)), + bold(formatdate(self.effective_to)), + ) + ) + + def warn_about_carry_forwarding(self): + if not self.carry_forward: + return + + leave_types = get_leave_type_details() + leave_policy = frappe.get_doc("Leave Policy", self.leave_policy) + + for policy in leave_policy.leave_policy_details: + leave_type = leave_types.get(policy.leave_type) + if not leave_type.is_carry_forward: + msg = _( + "Leaves for the Leave Type {0} won't be carry-forwarded since carry-forwarding is disabled." + ).format(frappe.bold(get_link_to_form("Leave Type", leave_type.name))) + frappe.msgprint(msg, indicator="orange", alert=True) + + @frappe.whitelist() + def grant_leave_alloc_for_employee(self): + if self.leaves_allocated: + frappe.throw(_("Leave already have been assigned for this Leave Policy Assignment")) + else: + leave_allocations = {} + leave_type_details = get_leave_type_details() + + leave_policy = frappe.get_doc("Leave Policy", self.leave_policy) + date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") + + for leave_policy_detail in leave_policy.leave_policy_details: + if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp: + leave_allocation, new_leaves_allocated = self.create_leave_allocation( + leave_policy_detail.leave_type, + leave_policy_detail.annual_allocation, + leave_type_details, + date_of_joining, + ) + leave_allocations[leave_policy_detail.leave_type] = { + "name": leave_allocation, + "leaves": new_leaves_allocated, + } + self.db_set("leaves_allocated", 1) + return leave_allocations + + def create_leave_allocation( + self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining + ): + # Creates leave allocation for the given employee in the provided leave period + carry_forward = self.carry_forward + if self.carry_forward and not leave_type_details.get(leave_type).is_carry_forward: + carry_forward = 0 + + new_leaves_allocated = self.get_new_leaves( + leave_type, new_leaves_allocated, leave_type_details, date_of_joining + ) + + allocation = frappe.get_doc( + dict( + doctype="Leave Allocation", + employee=self.employee, + leave_type=leave_type, + from_date=self.effective_from, + to_date=self.effective_to, + new_leaves_allocated=new_leaves_allocated, + leave_period=self.leave_period if self.assignment_based_on == "Leave Policy" else "", + leave_policy_assignment=self.name, + leave_policy=self.leave_policy, + carry_forward=carry_forward, + ) + ) + allocation.save(ignore_permissions=True) + allocation.submit() + return allocation.name, new_leaves_allocated + + def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining): + from frappe.model.meta import get_field_precision + + precision = get_field_precision( + frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated") + ) + + # Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0 + if leave_type_details.get(leave_type).is_compensatory == 1: + new_leaves_allocated = 0 + + elif leave_type_details.get(leave_type).is_earned_leave == 1: + if not self.assignment_based_on: + new_leaves_allocated = 0 + else: + # get leaves for past months if assignment is based on Leave Period / Joining Date + new_leaves_allocated = self.get_leaves_for_passed_months( + leave_type, new_leaves_allocated, leave_type_details, date_of_joining + ) + + # Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period + elif getdate(date_of_joining) > getdate(self.effective_from): + remaining_period = (date_diff(self.effective_to, date_of_joining) + 1) / ( + date_diff(self.effective_to, self.effective_from) + 1 + ) + new_leaves_allocated = ceil(new_leaves_allocated * remaining_period) + + return flt(new_leaves_allocated, precision) + + def get_leaves_for_passed_months( + self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining + ): + from hrms.hr.utils import get_monthly_earned_leave + + current_date = frappe.flags.current_date or getdate() + if current_date > getdate(self.effective_to): + current_date = getdate(self.effective_to) + + from_date = getdate(self.effective_from) + if getdate(date_of_joining) > from_date: + from_date = getdate(date_of_joining) + + months_passed = 0 + based_on_doj = leave_type_details.get(leave_type).based_on_date_of_joining + + if current_date.year == from_date.year and current_date.month >= from_date.month: + months_passed = current_date.month - from_date.month + months_passed = add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj) + + elif current_date.year > from_date.year: + months_passed = (12 - from_date.month) + current_date.month + months_passed = add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj) + + if months_passed > 0: + monthly_earned_leave = get_monthly_earned_leave( + new_leaves_allocated, + leave_type_details.get(leave_type).earned_leave_frequency, + leave_type_details.get(leave_type).rounding, + ) + new_leaves_allocated = monthly_earned_leave * months_passed + else: + new_leaves_allocated = 0 + + return new_leaves_allocated + + +def add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj): + date = getdate(frappe.flags.current_date) or getdate() + + if based_on_doj: + # if leave type allocation is based on DOJ, and the date of assignment creation is same as DOJ, + # then the month should be considered + if date.day == date_of_joining.day: + months_passed += 1 + else: + last_day_of_month = get_last_day(date) + # if its the last day of the month, then that month should be considered + if last_day_of_month == date: + months_passed += 1 + + return months_passed + + +@frappe.whitelist() +def create_assignment_for_multiple_employees(employees, data): + + if isinstance(employees, str): + employees = json.loads(employees) + + if isinstance(data, str): + data = frappe._dict(json.loads(data)) + + docs_name = [] + for employee in employees: + assignment = frappe.new_doc("Leave Policy Assignment") + assignment.employee = employee + assignment.assignment_based_on = data.assignment_based_on or None + assignment.leave_policy = data.leave_policy + assignment.effective_from = getdate(data.effective_from) or None + assignment.effective_to = getdate(data.effective_to) or None + assignment.leave_period = data.leave_period or None + assignment.carry_forward = data.carry_forward + assignment.save() + try: + assignment.submit() + except frappe.exceptions.ValidationError: + continue + + frappe.db.commit() + + docs_name.append(assignment.name) + + return docs_name + + +def get_leave_type_details(): + leave_type_details = frappe._dict() + leave_types = frappe.get_all( + "Leave Type", + fields=[ + "name", + "is_lwp", + "is_earned_leave", + "is_compensatory", + "based_on_date_of_joining", + "is_carry_forward", + "expire_carry_forwarded_leaves_after_days", + "earned_leave_frequency", + "rounding", + ], + ) + for d in leave_types: + leave_type_details.setdefault(d.name, d) + return leave_type_details diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py new file mode 100644 index 0000000..13b39c7 --- /dev/null +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py @@ -0,0 +1,10 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "leave_policy_assignment", + "transactions": [ + {"label": _("Leaves"), "items": ["Leave Allocation"]}, + ], + } diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js new file mode 100644 index 0000000..437e806 --- /dev/null +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js @@ -0,0 +1,117 @@ +frappe.listview_settings['Leave Policy Assignment'] = { + onload: function (list_view) { + let me = this; + list_view.page.add_inner_button(__("Bulk Leave Policy Assignment"), function () { + me.dialog = new frappe.ui.form.MultiSelectDialog({ + doctype: "Employee", + target: cur_list, + setters: { + employee_name: '', + company: '', + department: '', + }, + data_fields: [{ + fieldname: 'leave_policy', + fieldtype: 'Link', + options: 'Leave Policy', + label: __('Leave Policy'), + reqd: 1 + }, + { + fieldname: 'assignment_based_on', + fieldtype: 'Select', + options: ["", "Leave Period"], + label: __('Assignment Based On'), + onchange: () => { + if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period") { + cur_dialog.set_df_property("effective_from", "read_only", 1); + cur_dialog.set_df_property("leave_period", "reqd", 1); + cur_dialog.set_df_property("effective_to", "read_only", 1); + } else { + cur_dialog.set_df_property("effective_from", "read_only", 0); + cur_dialog.set_df_property("leave_period", "reqd", 0); + cur_dialog.set_df_property("effective_to", "read_only", 0); + cur_dialog.set_value("effective_from", ""); + cur_dialog.set_value("effective_to", ""); + } + } + }, + { + fieldname: "leave_period", + fieldtype: 'Link', + options: "Leave Period", + label: __('Leave Period'), + depends_on: doc => { + return doc.assignment_based_on == 'Leave Period'; + }, + onchange: () => { + if (cur_dialog.fields_dict.leave_period.value) { + me.set_effective_date(); + } + }, + get_query() { + let filters = {"is_active": 1}; + if (cur_dialog.fields_dict.company.value) + filters["company"] = cur_dialog.fields_dict.company.value; + + return { + filters: filters + }; + }, + }, + { + fieldtype: "Column Break" + }, + { + fieldname: 'effective_from', + fieldtype: 'Date', + label: __('Effective From'), + reqd: 1 + }, + { + fieldname: 'effective_to', + fieldtype: 'Date', + label: __('Effective To'), + reqd: 1 + }, + { + fieldname: 'carry_forward', + fieldtype: 'Check', + label: __('Add unused leaves from previous allocations') + } + ], + get_query() { + return { + filters: { + status: ['=', 'Active'] + } + }; + }, + add_filters_group: 1, + primary_action_label: "Assign", + action(employees, data) { + frappe.call({ + method: 'hrms.hr.doctype.leave_policy_assignment.leave_policy_assignment.create_assignment_for_multiple_employees', + async: false, + args: { + employees: employees, + data: data + } + }); + cur_dialog.hide(); + } + }); + }); + }, + + set_effective_date: function () { + if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period" && cur_dialog.fields_dict.leave_period.value) { + frappe.model.with_doc("Leave Period", cur_dialog.fields_dict.leave_period.value, function () { + let from_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "from_date"); + let to_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "to_date"); + cur_dialog.set_value("effective_from", from_date); + cur_dialog.set_value("effective_to", to_date); + }); + } + } +}; diff --git a/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py new file mode 100644 index 0000000..b1ad8de --- /dev/null +++ b/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py @@ -0,0 +1,376 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, add_months, get_first_day, get_last_day, getdate + +from hrms.hr.doctype.leave_application.test_leave_application import get_employee, get_leave_period +from hrms.hr.doctype.leave_policy.test_leave_policy import create_leave_policy +from hrms.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( + create_assignment_for_multiple_employees, +) + +test_dependencies = ["Employee"] + + +class TestLeavePolicyAssignment(FrappeTestCase): + def setUp(self): + for doctype in [ + "Leave Period", + "Leave Application", + "Leave Allocation", + "Leave Policy Assignment", + "Leave Ledger Entry", + ]: + frappe.db.delete(doctype) + + employee = get_employee() + self.original_doj = employee.date_of_joining + self.employee = employee + + def test_grant_leaves(self): + leave_period = get_leave_period() + # allocation = 10 + leave_policy = create_leave_policy() + leave_policy.submit() + + self.employee.date_of_joining = get_first_day(leave_period.from_date) + self.employee.save() + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + self.assertEqual( + frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), + 1, + ) + + leave_allocation = frappe.get_list( + "Leave Allocation", + filters={ + "employee": self.employee.name, + "leave_policy": leave_policy.name, + "leave_policy_assignment": leave_policy_assignments[0], + "docstatus": 1, + }, + )[0] + leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) + + self.assertEqual(leave_alloc_doc.new_leaves_allocated, 10) + self.assertEqual(leave_alloc_doc.leave_type, "_Test Leave Type") + self.assertEqual(getdate(leave_alloc_doc.from_date), getdate(leave_period.from_date)) + self.assertEqual(getdate(leave_alloc_doc.to_date), getdate(leave_period.to_date)) + self.assertEqual(leave_alloc_doc.leave_policy, leave_policy.name) + self.assertEqual(leave_alloc_doc.leave_policy_assignment, leave_policy_assignments[0]) + + def test_allow_to_grant_all_leave_after_cancellation_of_every_leave_allocation(self): + leave_period = get_leave_period() + # create the leave policy with leave type "_Test Leave Type", allocation = 10 + leave_policy = create_leave_policy() + leave_policy.submit() + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + + # every leave is allocated no more leave can be granted now + self.assertEqual( + frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), + 1, + ) + leave_allocation = frappe.get_list( + "Leave Allocation", + filters={ + "employee": self.employee.name, + "leave_policy": leave_policy.name, + "leave_policy_assignment": leave_policy_assignments[0], + "docstatus": 1, + }, + )[0] + + leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) + leave_alloc_doc.cancel() + leave_alloc_doc.delete() + self.assertEqual( + frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), + 0, + ) + + def test_earned_leave_allocation(self): + leave_period = create_leave_period("Test Earned Leave Period") + leave_type = create_earned_leave_type("Test Earned Leave") + + leave_policy = frappe.get_doc( + { + "doctype": "Leave Policy", + "title": "Test Leave Policy", + "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 6}], + } + ).submit() + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + + # second last day of the month + # leaves allocated should be 0 since it is an earned leave and allocation happens via scheduler based on set frequency + frappe.flags.current_date = add_days(get_last_day(getdate()), -1) + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "total_leaves_allocated", + ) + self.assertEqual(leaves_allocated, 0) + + def test_earned_leave_alloc_for_passed_months_based_on_leave_period(self): + leave_period, leave_policy = setup_leave_period_and_policy( + get_first_day(add_months(getdate(), -1)) + ) + + # Case 1: assignment created one month after the leave period, should allocate 1 leave + frappe.flags.current_date = get_first_day(getdate()) + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "total_leaves_allocated", + ) + self.assertEqual(leaves_allocated, 1) + + def test_earned_leave_alloc_for_passed_months_on_month_end_based_on_leave_period(self): + leave_period, leave_policy = setup_leave_period_and_policy( + get_first_day(add_months(getdate(), -2)) + ) + # Case 2: assignment created on the last day of the leave period's latter month + # should allocate 1 leave for current month even though the month has not ended + # since the daily job might have already executed + frappe.flags.current_date = get_last_day(getdate()) + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "total_leaves_allocated", + ) + self.assertEqual(leaves_allocated, 3) + + def test_earned_leave_alloc_for_passed_months_with_cf_leaves_based_on_leave_period(self): + from hrms.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation + + leave_period, leave_policy = setup_leave_period_and_policy( + get_first_day(add_months(getdate(), -2)) + ) + # initial leave allocation = 5 + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, + leave_type="Test Earned Leave", + from_date=add_months(getdate(), -12), + to_date=add_months(getdate(), -3), + new_leaves_allocated=5, + carry_forward=0, + ) + leave_allocation.submit() + + # Case 3: assignment created on the last day of the leave period's latter month with carry forwarding + frappe.flags.current_date = get_last_day(add_months(getdate(), -1)) + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + "carry_forward": 1, + } + # carry forwarded leaves = 5, 3 leaves allocated for passed months + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + + details = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + ["total_leaves_allocated", "new_leaves_allocated", "unused_leaves", "name"], + as_dict=True, + ) + self.assertEqual(details.new_leaves_allocated, 2) + self.assertEqual(details.unused_leaves, 5) + self.assertEqual(details.total_leaves_allocated, 7) + + def test_earned_leave_alloc_for_passed_months_based_on_joining_date(self): + # tests leave alloc for earned leaves for assignment based on joining date in policy assignment + leave_type = create_earned_leave_type("Test Earned Leave") + leave_policy = frappe.get_doc( + { + "doctype": "Leave Policy", + "title": "Test Leave Policy", + "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}], + } + ).submit() + + # joining date set to 2 months back + self.employee.date_of_joining = get_first_day(add_months(getdate(), -2)) + self.employee.save() + + # assignment created on the last day of the current month + frappe.flags.current_date = get_last_day(getdate()) + data = {"assignment_based_on": "Joining Date", "leave_policy": leave_policy.name} + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "total_leaves_allocated", + ) + effective_from = frappe.db.get_value( + "Leave Policy Assignment", leave_policy_assignments[0], "effective_from" + ) + self.assertEqual(effective_from, self.employee.date_of_joining) + self.assertEqual(leaves_allocated, 3) + + def test_grant_leaves_on_doj_for_earned_leaves_based_on_leave_period(self): + # tests leave alloc based on leave period for earned leaves with "based on doj" configuration in leave type + leave_period, leave_policy = setup_leave_period_and_policy( + get_first_day(add_months(getdate(), -2)), based_on_doj=True + ) + + # joining date set to 2 months back + self.employee.date_of_joining = get_first_day(add_months(getdate(), -2)) + self.employee.save() + + # assignment created on the same day of the current month, should allocate leaves including the current month + frappe.flags.current_date = get_first_day(getdate()) + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name, + } + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "total_leaves_allocated", + ) + self.assertEqual(leaves_allocated, 3) + + def test_grant_leaves_on_doj_for_earned_leaves_based_on_joining_date(self): + # tests leave alloc based on joining date for earned leaves with "based on doj" configuration in leave type + leave_type = create_earned_leave_type("Test Earned Leave", based_on_doj=True) + leave_policy = frappe.get_doc( + { + "doctype": "Leave Policy", + "title": "Test Leave Policy", + "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}], + } + ).submit() + + # joining date set to 2 months back + # leave should be allocated for current month too since this day is same as the joining day + self.employee.date_of_joining = get_first_day(add_months(getdate(), -2)) + self.employee.save() + + # assignment created on the first day of the current month + frappe.flags.current_date = get_first_day(getdate()) + data = {"assignment_based_on": "Joining Date", "leave_policy": leave_policy.name} + leave_policy_assignments = create_assignment_for_multiple_employees( + [self.employee.name], frappe._dict(data) + ) + leaves_allocated = frappe.db.get_value( + "Leave Allocation", + {"leave_policy_assignment": leave_policy_assignments[0]}, + "total_leaves_allocated", + ) + effective_from = frappe.db.get_value( + "Leave Policy Assignment", leave_policy_assignments[0], "effective_from" + ) + self.assertEqual(effective_from, self.employee.date_of_joining) + self.assertEqual(leaves_allocated, 3) + + def tearDown(self): + frappe.db.set_value("Employee", self.employee.name, "date_of_joining", self.original_doj) + frappe.flags.current_date = None + + +def create_earned_leave_type(leave_type, based_on_doj=False): + frappe.delete_doc_if_exists("Leave Type", leave_type, force=1) + + return frappe.get_doc( + dict( + leave_type_name=leave_type, + doctype="Leave Type", + is_earned_leave=1, + earned_leave_frequency="Monthly", + rounding=0.5, + is_carry_forward=1, + based_on_date_of_joining=based_on_doj, + ) + ).insert() + + +def create_leave_period(name, start_date=None): + frappe.delete_doc_if_exists("Leave Period", name, force=1) + if not start_date: + start_date = get_first_day(getdate()) + + return frappe.get_doc( + dict( + name=name, + doctype="Leave Period", + from_date=start_date, + to_date=add_months(start_date, 12), + company="_Test Company", + is_active=1, + ) + ).insert() + + +def setup_leave_period_and_policy(start_date, based_on_doj=False): + leave_type = create_earned_leave_type("Test Earned Leave", based_on_doj) + leave_period = create_leave_period("Test Earned Leave Period", start_date=start_date) + leave_policy = frappe.get_doc( + { + "doctype": "Leave Policy", + "title": "Test Leave Policy", + "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}], + } + ).insert() + + return leave_period, leave_policy diff --git a/hrms/hr/doctype/leave_policy_detail/__init__.py b/hrms/hr/doctype/leave_policy_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.js b/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.js new file mode 100644 index 0000000..ee21f8d --- /dev/null +++ b/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Policy Detail', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.json b/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.json new file mode 100644 index 0000000..572b2f7 --- /dev/null +++ b/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.json @@ -0,0 +1,104 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-04-13 16:01:20.928853", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 3, + "fieldname": "leave_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Leave Type", + "length": 0, + "no_copy": 0, + "options": "Leave Type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "annual_allocation", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Annual Allocation", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-04-14 13:00:34.511109", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Policy Detail", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.py b/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.py new file mode 100644 index 0000000..8916d3d --- /dev/null +++ b/hrms/hr/doctype/leave_policy_detail/leave_policy_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class LeavePolicyDetail(Document): + pass diff --git a/hrms/hr/doctype/leave_policy_detail/test_leave_policy_detail.py b/hrms/hr/doctype/leave_policy_detail/test_leave_policy_detail.py new file mode 100644 index 0000000..aacf64f --- /dev/null +++ b/hrms/hr/doctype/leave_policy_detail/test_leave_policy_detail.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestLeavePolicyDetail(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/leave_type/README.md b/hrms/hr/doctype/leave_type/README.md new file mode 100644 index 0000000..695e9dd --- /dev/null +++ b/hrms/hr/doctype/leave_type/README.md @@ -0,0 +1,6 @@ +Type of Leave. + +e.g. + +- Casual Leave +- Sick Leave \ No newline at end of file diff --git a/hrms/hr/doctype/leave_type/__init__.py b/hrms/hr/doctype/leave_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/leave_type/leave_type.js b/hrms/hr/doctype/leave_type/leave_type.js new file mode 100644 index 0000000..b930ded --- /dev/null +++ b/hrms/hr/doctype/leave_type/leave_type.js @@ -0,0 +1,38 @@ +frappe.ui.form.on("Leave Type", { + refresh: function(frm) { + } +}); + + +frappe.tour["Leave Type"] = [ + { + fieldname: "max_leaves_allowed", + title: "Maximum Leave Allocation Allowed", + description: __("This field allows you to set the maximum number of leaves that can be allocated annually for this Leave Type while creating the Leave Policy") + }, + { + fieldname: "max_continuous_days_allowed", + title: "Maximum Consecutive Leaves Allowed", + description: __("This field allows you to set the maximum number of consecutive leaves an Employee can apply for.") + }, + { + fieldname: "is_optional_leave", + title: "Is Optional Leave", + description: __("Optional Leaves are holidays that Employees can choose to avail from a list of holidays published by the company.") + }, + { + fieldname: "is_compensatory", + title: "Is Compensatory Leave", + description: __("Leaves you can avail against a holiday you worked on. You can claim Compensatory Off Leave using Compensatory Leave request. Click") + " here " + __('to know more') + }, + { + fieldname: "allow_encashment", + title: "Allow Encashment", + description: __("From here, you can enable encashment for the balance leaves.") + }, + { + fieldname: "is_earned_leave", + title: "Is Earned Leaves", + description: __("Earned Leaves are leaves earned by an Employee after working with the company for a certain amount of time. Enabling this will allocate leaves on pro-rata basis by automatically updating Leave Allocation for leaves of this type at intervals set by 'Earned Leave Frequency.") + } +]; \ No newline at end of file diff --git a/hrms/hr/doctype/leave_type/leave_type.json b/hrms/hr/doctype/leave_type/leave_type.json new file mode 100644 index 0000000..d40ff09 --- /dev/null +++ b/hrms/hr/doctype/leave_type/leave_type.json @@ -0,0 +1,265 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:leave_type_name", + "creation": "2013-02-21 09:55:58", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "leave_type_name", + "max_leaves_allowed", + "applicable_after", + "max_continuous_days_allowed", + "column_break_3", + "is_carry_forward", + "is_lwp", + "is_ppl", + "fraction_of_daily_salary_per_leave", + "is_optional_leave", + "allow_negative", + "allow_over_allocation", + "include_holiday", + "is_compensatory", + "carry_forward_section", + "maximum_carry_forwarded_leaves", + "expire_carry_forwarded_leaves_after_days", + "encashment", + "allow_encashment", + "encashment_threshold_days", + "column_break_17", + "earning_component", + "earned_leave", + "is_earned_leave", + "earned_leave_frequency", + "column_break_22", + "based_on_date_of_joining", + "rounding" + ], + "fields": [ + { + "fieldname": "leave_type_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Leave Type Name", + "oldfieldname": "leave_type_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "max_leaves_allowed", + "fieldtype": "Int", + "label": "Maximum Leave Allocation Allowed" + }, + { + "fieldname": "applicable_after", + "fieldtype": "Int", + "label": "Applicable After (Working Days)" + }, + { + "fieldname": "max_continuous_days_allowed", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Consecutive Leaves Allowed", + "oldfieldname": "max_days_allowed", + "oldfieldtype": "Data" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_carry_forward", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Carry Forward", + "oldfieldname": "is_carry_forward", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "eval:doc.is_ppl == 0", + "fieldname": "is_lwp", + "fieldtype": "Check", + "label": "Is Leave Without Pay" + }, + { + "default": "0", + "description": "These leaves are holidays permitted by the company however, availing it is optional for an Employee.", + "fieldname": "is_optional_leave", + "fieldtype": "Check", + "label": "Is Optional Leave" + }, + { + "default": "0", + "fieldname": "allow_negative", + "fieldtype": "Check", + "label": "Allow Negative Balance" + }, + { + "default": "0", + "fieldname": "include_holiday", + "fieldtype": "Check", + "label": "Include holidays within leaves as leaves" + }, + { + "default": "0", + "fieldname": "is_compensatory", + "fieldtype": "Check", + "label": "Is Compensatory" + }, + { + "collapsible": 1, + "depends_on": "eval: doc.is_carry_forward == 1", + "fieldname": "carry_forward_section", + "fieldtype": "Section Break", + "label": "Carry Forward" + }, + { + "description": "Calculated in days", + "fieldname": "expire_carry_forwarded_leaves_after_days", + "fieldtype": "Int", + "label": "Expire Carry Forwarded Leaves (Days)" + }, + { + "collapsible": 1, + "fieldname": "encashment", + "fieldtype": "Section Break", + "label": "Encashment" + }, + { + "default": "0", + "fieldname": "allow_encashment", + "fieldtype": "Check", + "label": "Allow Encashment" + }, + { + "depends_on": "allow_encashment", + "fieldname": "encashment_threshold_days", + "fieldtype": "Int", + "label": "Encashment Threshold Days" + }, + { + "depends_on": "allow_encashment", + "fieldname": "earning_component", + "fieldtype": "Link", + "label": "Earning Component", + "options": "Salary Component" + }, + { + "collapsible": 1, + "fieldname": "earned_leave", + "fieldtype": "Section Break", + "label": "Earned Leave" + }, + { + "default": "0", + "fieldname": "is_earned_leave", + "fieldtype": "Check", + "label": "Is Earned Leave" + }, + { + "depends_on": "is_earned_leave", + "fieldname": "earned_leave_frequency", + "fieldtype": "Select", + "label": "Earned Leave Frequency", + "options": "Monthly\nQuarterly\nHalf-Yearly\nYearly" + }, + { + "default": "0.5", + "depends_on": "is_earned_leave", + "fieldname": "rounding", + "fieldtype": "Select", + "label": "Rounding", + "options": "\n0.25\n0.5\n1.0" + }, + { + "depends_on": "is_carry_forward", + "fieldname": "maximum_carry_forwarded_leaves", + "fieldtype": "Float", + "label": "Maximum Carry Forwarded Leaves" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.is_earned_leave", + "description": "If checked, leave will be granted on the day of joining every month.", + "fieldname": "based_on_date_of_joining", + "fieldtype": "Check", + "label": "Based On Date Of Joining" + }, + { + "default": "0", + "depends_on": "eval:doc.is_lwp == 0", + "fieldname": "is_ppl", + "fieldtype": "Check", + "label": "Is Partially Paid Leave" + }, + { + "depends_on": "eval:doc.is_ppl == 1", + "description": "For a day of leave taken, if you still pay (say) 50% of the daily salary, then enter 0.50 in this field.", + "fieldname": "fraction_of_daily_salary_per_leave", + "fieldtype": "Float", + "label": "Fraction of Daily Salary per Leave", + "mandatory_depends_on": "eval:doc.is_ppl == 1" + }, + { + "default": "0", + "description": "Allows allocating more leaves than the number of days in the allocation period.", + "fieldname": "allow_over_allocation", + "fieldtype": "Check", + "label": "Allow Over Allocation" + } + ], + "icon": "fa fa-flag", + "idx": 1, + "links": [], + "modified": "2022-05-09 05:01:38.957545", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Type", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/leave_type/leave_type.py b/hrms/hr/doctype/leave_type/leave_type.py new file mode 100644 index 0000000..82b9bd6 --- /dev/null +++ b/hrms/hr/doctype/leave_type/leave_type.py @@ -0,0 +1,33 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import today + + +class LeaveType(Document): + def validate(self): + if self.is_lwp: + leave_allocation = frappe.get_all( + "Leave Allocation", + filters={"leave_type": self.name, "from_date": ("<=", today()), "to_date": (">=", today())}, + fields=["name"], + ) + leave_allocation = [l["name"] for l in leave_allocation] + if leave_allocation: + frappe.throw( + _( + "Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay" + ).format(", ".join(leave_allocation)) + ) # nosec + + if self.is_lwp and self.is_ppl: + frappe.throw(_("Leave Type can be either without pay or partial pay")) + + if self.is_ppl and ( + self.fraction_of_daily_salary_per_leave < 0 or self.fraction_of_daily_salary_per_leave > 1 + ): + frappe.throw(_("The fraction of Daily Salary per Leave should be between 0 and 1")) diff --git a/hrms/hr/doctype/leave_type/leave_type_dashboard.py b/hrms/hr/doctype/leave_type/leave_type_dashboard.py new file mode 100644 index 0000000..269a1ec --- /dev/null +++ b/hrms/hr/doctype/leave_type/leave_type_dashboard.py @@ -0,0 +1,10 @@ +def get_data(): + return { + "fieldname": "leave_type", + "transactions": [ + { + "items": ["Leave Allocation", "Leave Application"], + }, + {"items": ["Attendance", "Leave Encashment"]}, + ], + } diff --git a/hrms/hr/doctype/leave_type/test_leave_type.py b/hrms/hr/doctype/leave_type/test_leave_type.py new file mode 100644 index 0000000..69f9e12 --- /dev/null +++ b/hrms/hr/doctype/leave_type/test_leave_type.py @@ -0,0 +1,32 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +test_records = frappe.get_test_records("Leave Type") + + +def create_leave_type(**args): + args = frappe._dict(args) + if frappe.db.exists("Leave Type", args.leave_type_name): + return frappe.get_doc("Leave Type", args.leave_type_name) + leave_type = frappe.get_doc( + { + "doctype": "Leave Type", + "leave_type_name": args.leave_type_name or "_Test Leave Type", + "include_holiday": args.include_holidays or 1, + "allow_encashment": args.allow_encashment or 0, + "is_earned_leave": args.is_earned_leave or 0, + "is_lwp": args.is_lwp or 0, + "is_ppl": args.is_ppl or 0, + "is_carry_forward": args.is_carry_forward or 0, + "expire_carry_forwarded_leaves_after_days": args.expire_carry_forwarded_leaves_after_days or 0, + "encashment_threshold_days": args.encashment_threshold_days or 5, + "earning_component": "Leave Encashment", + } + ) + + if leave_type.is_ppl: + leave_type.fraction_of_daily_salary_per_leave = args.fraction_of_daily_salary_per_leave or 0.5 + + return leave_type diff --git a/hrms/hr/doctype/leave_type/test_records.json b/hrms/hr/doctype/leave_type/test_records.json new file mode 100644 index 0000000..f1f7d8f --- /dev/null +++ b/hrms/hr/doctype/leave_type/test_records.json @@ -0,0 +1,27 @@ +[ + { + "doctype": "Leave Type", + "leave_type_name": "_Test Leave Type", + "include_holiday": 1 + }, + { + "doctype": "Leave Type", + "is_lwp": 1, + "leave_type_name": "_Test Leave Type LWP", + "include_holiday": 1 + }, + { + "doctype": "Leave Type", + "leave_type_name": "_Test Leave Type Encashment", + "include_holiday": 1, + "allow_encashment": 1, + "encashment_threshold_days": 5, + "earning_component": "Leave Encashment" + }, + { + "doctype": "Leave Type", + "leave_type_name": "_Test Leave Type Earned", + "include_holiday": 1, + "is_earned_leave": 1 + } +] \ No newline at end of file diff --git a/hrms/hr/doctype/offer_term/__init__.py b/hrms/hr/doctype/offer_term/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/offer_term/offer_term.js b/hrms/hr/doctype/offer_term/offer_term.js new file mode 100644 index 0000000..3be6e7b --- /dev/null +++ b/hrms/hr/doctype/offer_term/offer_term.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Offer Term', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/offer_term/offer_term.json b/hrms/hr/doctype/offer_term/offer_term.json new file mode 100644 index 0000000..3b7bd42 --- /dev/null +++ b/hrms/hr/doctype/offer_term/offer_term.json @@ -0,0 +1,84 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:offer_term", + "beta": 0, + "creation": "2015-03-05 13:00:30.900471", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "offer_term", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Offer Term", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-07-25 05:24:25.724664", + "modified_by": "Administrator", + "module": "HR", + "name": "Offer Term", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "HR User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/offer_term/offer_term.py b/hrms/hr/doctype/offer_term/offer_term.py new file mode 100644 index 0000000..cee6c45 --- /dev/null +++ b/hrms/hr/doctype/offer_term/offer_term.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class OfferTerm(Document): + pass diff --git a/hrms/hr/doctype/offer_term/test_offer_term.py b/hrms/hr/doctype/offer_term/test_offer_term.py new file mode 100644 index 0000000..2bea7b2 --- /dev/null +++ b/hrms/hr/doctype/offer_term/test_offer_term.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +# test_records = frappe.get_test_records('Offer Term') + + +class TestOfferTerm(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/purpose_of_travel/__init__.py b/hrms/hr/doctype/purpose_of_travel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.js b/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.js new file mode 100644 index 0000000..a9424d6 --- /dev/null +++ b/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Purpose of Travel', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.json b/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.json new file mode 100644 index 0000000..68d2d6b --- /dev/null +++ b/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.json @@ -0,0 +1,93 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:purpose_of_travel", + "beta": 0, + "creation": "2018-05-15 07:00:30.933908", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "purpose_of_travel", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Purpose of Travel", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-05-15 07:05:26.219209", + "modified_by": "Administrator", + "module": "HR", + "name": "Purpose of Travel", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.py b/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.py new file mode 100644 index 0000000..c9d6e71 --- /dev/null +++ b/hrms/hr/doctype/purpose_of_travel/purpose_of_travel.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class PurposeofTravel(Document): + pass diff --git a/hrms/hr/doctype/purpose_of_travel/test_purpose_of_travel.py b/hrms/hr/doctype/purpose_of_travel/test_purpose_of_travel.py new file mode 100644 index 0000000..354663b --- /dev/null +++ b/hrms/hr/doctype/purpose_of_travel/test_purpose_of_travel.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestPurposeofTravel(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/shift_assignment/__init__.py b/hrms/hr/doctype/shift_assignment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/shift_assignment/shift_assignment.js b/hrms/hr/doctype/shift_assignment/shift_assignment.js new file mode 100644 index 0000000..74708b1 --- /dev/null +++ b/hrms/hr/doctype/shift_assignment/shift_assignment.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Shift Assignment', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/shift_assignment/shift_assignment.json b/hrms/hr/doctype/shift_assignment/shift_assignment.json new file mode 100644 index 0000000..ce2a10f --- /dev/null +++ b/hrms/hr/doctype/shift_assignment/shift_assignment.json @@ -0,0 +1,155 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-SHA-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:25:04.562730", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "shift_type", + "status", + "column_break_3", + "company", + "start_date", + "end_date", + "shift_request", + "department", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "shift_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Shift Type", + "options": "Shift Type", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "shift_request", + "fieldtype": "Link", + "label": "Shift Request", + "options": "Shift Request", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Shift Assignment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "show_days": 1, + "show_seconds": 1 + }, + { + "allow_on_submit": 1, + "default": "Active", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Active\nInactive", + "show_days": 1, + "show_seconds": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-06-15 14:27:54.310773", + "modified_by": "Administrator", + "module": "HR", + "name": "Shift Assignment", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/shift_assignment/shift_assignment.py b/hrms/hr/doctype/shift_assignment/shift_assignment.py new file mode 100644 index 0000000..cf25a09 --- /dev/null +++ b/hrms/hr/doctype/shift_assignment/shift_assignment.py @@ -0,0 +1,518 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from datetime import datetime, timedelta +from typing import Dict, List + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.query_builder import Criterion +from frappe.utils import cstr, get_datetime, get_link_to_form, get_time, getdate, now_datetime + +from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee +from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday + +from hrms.hr.utils import validate_active_employee + + +class OverlappingShiftError(frappe.ValidationError): + pass + + +class ShiftAssignment(Document): + def validate(self): + validate_active_employee(self.employee) + self.validate_overlapping_shifts() + + if self.end_date: + self.validate_from_to_dates("start_date", "end_date") + + def validate_overlapping_shifts(self): + overlapping_dates = self.get_overlapping_dates() + if len(overlapping_dates): + # if dates are overlapping, check if timings are overlapping, else allow + overlapping_timings = has_overlapping_timings(self.shift_type, overlapping_dates[0].shift_type) + if overlapping_timings: + self.throw_overlap_error(overlapping_dates[0]) + + def get_overlapping_dates(self): + if not self.name: + self.name = "New Shift Assignment" + + shift = frappe.qb.DocType("Shift Assignment") + query = ( + frappe.qb.from_(shift) + .select(shift.name, shift.shift_type, shift.docstatus, shift.status) + .where( + (shift.employee == self.employee) + & (shift.docstatus == 1) + & (shift.name != self.name) + & (shift.status == "Active") + ) + ) + + if self.end_date: + query = query.where( + Criterion.any( + [ + Criterion.any( + [ + shift.end_date.isnull(), + ((self.start_date >= shift.start_date) & (self.start_date <= shift.end_date)), + ] + ), + Criterion.any( + [ + ((self.end_date >= shift.start_date) & (self.end_date <= shift.end_date)), + shift.start_date.between(self.start_date, self.end_date), + ] + ), + ] + ) + ) + else: + query = query.where( + shift.end_date.isnull() + | ((self.start_date >= shift.start_date) & (self.start_date <= shift.end_date)) + ) + + return query.run(as_dict=True) + + def throw_overlap_error(self, shift_details): + shift_details = frappe._dict(shift_details) + if shift_details.docstatus == 1 and shift_details.status == "Active": + msg = _( + "Employee {0} already has an active Shift {1}: {2} that overlaps within this period." + ).format( + frappe.bold(self.employee), + frappe.bold(shift_details.shift_type), + get_link_to_form("Shift Assignment", shift_details.name), + ) + frappe.throw(msg, title=_("Overlapping Shifts"), exc=OverlappingShiftError) + + +def has_overlapping_timings(shift_1: str, shift_2: str) -> bool: + """ + Accepts two shift types and checks whether their timings are overlapping + """ + curr_shift = frappe.db.get_value("Shift Type", shift_1, ["start_time", "end_time"], as_dict=True) + overlapping_shift = frappe.db.get_value( + "Shift Type", shift_2, ["start_time", "end_time"], as_dict=True + ) + + if ( + ( + curr_shift.start_time > overlapping_shift.start_time + and curr_shift.start_time < overlapping_shift.end_time + ) + or ( + curr_shift.end_time > overlapping_shift.start_time + and curr_shift.end_time < overlapping_shift.end_time + ) + or ( + curr_shift.start_time <= overlapping_shift.start_time + and curr_shift.end_time >= overlapping_shift.end_time + ) + ): + return True + return False + + +@frappe.whitelist() +def get_events(start, end, filters=None): + from frappe.desk.calendar import get_event_conditions + + employee = frappe.db.get_value( + "Employee", {"user_id": frappe.session.user}, ["name", "company"], as_dict=True + ) + if employee: + employee, company = employee.name, employee.company + else: + employee = "" + company = frappe.db.get_value("Global Defaults", None, "default_company") + + conditions = get_event_conditions("Shift Assignment", filters) + events = add_assignments(start, end, conditions=conditions) + return events + + +def add_assignments(start, end, conditions=None): + events = [] + + query = """select name, start_date, end_date, employee_name, + employee, docstatus, shift_type + from `tabShift Assignment` where + ( + start_date >= %(start_date)s + or end_date <= %(end_date)s + or (%(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date) + ) + and docstatus = 1""" + if conditions: + query += conditions + + records = frappe.db.sql(query, {"start_date": start, "end_date": end}, as_dict=True) + shift_timing_map = get_shift_type_timing([d.shift_type for d in records]) + + for d in records: + daily_event_start = d.start_date + daily_event_end = d.end_date if d.end_date else getdate() + delta = timedelta(days=1) + while daily_event_start <= daily_event_end: + start_timing = ( + frappe.utils.get_datetime(daily_event_start) + shift_timing_map[d.shift_type]["start_time"] + ) + end_timing = ( + frappe.utils.get_datetime(daily_event_start) + shift_timing_map[d.shift_type]["end_time"] + ) + daily_event_start += delta + e = { + "name": d.name, + "doctype": "Shift Assignment", + "start_date": start_timing, + "end_date": end_timing, + "title": cstr(d.employee_name) + ": " + cstr(d.shift_type), + "docstatus": d.docstatus, + "allDay": 0, + } + if e not in events: + events.append(e) + + return events + + +def get_shift_type_timing(shift_types): + shift_timing_map = {} + data = frappe.get_all( + "Shift Type", filters={"name": ("IN", shift_types)}, fields=["name", "start_time", "end_time"] + ) + + for d in data: + shift_timing_map[d.name] = d + + return shift_timing_map + + +def get_shift_for_time(shifts: List[Dict], for_timestamp: datetime) -> Dict: + """Returns shift with details for given timestamp""" + valid_shifts = [] + + for entry in shifts: + shift_details = get_shift_details(entry.shift_type, for_timestamp=for_timestamp) + + if ( + get_datetime(shift_details.actual_start) + <= get_datetime(for_timestamp) + <= get_datetime(shift_details.actual_end) + ): + valid_shifts.append(shift_details) + + valid_shifts.sort(key=lambda x: x["actual_start"]) + + if len(valid_shifts) > 1: + for i in range(len(valid_shifts) - 1): + # comparing 2 consecutive shifts and adjusting start and end times + # if they are overlapping within grace period + curr_shift = valid_shifts[i] + next_shift = valid_shifts[i + 1] + + if curr_shift and next_shift: + next_shift.actual_start = ( + curr_shift.end_datetime + if next_shift.actual_start < curr_shift.end_datetime + else next_shift.actual_start + ) + curr_shift.actual_end = ( + next_shift.actual_start + if curr_shift.actual_end > next_shift.actual_start + else curr_shift.actual_end + ) + + valid_shifts[i] = curr_shift + valid_shifts[i + 1] = next_shift + + return get_exact_shift(valid_shifts, for_timestamp) or {} + + return (valid_shifts and valid_shifts[0]) or {} + + +def get_shifts_for_date(employee: str, for_timestamp: datetime) -> List[Dict[str, str]]: + """Returns list of shifts with details for given date""" + assignment = frappe.qb.DocType("Shift Assignment") + + return ( + frappe.qb.from_(assignment) + .select(assignment.name, assignment.shift_type) + .where( + (assignment.employee == employee) + & (assignment.docstatus == 1) + & (assignment.status == "Active") + & (assignment.start_date <= getdate(for_timestamp.date())) + & ( + Criterion.any( + [ + assignment.end_date.isnull(), + (assignment.end_date.isnotnull() & (getdate(for_timestamp.date()) <= assignment.end_date)), + ] + ) + ) + ) + ).run(as_dict=True) + + +def get_shift_for_timestamp(employee: str, for_timestamp: datetime) -> Dict: + shifts = get_shifts_for_date(employee, for_timestamp) + if shifts: + return get_shift_for_time(shifts, for_timestamp) + return {} + + +def get_employee_shift( + employee: str, + for_timestamp: datetime = None, + consider_default_shift: bool = False, + next_shift_direction: str = None, +) -> Dict: + """Returns a Shift Type for the given employee on the given date. (excluding the holidays) + + :param employee: Employee for which shift is required. + :param for_timestamp: DateTime on which shift is required + :param consider_default_shift: If set to true, default shift is taken when no shift assignment is found. + :param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date. + """ + if for_timestamp is None: + for_timestamp = now_datetime() + + shift_details = get_shift_for_timestamp(employee, for_timestamp) + + # if shift assignment is not found, consider default shift + default_shift = frappe.db.get_value("Employee", employee, "default_shift") + if not shift_details and consider_default_shift: + shift_details = get_shift_details(default_shift, for_timestamp) + + # if its a holiday, reset + if shift_details and is_holiday_date(employee, shift_details): + shift_details = None + + # if no shift is found, find next or prev shift assignment based on direction + if not shift_details and next_shift_direction: + shift_details = get_prev_or_next_shift( + employee, for_timestamp, consider_default_shift, default_shift, next_shift_direction + ) + + return shift_details or {} + + +def get_prev_or_next_shift( + employee: str, + for_timestamp: datetime, + consider_default_shift: bool, + default_shift: str, + next_shift_direction: str, +) -> Dict: + """Returns a dict of shift details for the next or prev shift based on the next_shift_direction""" + MAX_DAYS = 366 + shift_details = {} + + if consider_default_shift and default_shift: + direction = -1 if next_shift_direction == "reverse" else 1 + for i in range(MAX_DAYS): + date = for_timestamp + timedelta(days=direction * (i + 1)) + shift_details = get_employee_shift(employee, date, consider_default_shift, None) + if shift_details: + break + else: + direction = "<" if next_shift_direction == "reverse" else ">" + sort_order = "desc" if next_shift_direction == "reverse" else "asc" + dates = frappe.db.get_all( + "Shift Assignment", + ["start_date", "end_date"], + { + "employee": employee, + "start_date": (direction, for_timestamp.date()), + "docstatus": 1, + "status": "Active", + }, + as_list=True, + limit=MAX_DAYS, + order_by="start_date " + sort_order, + ) + + if dates: + for date in dates: + if date[1] and date[1] < for_timestamp.date(): + continue + shift_details = get_employee_shift( + employee, datetime.combine(date[0], for_timestamp.time()), consider_default_shift, None + ) + if shift_details: + break + + return shift_details or {} + + +def is_holiday_date(employee: str, shift_details: Dict) -> bool: + holiday_list_name = frappe.db.get_value( + "Shift Type", shift_details.shift_type.name, "holiday_list" + ) + + if not holiday_list_name: + holiday_list_name = get_holiday_list_for_employee(employee, False) + + return holiday_list_name and is_holiday(holiday_list_name, shift_details.start_datetime.date()) + + +def get_employee_shift_timings( + employee: str, for_timestamp: datetime = None, consider_default_shift: bool = False +) -> List[Dict]: + """Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee""" + if for_timestamp is None: + for_timestamp = now_datetime() + + # write and verify a test case for midnight shift. + prev_shift = curr_shift = next_shift = None + curr_shift = get_employee_shift(employee, for_timestamp, consider_default_shift, "forward") + if curr_shift: + next_shift = get_employee_shift( + employee, curr_shift.start_datetime + timedelta(days=1), consider_default_shift, "forward" + ) + prev_shift = get_employee_shift( + employee, for_timestamp + timedelta(days=-1), consider_default_shift, "reverse" + ) + + if curr_shift: + # adjust actual start and end times if they are overlapping with grace period (before start and after end) + if prev_shift: + curr_shift.actual_start = ( + prev_shift.end_datetime + if curr_shift.actual_start < prev_shift.end_datetime + else curr_shift.actual_start + ) + prev_shift.actual_end = ( + curr_shift.actual_start + if prev_shift.actual_end > curr_shift.actual_start + else prev_shift.actual_end + ) + if next_shift: + next_shift.actual_start = ( + curr_shift.end_datetime + if next_shift.actual_start < curr_shift.end_datetime + else next_shift.actual_start + ) + curr_shift.actual_end = ( + next_shift.actual_start + if curr_shift.actual_end > next_shift.actual_start + else curr_shift.actual_end + ) + + return prev_shift, curr_shift, next_shift + + +def get_actual_start_end_datetime_of_shift( + employee: str, for_timestamp: datetime, consider_default_shift: bool = False +) -> Dict: + """Returns a Dict containing shift details with actual_start and actual_end datetime values + Here 'actual' means taking into account the "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time". + Empty Dict is returned if the timestamp is outside any actual shift timings. + + :param employee (str): Employee name + :param for_timestamp (datetime, optional): Datetime value of checkin, if not provided considers current datetime + :param consider_default_shift (bool, optional): Flag (defaults to False) to specify whether to consider + default shift in employee master if no shift assignment is found + """ + shift_timings_as_per_timestamp = get_employee_shift_timings( + employee, for_timestamp, consider_default_shift + ) + return get_exact_shift(shift_timings_as_per_timestamp, for_timestamp) + + +def get_exact_shift(shifts: List, for_timestamp: datetime) -> Dict: + """Returns the shift details (dict) for the exact shift in which the 'for_timestamp' value falls among multiple shifts""" + shift_details = dict() + timestamp_list = [] + + for shift in shifts: + if shift: + timestamp_list.extend([shift.actual_start, shift.actual_end]) + else: + timestamp_list.extend([None, None]) + + timestamp_index = None + for index, timestamp in enumerate(timestamp_list): + if not timestamp: + continue + + if for_timestamp < timestamp: + timestamp_index = index + elif for_timestamp == timestamp: + # on timestamp boundary + if index % 2 == 1: + timestamp_index = index + else: + timestamp_index = index + 1 + + if timestamp_index: + break + + if timestamp_index and timestamp_index % 2 == 1: + shift_details = shifts[int((timestamp_index - 1) / 2)] + + return shift_details + + +def get_shift_details(shift_type_name: str, for_timestamp: datetime = None) -> Dict: + """Returns a Dict containing shift details with the following data: + 'shift_type' - Object of DocType Shift Type, + 'start_datetime' - datetime of shift start on given timestamp, + 'end_datetime' - datetime of shift end on given timestamp, + 'actual_start' - datetime of shift start after adding 'begin_check_in_before_shift_start_time', + 'actual_end' - datetime of shift end after adding 'allow_check_out_after_shift_end_time' (None is returned if this is zero) + + :param shift_type_name (str): shift type name for which shift_details are required. + :param for_timestamp (datetime, optional): Datetime value of checkin, if not provided considers current datetime + """ + if not shift_type_name: + return {} + + if for_timestamp is None: + for_timestamp = now_datetime() + + shift_type = frappe.get_doc("Shift Type", shift_type_name) + shift_actual_start = shift_type.start_time - timedelta( + minutes=shift_type.begin_check_in_before_shift_start_time + ) + + if shift_type.start_time > shift_type.end_time: + # shift spans accross 2 different days + if get_time(for_timestamp.time()) >= get_time(shift_actual_start): + # if for_timestamp is greater than start time, it's within the first day + start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time + for_timestamp = for_timestamp + timedelta(days=1) + end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time + + elif get_time(for_timestamp.time()) < get_time(shift_actual_start): + # if for_timestamp is less than start time, it's within the second day + end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time + for_timestamp = for_timestamp + timedelta(days=-1) + start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time + else: + # start and end timings fall on the same day + start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time + end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time + + actual_start = start_datetime - timedelta( + minutes=shift_type.begin_check_in_before_shift_start_time + ) + actual_end = end_datetime + timedelta(minutes=shift_type.allow_check_out_after_shift_end_time) + + return frappe._dict( + { + "shift_type": shift_type, + "start_datetime": start_datetime, + "end_datetime": end_datetime, + "actual_start": actual_start, + "actual_end": actual_end, + } + ) diff --git a/hrms/hr/doctype/shift_assignment/shift_assignment_calendar.js b/hrms/hr/doctype/shift_assignment/shift_assignment_calendar.js new file mode 100644 index 0000000..7718594 --- /dev/null +++ b/hrms/hr/doctype/shift_assignment/shift_assignment_calendar.js @@ -0,0 +1,13 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.views.calendar["Shift Assignment"] = { + field_map: { + "start": "start_date", + "end": "end_date", + "id": "name", + "docstatus": 1, + "allDay": "allDay", + }, + get_events_method: "hrms.hr.doctype.shift_assignment.shift_assignment.get_events" +} diff --git a/hrms/hr/doctype/shift_assignment/test_shift_assignment.py b/hrms/hr/doctype/shift_assignment/test_shift_assignment.py new file mode 100644 index 0000000..3a4c840 --- /dev/null +++ b/hrms/hr/doctype/shift_assignment/test_shift_assignment.py @@ -0,0 +1,172 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, getdate, nowdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.shift_assignment.shift_assignment import OverlappingShiftError, get_events +from hrms.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type + +test_dependencies = ["Shift Type"] + + +class TestShiftAssignment(FrappeTestCase): + def setUp(self): + frappe.db.delete("Shift Assignment") + frappe.db.delete("Shift Type") + + def test_make_shift_assignment(self): + setup_shift_type(shift_type="Day Shift") + shift_assignment = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": "_T-Employee-00001", + "start_date": nowdate(), + } + ).insert() + shift_assignment.submit() + + self.assertEqual(shift_assignment.docstatus, 1) + + def test_overlapping_for_ongoing_shift(self): + # shift should be Ongoing if Only start_date is present and status = Active + setup_shift_type(shift_type="Day Shift") + shift_assignment_1 = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": "_T-Employee-00001", + "start_date": nowdate(), + "status": "Active", + } + ).insert() + shift_assignment_1.submit() + + self.assertEqual(shift_assignment_1.docstatus, 1) + + shift_assignment = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": "_T-Employee-00001", + "start_date": add_days(nowdate(), 2), + } + ) + + self.assertRaises(OverlappingShiftError, shift_assignment.save) + + def test_overlapping_for_fixed_period_shift(self): + # shift should is for Fixed period if Only start_date and end_date both are present and status = Active + setup_shift_type(shift_type="Day Shift") + shift_assignment_1 = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": "_T-Employee-00001", + "start_date": nowdate(), + "end_date": add_days(nowdate(), 30), + "status": "Active", + } + ).insert() + shift_assignment_1.submit() + + # it should not allowed within period of any shift. + shift_assignment_3 = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": "_T-Employee-00001", + "start_date": add_days(nowdate(), 10), + "end_date": add_days(nowdate(), 35), + "status": "Active", + } + ) + + self.assertRaises(OverlappingShiftError, shift_assignment_3.save) + + def test_overlapping_for_a_fixed_period_shift_and_ongoing_shift(self): + employee = make_employee("test_shift_assignment@example.com", company="_Test Company") + + # shift setup for 8-12 + shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") + date = getdate() + # shift with end date + make_shift_assignment(shift_type.name, employee, date, add_days(date, 30)) + + # shift setup for 11-15 + shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00") + date = getdate() + + # shift assignment without end date + shift2 = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": shift_type.name, + "company": "_Test Company", + "employee": employee, + "start_date": date, + } + ) + self.assertRaises(OverlappingShiftError, shift2.insert) + + def test_overlap_validation_for_shifts_on_same_day_with_overlapping_timeslots(self): + employee = make_employee("test_shift_assignment@example.com", company="_Test Company") + + # shift setup for 8-12 + shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + # shift setup for 11-15 + shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00") + date = getdate() + + shift2 = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": shift_type.name, + "company": "_Test Company", + "employee": employee, + "start_date": date, + } + ) + self.assertRaises(OverlappingShiftError, shift2.insert) + + def test_multiple_shift_assignments_for_same_day(self): + employee = make_employee("test_shift_assignment@example.com", company="_Test Company") + + # shift setup for 8-12 + shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + # shift setup for 13-15 + shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00") + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + def test_shift_assignment_calendar(self): + employee1 = make_employee("test_shift_assignment1@example.com", company="_Test Company") + employee2 = make_employee("test_shift_assignment2@example.com", company="_Test Company") + + shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") + date = getdate() + shift1 = make_shift_assignment(shift_type.name, employee1, date) + make_shift_assignment(shift_type.name, employee2, date) + + events = get_events( + start=date, end=date, filters=[["Shift Assignment", "employee", "=", employee1, False]] + ) + self.assertEqual(len(events), 1) + self.assertEqual(events[0]["name"], shift1.name) diff --git a/hrms/hr/doctype/shift_request/__init__.py b/hrms/hr/doctype/shift_request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/shift_request/shift_request.js b/hrms/hr/doctype/shift_request/shift_request.js new file mode 100644 index 0000000..5f93960 --- /dev/null +++ b/hrms/hr/doctype/shift_request/shift_request.js @@ -0,0 +1,17 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Shift Request', { + setup: function(frm) { + frm.set_query("approver", function() { + return { + query: "hrms.hr.doctype.department_approver.department_approver.get_approvers", + filters: { + employee: frm.doc.employee, + doctype: frm.doc.doctype + } + }; + }); + frm.set_query("employee", erpnext.queries.employee); + }, +}); diff --git a/hrms/hr/doctype/shift_request/shift_request.json b/hrms/hr/doctype/shift_request/shift_request.json new file mode 100644 index 0000000..64cbdff --- /dev/null +++ b/hrms/hr/doctype/shift_request/shift_request.json @@ -0,0 +1,155 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-SHR-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:32:27.974273", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "shift_type", + "employee", + "employee_name", + "department", + "status", + "column_break_4", + "company", + "approver", + "from_date", + "to_date", + "amended_from" + ], + "fields": [ + { + "fieldname": "shift_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Shift Type", + "options": "Shift Type", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Shift Request", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Draft\nApproved\nRejected", + "reqd": 1 + }, + { + "fetch_from": "employee.shift_request_approver", + "fetch_if_empty": 1, + "fieldname": "approver", + "fieldtype": "Link", + "label": "Approver", + "options": "User", + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-08-10 17:59:31.550558", + "modified_by": "Administrator", + "module": "HR", + "name": "Shift Request", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/shift_request/shift_request.py b/hrms/hr/doctype/shift_request/shift_request.py new file mode 100644 index 0000000..79cafe4 --- /dev/null +++ b/hrms/hr/doctype/shift_request/shift_request.py @@ -0,0 +1,140 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.query_builder import Criterion +from frappe.utils import get_link_to_form, getdate + +from hrms.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings +from hrms.hr.utils import share_doc_with_approver, validate_active_employee + + +class OverlappingShiftRequestError(frappe.ValidationError): + pass + + +class ShiftRequest(Document): + def validate(self): + validate_active_employee(self.employee) + self.validate_dates() + self.validate_overlapping_shift_requests() + self.validate_approver() + self.validate_default_shift() + + def on_update(self): + share_doc_with_approver(self, self.approver) + + def on_submit(self): + if self.status not in ["Approved", "Rejected"]: + frappe.throw(_("Only Shift Request with status 'Approved' and 'Rejected' can be submitted")) + if self.status == "Approved": + assignment_doc = frappe.new_doc("Shift Assignment") + assignment_doc.company = self.company + assignment_doc.shift_type = self.shift_type + assignment_doc.employee = self.employee + assignment_doc.start_date = self.from_date + if self.to_date: + assignment_doc.end_date = self.to_date + assignment_doc.shift_request = self.name + assignment_doc.flags.ignore_permissions = 1 + assignment_doc.insert() + assignment_doc.submit() + + frappe.msgprint( + _("Shift Assignment: {0} created for Employee: {1}").format( + frappe.bold(assignment_doc.name), frappe.bold(self.employee) + ) + ) + + def on_cancel(self): + shift_assignment_list = frappe.get_list( + "Shift Assignment", {"employee": self.employee, "shift_request": self.name} + ) + if shift_assignment_list: + for shift in shift_assignment_list: + shift_assignment_doc = frappe.get_doc("Shift Assignment", shift["name"]) + shift_assignment_doc.cancel() + + def validate_default_shift(self): + default_shift = frappe.get_value("Employee", self.employee, "default_shift") + if self.shift_type == default_shift: + frappe.throw( + _("You can not request for your Default Shift: {0}").format(frappe.bold(self.shift_type)) + ) + + def validate_approver(self): + department = frappe.get_value("Employee", self.employee, "department") + shift_approver = frappe.get_value("Employee", self.employee, "shift_request_approver") + approvers = frappe.db.sql( + """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", + (department), + ) + approvers = [approver[0] for approver in approvers] + approvers.append(shift_approver) + if self.approver not in approvers: + frappe.throw(_("Only Approvers can Approve this Request.")) + + def validate_dates(self): + if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): + frappe.throw(_("To date cannot be before from date")) + + def validate_overlapping_shift_requests(self): + overlapping_dates = self.get_overlapping_dates() + if len(overlapping_dates): + # if dates are overlapping, check if timings are overlapping, else allow + overlapping_timings = has_overlapping_timings(self.shift_type, overlapping_dates[0].shift_type) + if overlapping_timings: + self.throw_overlap_error(overlapping_dates[0]) + + def get_overlapping_dates(self): + if not self.name: + self.name = "New Shift Request" + + shift = frappe.qb.DocType("Shift Request") + query = ( + frappe.qb.from_(shift) + .select(shift.name, shift.shift_type) + .where((shift.employee == self.employee) & (shift.docstatus < 2) & (shift.name != self.name)) + ) + + if self.to_date: + query = query.where( + Criterion.any( + [ + Criterion.any( + [ + shift.to_date.isnull(), + ((self.from_date >= shift.from_date) & (self.from_date <= shift.to_date)), + ] + ), + Criterion.any( + [ + ((self.to_date >= shift.from_date) & (self.to_date <= shift.to_date)), + shift.from_date.between(self.from_date, self.to_date), + ] + ), + ] + ) + ) + else: + query = query.where( + shift.to_date.isnull() + | ((self.from_date >= shift.from_date) & (self.from_date <= shift.to_date)) + ) + + return query.run(as_dict=True) + + def throw_overlap_error(self, shift_details): + shift_details = frappe._dict(shift_details) + msg = _( + "Employee {0} has already applied for Shift {1}: {2} that overlaps within this period" + ).format( + frappe.bold(self.employee), + frappe.bold(shift_details.shift_type), + get_link_to_form("Shift Request", shift_details.name), + ) + + frappe.throw(msg, title=_("Overlapping Shift Requests"), exc=OverlappingShiftRequestError) diff --git a/hrms/hr/doctype/shift_request/shift_request_dashboard.py b/hrms/hr/doctype/shift_request/shift_request_dashboard.py new file mode 100644 index 0000000..2859b8f --- /dev/null +++ b/hrms/hr/doctype/shift_request/shift_request_dashboard.py @@ -0,0 +1,7 @@ +def get_data(): + return { + "fieldname": "shift_request", + "transactions": [ + {"items": ["Shift Assignment"]}, + ], + } diff --git a/hrms/hr/doctype/shift_request/test_shift_request.py b/hrms/hr/doctype/shift_request/test_shift_request.py new file mode 100644 index 0000000..9ae321d --- /dev/null +++ b/hrms/hr/doctype/shift_request/test_shift_request.py @@ -0,0 +1,259 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, nowdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.shift_request.shift_request import OverlappingShiftRequestError +from hrms.hr.doctype.shift_type.test_shift_type import setup_shift_type + +test_dependencies = ["Shift Type"] + + +class TestShiftRequest(FrappeTestCase): + def setUp(self): + for doctype in ["Shift Request", "Shift Assignment", "Shift Type"]: + frappe.db.delete(doctype) + + def test_make_shift_request(self): + "Test creation/updation of Shift Assignment from Shift Request." + setup_shift_type(shift_type="Day Shift") + department = frappe.get_value("Employee", "_T-Employee-00001", "department") + set_shift_approver(department) + approver = frappe.db.sql( + """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", + (department), + )[0][0] + + shift_request = make_shift_request(approver) + + # Only one shift assignment is created against a shift request + shift_assignment = frappe.db.get_value( + "Shift Assignment", + filters={"shift_request": shift_request.name}, + fieldname=["employee", "docstatus"], + as_dict=True, + ) + self.assertEqual(shift_request.employee, shift_assignment.employee) + self.assertEqual(shift_assignment.docstatus, 1) + + shift_request.cancel() + + shift_assignment_docstatus = frappe.db.get_value( + "Shift Assignment", filters={"shift_request": shift_request.name}, fieldname="docstatus" + ) + self.assertEqual(shift_assignment_docstatus, 2) + + def test_shift_request_approver_perms(self): + setup_shift_type(shift_type="Day Shift") + employee = frappe.get_doc("Employee", "_T-Employee-00001") + user = "test_approver_perm_emp@example.com" + make_employee(user, "_Test Company") + + # set approver for employee + employee.reload() + employee.shift_request_approver = user + employee.save() + + shift_request = make_shift_request(user, do_not_submit=True) + self.assertTrue(shift_request.name in frappe.share.get_shared("Shift Request", user)) + + # check shared doc revoked + shift_request.reload() + department = frappe.get_value("Employee", "_T-Employee-00001", "department") + set_shift_approver(department) + department_approver = frappe.db.sql( + """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", + (department), + )[0][0] + shift_request.approver = department_approver + shift_request.save() + self.assertTrue(shift_request.name not in frappe.share.get_shared("Shift Request", user)) + + shift_request.reload() + shift_request.approver = user + shift_request.save() + + frappe.set_user(user) + shift_request.reload() + shift_request.status = "Approved" + shift_request.submit() + + # unset approver + frappe.set_user("Administrator") + employee.reload() + employee.shift_request_approver = "" + employee.save() + + def test_overlap_for_request_without_to_date(self): + # shift should be Ongoing if Only from_date is present + user = "test_shift_request@example.com" + employee = make_employee(user, company="_Test Company", shift_request_approver=user) + setup_shift_type(shift_type="Day Shift") + + shift_request = frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": employee, + "from_date": nowdate(), + "approver": user, + "status": "Approved", + } + ).submit() + + shift_request = frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": employee, + "from_date": add_days(nowdate(), 2), + "approver": user, + "status": "Approved", + } + ) + + self.assertRaises(OverlappingShiftRequestError, shift_request.save) + + def test_overlap_for_request_with_from_and_to_dates(self): + user = "test_shift_request@example.com" + employee = make_employee(user, company="_Test Company", shift_request_approver=user) + setup_shift_type(shift_type="Day Shift") + + shift_request = frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": employee, + "from_date": nowdate(), + "to_date": add_days(nowdate(), 30), + "approver": user, + "status": "Approved", + } + ).submit() + + shift_request = frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": employee, + "from_date": add_days(nowdate(), 10), + "to_date": add_days(nowdate(), 35), + "approver": user, + "status": "Approved", + } + ) + + self.assertRaises(OverlappingShiftRequestError, shift_request.save) + + def test_overlapping_for_a_fixed_period_shift_and_ongoing_shift(self): + user = "test_shift_request@example.com" + employee = make_employee(user, company="_Test Company", shift_request_approver=user) + + # shift setup for 8-12 + shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") + date = nowdate() + + # shift with end date + frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": shift_type.name, + "company": "_Test Company", + "employee": employee, + "from_date": date, + "to_date": add_days(date, 30), + "approver": user, + "status": "Approved", + } + ).submit() + + # shift setup for 11-15 + shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00") + shift2 = frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": shift_type.name, + "company": "_Test Company", + "employee": employee, + "from_date": date, + "approver": user, + "status": "Approved", + } + ) + + self.assertRaises(OverlappingShiftRequestError, shift2.insert) + + def test_allow_non_overlapping_shift_requests_for_same_day(self): + user = "test_shift_request@example.com" + employee = make_employee(user, company="_Test Company", shift_request_approver=user) + + # shift setup for 8-12 + shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") + date = nowdate() + + # shift with end date + frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": shift_type.name, + "company": "_Test Company", + "employee": employee, + "from_date": date, + "to_date": add_days(date, 30), + "approver": user, + "status": "Approved", + } + ).submit() + + # shift setup for 13-15 + shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00") + frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": shift_type.name, + "company": "_Test Company", + "employee": employee, + "from_date": date, + "approver": user, + "status": "Approved", + } + ).submit() + + +def set_shift_approver(department): + department_doc = frappe.get_doc("Department", department) + department_doc.append("shift_request_approver", {"approver": "test1@example.com"}) + department_doc.save() + department_doc.reload() + + +def make_shift_request(approver, do_not_submit=0): + shift_request = frappe.get_doc( + { + "doctype": "Shift Request", + "shift_type": "Day Shift", + "company": "_Test Company", + "employee": "_T-Employee-00001", + "employee_name": "_Test Employee", + "from_date": nowdate(), + "to_date": add_days(nowdate(), 10), + "approver": approver, + "status": "Approved", + } + ).insert() + + if do_not_submit: + return shift_request + + shift_request.submit() + return shift_request diff --git a/hrms/hr/doctype/shift_type/__init__.py b/hrms/hr/doctype/shift_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/shift_type/shift_type.js b/hrms/hr/doctype/shift_type/shift_type.js new file mode 100644 index 0000000..7138e3b --- /dev/null +++ b/hrms/hr/doctype/shift_type/shift_type.js @@ -0,0 +1,35 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Shift Type', { + refresh: function(frm) { + frm.add_custom_button( + __('Mark Attendance'), + () => { + if (!frm.doc.enable_auto_attendance) { + frm.scroll_to_field('enable_auto_attendance'); + frappe.throw(__('Please Enable Auto Attendance and complete the setup first.')); + } + + if (!frm.doc.process_attendance_after) { + frm.scroll_to_field('process_attendance_after'); + frappe.throw(__('Please set {0}.', [__('Process Attendance After').bold()])); + } + + if (!frm.doc.last_sync_of_checkin) { + frm.scroll_to_field('last_sync_of_checkin'); + frappe.throw(__('Please set {0}.', [__('Last Sync of Checkin').bold()])); + } + + frm.call({ + doc: frm.doc, + method: 'process_auto_attendance', + freeze: true, + callback: () => { + frappe.msgprint(__('Attendance has been marked as per employee check-ins')); + } + }); + } + ); + } +}); diff --git a/hrms/hr/doctype/shift_type/shift_type.json b/hrms/hr/doctype/shift_type/shift_type.json new file mode 100644 index 0000000..61f3d2c --- /dev/null +++ b/hrms/hr/doctype/shift_type/shift_type.json @@ -0,0 +1,204 @@ +{ + "autoname": "prompt", + "creation": "2018-04-13 16:22:52.954783", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "start_time", + "end_time", + "column_break_3", + "holiday_list", + "enable_auto_attendance", + "auto_attendance_settings_section", + "determine_check_in_and_check_out", + "working_hours_calculation_based_on", + "begin_check_in_before_shift_start_time", + "allow_check_out_after_shift_end_time", + "column_break_10", + "working_hours_threshold_for_half_day", + "working_hours_threshold_for_absent", + "process_attendance_after", + "last_sync_of_checkin", + "grace_period_settings_auto_attendance_section", + "enable_entry_grace_period", + "late_entry_grace_period", + "column_break_18", + "enable_exit_grace_period", + "early_exit_grace_period" + ], + "fields": [ + { + "fieldname": "start_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "Start Time", + "reqd": 1 + }, + { + "fieldname": "end_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "End Time", + "reqd": 1 + }, + { + "fieldname": "holiday_list", + "fieldtype": "Link", + "label": "Holiday List", + "options": "Holiday List" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "determine_check_in_and_check_out", + "fieldtype": "Select", + "label": "Determine Check-in and Check-out", + "options": "Alternating entries as IN and OUT during the same shift\nStrictly based on Log Type in Employee Checkin" + }, + { + "fieldname": "working_hours_calculation_based_on", + "fieldtype": "Select", + "label": "Working Hours Calculation Based On", + "options": "First Check-in and Last Check-out\nEvery Valid Check-in and Check-out" + }, + { + "description": "Working hours below which Half Day is marked. (Zero to disable)", + "fieldname": "working_hours_threshold_for_half_day", + "fieldtype": "Float", + "label": "Working Hours Threshold for Half Day", + "precision": "1" + }, + { + "description": "Working hours below which Absent is marked. (Zero to disable)", + "fieldname": "working_hours_threshold_for_absent", + "fieldtype": "Float", + "label": "Working Hours Threshold for Absent", + "precision": "1" + }, + { + "default": "60", + "description": "The time before the shift start time during which Employee Check-in is considered for attendance.", + "fieldname": "begin_check_in_before_shift_start_time", + "fieldtype": "Int", + "label": "Begin check-in before shift start time (in minutes)" + }, + { + "default": "0", + "fieldname": "enable_entry_grace_period", + "fieldtype": "Check", + "label": "Enable Entry Grace Period" + }, + { + "depends_on": "enable_entry_grace_period", + "description": "The time after the shift start time when check-in is considered as late (in minutes).", + "fieldname": "late_entry_grace_period", + "fieldtype": "Int", + "label": "Late Entry Grace Period" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "enable_exit_grace_period", + "fieldtype": "Check", + "label": "Enable Exit Grace Period" + }, + { + "depends_on": "eval:doc.enable_exit_grace_period", + "description": "The time before the shift end time when check-out is considered as early (in minutes).", + "fieldname": "early_exit_grace_period", + "fieldtype": "Int", + "label": "Early Exit Grace Period" + }, + { + "default": "60", + "description": "Time after the end of shift during which check-out is considered for attendance.", + "fieldname": "allow_check_out_after_shift_end_time", + "fieldtype": "Int", + "label": "Allow check-out after shift end time (in minutes)" + }, + { + "depends_on": "enable_auto_attendance", + "fieldname": "auto_attendance_settings_section", + "fieldtype": "Section Break", + "label": "Auto Attendance Settings" + }, + { + "depends_on": "enable_auto_attendance", + "fieldname": "grace_period_settings_auto_attendance_section", + "fieldtype": "Section Break", + "label": "Grace Period Settings For Auto Attendance" + }, + { + "default": "0", + "description": "Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.", + "fieldname": "enable_auto_attendance", + "fieldtype": "Check", + "label": "Enable Auto Attendance" + }, + { + "description": "Attendance will be marked automatically only after this date.", + "fieldname": "process_attendance_after", + "fieldtype": "Date", + "label": "Process Attendance After" + }, + { + "description": "Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.", + "fieldname": "last_sync_of_checkin", + "fieldtype": "Datetime", + "label": "Last Sync of Checkin" + } + ], + "modified": "2019-07-30 01:05:24.660666", + "modified_by": "Administrator", + "module": "HR", + "name": "Shift Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/shift_type/shift_type.py b/hrms/hr/doctype/shift_type/shift_type.py new file mode 100644 index 0000000..d572854 --- /dev/null +++ b/hrms/hr/doctype/shift_type/shift_type.py @@ -0,0 +1,200 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import itertools +from datetime import datetime, timedelta + +import frappe +from frappe.model.document import Document +from frappe.utils import cint, get_datetime, get_time, getdate + +from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange +from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee +from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday + +from hrms.hr.doctype.attendance.attendance import mark_attendance +from hrms.hr.doctype.employee_checkin.employee_checkin import ( + calculate_working_hours, + mark_attendance_and_link_log, +) +from hrms.hr.doctype.shift_assignment.shift_assignment import get_employee_shift, get_shift_details + + +class ShiftType(Document): + @frappe.whitelist() + def process_auto_attendance(self): + if ( + not cint(self.enable_auto_attendance) + or not self.process_attendance_after + or not self.last_sync_of_checkin + ): + return + + filters = { + "skip_auto_attendance": 0, + "attendance": ("is", "not set"), + "time": (">=", self.process_attendance_after), + "shift_actual_end": ("<", self.last_sync_of_checkin), + "shift": self.name, + } + logs = frappe.db.get_list( + "Employee Checkin", fields="*", filters=filters, order_by="employee,time" + ) + + for key, group in itertools.groupby( + logs, key=lambda x: (x["employee"], x["shift_actual_start"]) + ): + single_shift_logs = list(group) + ( + attendance_status, + working_hours, + late_entry, + early_exit, + in_time, + out_time, + ) = self.get_attendance(single_shift_logs) + + mark_attendance_and_link_log( + single_shift_logs, + attendance_status, + key[1].date(), + working_hours, + late_entry, + early_exit, + in_time, + out_time, + self.name, + ) + + for employee in self.get_assigned_employee(self.process_attendance_after, True): + self.mark_absent_for_dates_with_no_attendance(employee) + + def get_attendance(self, logs): + """Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time + for a set of logs belonging to a single shift. + Assumptions: + 1. These logs belongs to a single shift, single employee and it's not in a holiday date. + 2. Logs are in chronological order + """ + late_entry = early_exit = False + total_working_hours, in_time, out_time = calculate_working_hours( + logs, self.determine_check_in_and_check_out, self.working_hours_calculation_based_on + ) + if ( + cint(self.enable_entry_grace_period) + and in_time + and in_time > logs[0].shift_start + timedelta(minutes=cint(self.late_entry_grace_period)) + ): + late_entry = True + + if ( + cint(self.enable_exit_grace_period) + and out_time + and out_time < logs[0].shift_end - timedelta(minutes=cint(self.early_exit_grace_period)) + ): + early_exit = True + + if ( + self.working_hours_threshold_for_half_day + and total_working_hours < self.working_hours_threshold_for_half_day + ): + return "Half Day", total_working_hours, late_entry, early_exit, in_time, out_time + if ( + self.working_hours_threshold_for_absent + and total_working_hours < self.working_hours_threshold_for_absent + ): + return "Absent", total_working_hours, late_entry, early_exit, in_time, out_time + return "Present", total_working_hours, late_entry, early_exit, in_time, out_time + + def mark_absent_for_dates_with_no_attendance(self, employee): + """Marks Absents for the given employee on working days in this shift which have no attendance marked. + The Absent is marked starting from 'process_attendance_after' or employee creation date. + """ + start_date, end_date = self.get_start_and_end_dates(employee) + + # no shift assignment found, no need to process absent attendance records + if start_date is None: + return + + holiday_list_name = self.holiday_list + if not holiday_list_name: + holiday_list_name = get_holiday_list_for_employee(employee, False) + + start_time = get_time(self.start_time) + + for date in daterange(getdate(start_date), getdate(end_date)): + if is_holiday(holiday_list_name, date): + # skip marking absent on a holiday + continue + + timestamp = datetime.combine(date, start_time) + shift_details = get_employee_shift(employee, timestamp, True) + + if shift_details and shift_details.shift_type.name == self.name: + attendance = mark_attendance(employee, date, "Absent", self.name) + if attendance: + frappe.get_doc( + { + "doctype": "Comment", + "comment_type": "Comment", + "reference_doctype": "Attendance", + "reference_name": attendance, + "content": frappe._("Employee was marked Absent due to missing Employee Checkins."), + } + ).insert(ignore_permissions=True) + + def get_start_and_end_dates(self, employee): + """Returns start and end dates for checking attendance and marking absent + return: start date = max of `process_attendance_after` and DOJ + return: end date = min of shift before `last_sync_of_checkin` and Relieving Date + """ + date_of_joining, relieving_date, employee_creation = frappe.db.get_value( + "Employee", employee, ["date_of_joining", "relieving_date", "creation"] + ) + + if not date_of_joining: + date_of_joining = employee_creation.date() + + start_date = max(getdate(self.process_attendance_after), date_of_joining) + end_date = None + + shift_details = get_shift_details(self.name, get_datetime(self.last_sync_of_checkin)) + last_shift_time = ( + shift_details.actual_start if shift_details else get_datetime(self.last_sync_of_checkin) + ) + + # check if shift is found for 1 day before the last sync of checkin + # absentees are auto-marked 1 day after the shift to wait for any manual attendance records + prev_shift = get_employee_shift(employee, last_shift_time - timedelta(days=1), True, "reverse") + if prev_shift: + end_date = ( + min(prev_shift.start_datetime.date(), relieving_date) + if relieving_date + else prev_shift.start_datetime.date() + ) + else: + # no shift found + return None, None + return start_date, end_date + + def get_assigned_employee(self, from_date=None, consider_default_shift=False): + filters = {"shift_type": self.name, "docstatus": "1"} + if from_date: + filters["start_date"] = (">", from_date) + + assigned_employees = frappe.get_all("Shift Assignment", filters=filters, pluck="employee") + + if consider_default_shift: + filters = {"default_shift": self.name, "status": ["!=", "Inactive"]} + default_shift_employees = frappe.get_all("Employee", filters=filters, pluck="name") + + return list(set(assigned_employees + default_shift_employees)) + return assigned_employees + + +def process_auto_attendance_for_all_shifts(): + shift_list = frappe.get_all("Shift Type", "name", {"enable_auto_attendance": "1"}, as_list=True) + for shift in shift_list: + doc = frappe.get_doc("Shift Type", shift[0]) + doc.process_auto_attendance() diff --git a/hrms/hr/doctype/shift_type/shift_type_dashboard.py b/hrms/hr/doctype/shift_type/shift_type_dashboard.py new file mode 100644 index 0000000..920d8fd --- /dev/null +++ b/hrms/hr/doctype/shift_type/shift_type_dashboard.py @@ -0,0 +1,8 @@ +def get_data(): + return { + "fieldname": "shift", + "non_standard_fieldnames": {"Shift Request": "shift_type", "Shift Assignment": "shift_type"}, + "transactions": [ + {"items": ["Attendance", "Employee Checkin", "Shift Request", "Shift Assignment"]} + ], + } diff --git a/hrms/hr/doctype/shift_type/test_records.json b/hrms/hr/doctype/shift_type/test_records.json new file mode 100644 index 0000000..9040b91 --- /dev/null +++ b/hrms/hr/doctype/shift_type/test_records.json @@ -0,0 +1,8 @@ +[ + { + "doctype": "Shift Type", + "name": "Day Shift", + "start_time": "9:00:00", + "end_time": "18:00:00" + } +] diff --git a/hrms/hr/doctype/shift_type/test_shift_type.py b/hrms/hr/doctype/shift_type/test_shift_type.py new file mode 100644 index 0000000..804fdfb --- /dev/null +++ b/hrms/hr/doctype/shift_type/test_shift_type.py @@ -0,0 +1,383 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest +from datetime import datetime, timedelta + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, get_time, get_year_ending, get_year_start, getdate, now_datetime + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.leave_application.test_leave_application import get_first_sunday +from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + +class TestShiftType(FrappeTestCase): + def setUp(self): + frappe.db.delete("Shift Type") + frappe.db.delete("Shift Assignment") + frappe.db.delete("Employee Checkin") + frappe.db.delete("Attendance") + + from_date = get_year_start(getdate()) + to_date = get_year_ending(getdate()) + self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) + + def test_mark_attendance(self): + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + + shift_type = setup_shift_type() + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("12:00:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", {"shift": shift_type.name}, ["status", "name"], as_dict=True + ) + self.assertEqual(attendance.status, "Present") + + def test_entry_and_exit_grace(self): + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + + # doesn't mark late entry until 60 mins after shift start i.e. till 9 + # doesn't mark late entry until 60 mins before shift end i.e. 11 + shift_type = setup_shift_type( + enable_entry_grace_period=1, + enable_exit_grace_period=1, + late_entry_grace_period=60, + early_exit_grace_period=60, + ) + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("09:30:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("10:30:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", + {"shift": shift_type.name}, + ["status", "name", "late_entry", "early_exit"], + as_dict=True, + ) + self.assertEqual(attendance.status, "Present") + self.assertEqual(attendance.late_entry, 1) + self.assertEqual(attendance.early_exit, 1) + + def test_working_hours_threshold_for_half_day(self): + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type(shift_type="Half Day Test", working_hours_threshold_for_half_day=2) + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("09:30:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True + ) + self.assertEqual(attendance.status, "Half Day") + self.assertEqual(attendance.working_hours, 1.5) + + def test_working_hours_threshold_for_absent(self): + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type(shift_type="Absent Test", working_hours_threshold_for_absent=2) + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("09:30:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True + ) + self.assertEqual(attendance.status, "Absent") + self.assertEqual(attendance.working_hours, 1.5) + + def test_working_hours_threshold_for_absent_and_half_day_1(self): + # considers half day over absent + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type( + shift_type="Half Day + Absent Test", + working_hours_threshold_for_half_day=1, + working_hours_threshold_for_absent=2, + ) + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("08:45:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True + ) + self.assertEqual(attendance.status, "Half Day") + self.assertEqual(attendance.working_hours, 0.75) + + def test_working_hours_threshold_for_absent_and_half_day_2(self): + # considers absent over half day + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type( + shift_type="Half Day + Absent Test", + working_hours_threshold_for_half_day=1, + working_hours_threshold_for_absent=2, + ) + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("09:30:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value("Attendance", {"shift": shift_type.name}, "status") + self.assertEqual(attendance, "Absent") + + def test_mark_absent_for_dates_with_no_attendance(self): + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type(shift_type="Test Absent with no Attendance") + + # absentees are auto-marked one day after to wait for any manual attendance records + date = add_days(getdate(), -1) + make_shift_assignment(shift_type.name, employee, date) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", {"attendance_date": date, "employee": employee}, "status" + ) + self.assertEqual(attendance, "Absent") + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_skip_marking_absent_on_a_holiday(self): + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_type = setup_shift_type(shift_type="Test Absent with no Attendance") + shift_type.holiday_list = None + shift_type.save() + + # should not mark any attendance if no shift assignment is created + shift_type.process_auto_attendance() + attendance = frappe.db.get_value("Attendance", {"employee": employee}, "status") + self.assertIsNone(attendance) + + first_sunday = get_first_sunday(self.holiday_list, for_date=getdate()) + make_shift_assignment(shift_type.name, employee, first_sunday) + + shift_type.process_auto_attendance() + + attendance = frappe.db.get_value( + "Attendance", {"attendance_date": first_sunday, "employee": employee}, "status" + ) + self.assertIsNone(attendance) + + def test_get_start_and_end_dates(self): + date = getdate() + + doj = add_days(date, -30) + relieving_date = add_days(date, -5) + employee = make_employee( + "test_employee_dates@example.com", + company="_Test Company", + date_of_joining=doj, + relieving_date=relieving_date, + ) + shift_type = setup_shift_type( + shift_type="Test Absent with no Attendance", process_attendance_after=add_days(doj, 2) + ) + + make_shift_assignment(shift_type.name, employee, add_days(date, -25)) + + shift_type.process_auto_attendance() + + # should not mark absent before shift assignment/process attendance after date + attendance = frappe.db.get_value( + "Attendance", {"attendance_date": doj, "employee": employee}, "name" + ) + self.assertIsNone(attendance) + + # mark absent on Relieving Date + attendance = frappe.db.get_value( + "Attendance", {"attendance_date": relieving_date, "employee": employee}, "status" + ) + self.assertEquals(attendance, "Absent") + + # should not mark absent after Relieving Date + attendance = frappe.db.get_value( + "Attendance", {"attendance_date": add_days(relieving_date, 1), "employee": employee}, "name" + ) + self.assertIsNone(attendance) + + def test_skip_auto_attendance_for_duplicate_record(self): + # Skip auto attendance in case of duplicate attendance record + from hrms.hr.doctype.attendance.attendance import mark_attendance + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + + shift_type = setup_shift_type() + date = getdate() + + # mark attendance + mark_attendance(employee, date, "Present") + make_shift_assignment(shift_type.name, employee, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_type.name) + + timestamp = datetime.combine(date, get_time("12:00:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_type.name) + + # auto attendance should skip marking + shift_type.process_auto_attendance() + + log_in.reload() + log_out.reload() + self.assertEqual(log_in.skip_auto_attendance, 1) + self.assertEqual(log_out.skip_auto_attendance, 1) + + def test_skip_auto_attendance_for_overlapping_shift(self): + # Skip auto attendance in case of overlapping shift attendance record + # this case won't occur in case of shift assignment, since it will not allow overlapping shifts to be assigned + # can happen if manual attendance records are created + from hrms.hr.doctype.attendance.attendance import mark_attendance + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") + shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00") + + date = getdate() + + # mark attendance + mark_attendance(employee, date, "Present", shift=shift_1.name) + make_shift_assignment(shift_2.name, employee, date) + + timestamp = datetime.combine(date, get_time("09:30:00")) + log_in = make_checkin(employee, timestamp) + self.assertEqual(log_in.shift, shift_2.name) + + timestamp = datetime.combine(date, get_time("11:00:00")) + log_out = make_checkin(employee, timestamp) + self.assertEqual(log_out.shift, shift_2.name) + + # auto attendance should be skipped for shift 2 + # since it is already marked for overlapping shift 1 + shift_2.process_auto_attendance() + + log_in.reload() + log_out.reload() + self.assertEqual(log_in.skip_auto_attendance, 1) + self.assertEqual(log_out.skip_auto_attendance, 1) + + +def setup_shift_type(**args): + args = frappe._dict(args) + date = getdate() + + shift_type = frappe.get_doc( + { + "doctype": "Shift Type", + "__newname": args.shift_type or "_Test Shift", + "start_time": "08:00:00", + "end_time": "12:00:00", + "enable_auto_attendance": 1, + "determine_check_in_and_check_out": "Alternating entries as IN and OUT during the same shift", + "working_hours_calculation_based_on": "First Check-in and Last Check-out", + "begin_check_in_before_shift_start_time": 60, + "allow_check_out_after_shift_end_time": 60, + "process_attendance_after": add_days(date, -2), + "last_sync_of_checkin": now_datetime() + timedelta(days=1), + } + ) + + holiday_list = "Employee Checkin Test Holiday List" + if not frappe.db.exists("Holiday List", "Employee Checkin Test Holiday List"): + holiday_list = frappe.get_doc( + { + "doctype": "Holiday List", + "holiday_list_name": "Employee Checkin Test Holiday List", + "from_date": get_year_start(date), + "to_date": get_year_ending(date), + } + ).insert() + holiday_list = holiday_list.name + + shift_type.holiday_list = holiday_list + shift_type.update(args) + shift_type.save() + + return shift_type + + +def make_shift_assignment(shift_type, employee, start_date, end_date=None): + shift_assignment = frappe.get_doc( + { + "doctype": "Shift Assignment", + "shift_type": shift_type, + "company": "_Test Company", + "employee": employee, + "start_date": start_date, + "end_date": end_date, + } + ).insert() + shift_assignment.submit() + + return shift_assignment diff --git a/hrms/hr/doctype/skill/__init__.py b/hrms/hr/doctype/skill/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/skill/skill.js b/hrms/hr/doctype/skill/skill.js new file mode 100644 index 0000000..a939ff0 --- /dev/null +++ b/hrms/hr/doctype/skill/skill.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Skill', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/hr/doctype/skill/skill.json b/hrms/hr/doctype/skill/skill.json new file mode 100644 index 0000000..b635826 --- /dev/null +++ b/hrms/hr/doctype/skill/skill.json @@ -0,0 +1,119 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "autoname": "field:skill_name", + "beta": 0, + "creation": "2019-04-16 09:54:39.486915", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 1, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "skill_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Skill Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_in_quick_entry": 1, + "fieldname": "description", + "fieldtype": "Text", + "label": "Description" + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2022-02-26 10:55:00.536328", + "modified_by": "Administrator", + "module": "HR", + "name": "Skill", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} diff --git a/hrms/hr/doctype/skill/skill.py b/hrms/hr/doctype/skill/skill.py new file mode 100644 index 0000000..d26e7ca --- /dev/null +++ b/hrms/hr/doctype/skill/skill.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class Skill(Document): + pass diff --git a/hrms/hr/doctype/skill_assessment/__init__.py b/hrms/hr/doctype/skill_assessment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/skill_assessment/skill_assessment.json b/hrms/hr/doctype/skill_assessment/skill_assessment.json new file mode 100644 index 0000000..8b935c4 --- /dev/null +++ b/hrms/hr/doctype/skill_assessment/skill_assessment.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "creation": "2021-04-12 17:07:39.656289", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "skill", + "rating" + ], + "fields": [ + { + "fieldname": "skill", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Skill", + "options": "Skill", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "rating", + "fieldtype": "Rating", + "in_list_view": 1, + "label": "Rating", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-12 17:18:14.032298", + "modified_by": "Administrator", + "module": "HR", + "name": "Skill Assessment", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/skill_assessment/skill_assessment.py b/hrms/hr/doctype/skill_assessment/skill_assessment.py new file mode 100644 index 0000000..13775be --- /dev/null +++ b/hrms/hr/doctype/skill_assessment/skill_assessment.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class SkillAssessment(Document): + pass diff --git a/hrms/hr/doctype/staffing_plan/__init__.py b/hrms/hr/doctype/staffing_plan/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/staffing_plan/staffing_plan.js b/hrms/hr/doctype/staffing_plan/staffing_plan.js new file mode 100644 index 0000000..7cd4537 --- /dev/null +++ b/hrms/hr/doctype/staffing_plan/staffing_plan.js @@ -0,0 +1,106 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Staffing Plan', { + setup: function(frm) { + frm.set_query("designation", "staffing_details", function() { + let designations = []; + (frm.doc.staffing_details || []).forEach(function(staff_detail) { + if(staff_detail.designation){ + designations.push(staff_detail.designation) + } + }) + // Filter out designations already selected in Staffing Plan Detail + return { + filters: [ + ['Designation', 'name', 'not in', designations], + ] + } + }); + + frm.set_query("department", function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); + }, +}); + +frappe.ui.form.on('Staffing Plan Detail', { + designation: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if(frm.doc.company && child.designation) { + set_number_of_positions(frm, cdt, cdn); + } + }, + + vacancies: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if(child.vacancies < child.current_openings) { + frappe.throw(__("Vacancies cannot be lower than the current openings")); + } + set_number_of_positions(frm, cdt, cdn); + }, + + current_count: function(frm, cdt, cdn) { + set_number_of_positions(frm, cdt, cdn); + }, + + estimated_cost_per_position: function(frm, cdt, cdn) { + set_total_estimated_cost(frm, cdt, cdn); + } +}); + +var set_number_of_positions = function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (!child.designation) frappe.throw(__("Please enter the designation")); + frappe.call({ + "method": "hrms.hr.doctype.staffing_plan.staffing_plan.get_designation_counts", + args: { + designation: child.designation, + company: frm.doc.company + }, + callback: function (data) { + if(data.message){ + frappe.model.set_value(cdt, cdn, 'current_count', data.message.employee_count); + frappe.model.set_value(cdt, cdn, 'current_openings', data.message.job_openings); + let total_positions = cint(data.message.employee_count) + cint(child.vacancies); + if (cint(child.number_of_positions) < total_positions){ + frappe.model.set_value(cdt, cdn, 'number_of_positions', total_positions); + } + } + else{ // No employees for this designation + frappe.model.set_value(cdt, cdn, 'current_count', 0); + frappe.model.set_value(cdt, cdn, 'current_openings', 0); + } + } + }); + refresh_field("staffing_details"); + set_total_estimated_cost(frm, cdt, cdn); +} + +// Note: Estimated Cost is calculated on number of Vacancies +var set_total_estimated_cost = function(frm, cdt, cdn) { + let child = locals[cdt][cdn] + if(child.vacancies > 0 && child.estimated_cost_per_position) { + frappe.model.set_value(cdt, cdn, 'total_estimated_cost', child.vacancies * child.estimated_cost_per_position); + } + else { + frappe.model.set_value(cdt, cdn, 'total_estimated_cost', 0); + } + set_total_estimated_budget(frm); +}; + +var set_total_estimated_budget = function(frm) { + let estimated_budget = 0.0 + if(frm.doc.staffing_details) { + (frm.doc.staffing_details || []).forEach(function(staff_detail) { + if(staff_detail.total_estimated_cost){ + estimated_budget += staff_detail.total_estimated_cost + } + }) + frm.set_value('total_estimated_budget', estimated_budget); + } +}; diff --git a/hrms/hr/doctype/staffing_plan/staffing_plan.json b/hrms/hr/doctype/staffing_plan/staffing_plan.json new file mode 100644 index 0000000..9576bc3 --- /dev/null +++ b/hrms/hr/doctype/staffing_plan/staffing_plan.json @@ -0,0 +1,403 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "prompt", + "beta": 0, + "creation": "2018-04-13 18:07:21.582747", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "from_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "From Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "to_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "To Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "staffing_plan_details", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Staffing Plan Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "staffing_details", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Staffing Plan Detail", + "length": 0, + "no_copy": 0, + "options": "Staffing Plan Detail", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0.00", + "fieldname": "total_estimated_budget", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Total Estimated Budget", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Staffing Plan", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-05-28 18:30:27.041395", + "modified_by": "Administrator", + "module": "HR", + "name": "Staffing Plan", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/staffing_plan/staffing_plan.py b/hrms/hr/doctype/staffing_plan/staffing_plan.py new file mode 100644 index 0000000..82472de --- /dev/null +++ b/hrms/hr/doctype/staffing_plan/staffing_plan.py @@ -0,0 +1,222 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import cint, flt, getdate, nowdate +from frappe.utils.nestedset import get_descendants_of + + +class SubsidiaryCompanyError(frappe.ValidationError): + pass + + +class ParentCompanyError(frappe.ValidationError): + pass + + +class StaffingPlan(Document): + def validate(self): + self.validate_period() + self.validate_details() + self.set_total_estimated_budget() + + def validate_period(self): + # Validate Dates + if self.from_date and self.to_date and self.from_date > self.to_date: + frappe.throw(_("From Date cannot be greater than To Date")) + + def validate_details(self): + for detail in self.get("staffing_details"): + self.validate_overlap(detail) + self.validate_with_subsidiary_plans(detail) + self.validate_with_parent_plan(detail) + + def set_total_estimated_budget(self): + self.total_estimated_budget = 0 + + for detail in self.get("staffing_details"): + # Set readonly fields + self.set_number_of_positions(detail) + designation_counts = get_designation_counts(detail.designation, self.company) + detail.current_count = designation_counts["employee_count"] + detail.current_openings = designation_counts["job_openings"] + + detail.total_estimated_cost = 0 + if detail.number_of_positions > 0: + if detail.vacancies and detail.estimated_cost_per_position: + detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position) + + self.total_estimated_budget += detail.total_estimated_cost + + def set_number_of_positions(self, detail): + detail.number_of_positions = cint(detail.vacancies) + cint(detail.current_count) + + def validate_overlap(self, staffing_plan_detail): + # Validate if any submitted Staffing Plan exist for any Designations in this plan + # and spd.vacancies>0 ? + overlap = frappe.db.sql( + """select spd.parent + from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name + where spd.designation=%s and sp.docstatus=1 + and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s + """, + (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), + ) + if overlap and overlap[0][0]: + frappe.throw( + _("Staffing Plan {0} already exist for designation {1}").format( + overlap[0][0], staffing_plan_detail.designation + ) + ) + + def validate_with_parent_plan(self, staffing_plan_detail): + if not frappe.get_cached_value("Company", self.company, "parent_company"): + return # No parent, nothing to validate + + # Get staffing plan applicable for the company (Parent Company) + parent_plan_details = get_active_staffing_plan_details( + self.company, staffing_plan_detail.designation, self.from_date, self.to_date + ) + if not parent_plan_details: + return # no staffing plan for any parent Company in hierarchy + + # Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the hierarchy + parent_company = frappe.db.get_value("Staffing Plan", parent_plan_details[0].name, "company") + # Parent plan available, validate with parent, siblings as well as children of staffing plan Company + if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or flt( + staffing_plan_detail.total_estimated_cost + ) > flt(parent_plan_details[0].total_estimated_cost): + frappe.throw( + _( + "You can only plan for upto {0} vacancies and budget {1} for {2} as per staffing plan {3} for parent company {4}." + ).format( + cint(parent_plan_details[0].vacancies), + parent_plan_details[0].total_estimated_cost, + frappe.bold(staffing_plan_detail.designation), + parent_plan_details[0].name, + parent_company, + ), + ParentCompanyError, + ) + + # Get vacanices already planned for all companies down the hierarchy of Parent Company + lft, rgt = frappe.get_cached_value("Company", parent_company, ["lft", "rgt"]) + all_sibling_details = frappe.db.sql( + """select sum(spd.vacancies) as vacancies, + sum(spd.total_estimated_cost) as total_estimated_cost + from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name + where spd.designation=%s and sp.docstatus=1 + and sp.to_date >= %s and sp.from_date <=%s + and sp.company in (select name from tabCompany where lft > %s and rgt < %s) + """, + (staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt), + as_dict=1, + )[0] + + if ( + cint(parent_plan_details[0].vacancies) + < (cint(staffing_plan_detail.vacancies) + cint(all_sibling_details.vacancies)) + ) or ( + flt(parent_plan_details[0].total_estimated_cost) + < ( + flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost) + ) + ): + frappe.throw( + _( + "{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}." + ).format( + cint(all_sibling_details.vacancies), + all_sibling_details.total_estimated_cost, + frappe.bold(staffing_plan_detail.designation), + parent_company, + cint(parent_plan_details[0].vacancies), + parent_plan_details[0].total_estimated_cost, + parent_plan_details[0].name, + ) + ) + + def validate_with_subsidiary_plans(self, staffing_plan_detail): + # Valdate this plan with all child company plan + children_details = frappe.db.sql( + """select sum(spd.vacancies) as vacancies, + sum(spd.total_estimated_cost) as total_estimated_cost + from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name + where spd.designation=%s and sp.docstatus=1 + and sp.to_date >= %s and sp.from_date <=%s + and sp.company in (select name from tabCompany where parent_company = %s) + """, + (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), + as_dict=1, + )[0] + + if ( + children_details + and cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) + or flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost) + ): + frappe.throw( + _( + "Subsidiary companies have already planned for {1} vacancies at a budget of {2}. Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies" + ).format( + self.company, + cint(children_details.vacancies), + children_details.total_estimated_cost, + frappe.bold(staffing_plan_detail.designation), + ), + SubsidiaryCompanyError, + ) + + +@frappe.whitelist() +def get_designation_counts(designation, company, job_opening=None): + if not designation: + return False + + company_set = get_descendants_of("Company", company) + company_set.append(company) + + employee_count = frappe.db.count( + "Employee", {"designation": designation, "status": "Active", "company": ("in", company_set)} + ) + + filters = {"designation": designation, "status": "Open", "company": ("in", company_set)} + if job_opening: + filters["name"] = ("!=", job_opening) + + job_openings = frappe.db.count("Job Opening", filters) + + return {"employee_count": employee_count, "job_openings": job_openings} + + +@frappe.whitelist() +def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None): + if from_date is None: + from_date = getdate(nowdate()) + if to_date is None: + to_date = getdate(nowdate()) + if not company or not designation: + frappe.throw(_("Please select Company and Designation")) + + staffing_plan = frappe.db.sql( + """ + select sp.name, spd.vacancies, spd.total_estimated_cost + from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name + where company=%s and spd.designation=%s and sp.docstatus=1 + and to_date >= %s and from_date <= %s """, + (company, designation, from_date, to_date), + as_dict=1, + ) + + if not staffing_plan: + parent_company = frappe.get_cached_value("Company", company, "parent_company") + if parent_company: + staffing_plan = get_active_staffing_plan_details( + parent_company, designation, from_date, to_date + ) + + # Only a single staffing plan can be active for a designation on given date + return staffing_plan if staffing_plan else None diff --git a/hrms/hr/doctype/staffing_plan/staffing_plan_dashboard.py b/hrms/hr/doctype/staffing_plan/staffing_plan_dashboard.py new file mode 100644 index 0000000..0f555d9 --- /dev/null +++ b/hrms/hr/doctype/staffing_plan/staffing_plan_dashboard.py @@ -0,0 +1,5 @@ +def get_data(): + return { + "fieldname": "staffing_plan", + "transactions": [{"items": ["Job Opening"]}], + } diff --git a/hrms/hr/doctype/staffing_plan/test_staffing_plan.py b/hrms/hr/doctype/staffing_plan/test_staffing_plan.py new file mode 100644 index 0000000..345d6f8 --- /dev/null +++ b/hrms/hr/doctype/staffing_plan/test_staffing_plan.py @@ -0,0 +1,98 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, nowdate + +from hrms.hr.doctype.staffing_plan.staffing_plan import ParentCompanyError, SubsidiaryCompanyError + +test_dependencies = ["Designation"] + + +class TestStaffingPlan(unittest.TestCase): + def test_staffing_plan(self): + _set_up() + frappe.db.set_value("Company", "_Test Company 3", "is_group", 1) + if frappe.db.exists("Staffing Plan", "Test"): + return + staffing_plan = frappe.new_doc("Staffing Plan") + staffing_plan.company = "_Test Company 10" + staffing_plan.name = "Test" + staffing_plan.from_date = nowdate() + staffing_plan.to_date = add_days(nowdate(), 10) + staffing_plan.append( + "staffing_details", + {"designation": "Designer", "vacancies": 6, "estimated_cost_per_position": 50000}, + ) + staffing_plan.insert() + staffing_plan.submit() + self.assertEqual(staffing_plan.total_estimated_budget, 300000.00) + + def test_staffing_plan_subsidiary_company(self): + self.test_staffing_plan() + if frappe.db.exists("Staffing Plan", "Test 1"): + return + staffing_plan = frappe.new_doc("Staffing Plan") + staffing_plan.company = "_Test Company 3" + staffing_plan.name = "Test 1" + staffing_plan.from_date = nowdate() + staffing_plan.to_date = add_days(nowdate(), 10) + staffing_plan.append( + "staffing_details", + {"designation": "Designer", "vacancies": 3, "estimated_cost_per_position": 45000}, + ) + self.assertRaises(SubsidiaryCompanyError, staffing_plan.insert) + + def test_staffing_plan_parent_company(self): + _set_up() + if frappe.db.exists("Staffing Plan", "Test"): + return + staffing_plan = frappe.new_doc("Staffing Plan") + staffing_plan.company = "_Test Company 3" + staffing_plan.name = "Test" + staffing_plan.from_date = nowdate() + staffing_plan.to_date = add_days(nowdate(), 10) + staffing_plan.append( + "staffing_details", + {"designation": "Designer", "vacancies": 7, "estimated_cost_per_position": 50000}, + ) + staffing_plan.insert() + staffing_plan.submit() + self.assertEqual(staffing_plan.total_estimated_budget, 350000.00) + if frappe.db.exists("Staffing Plan", "Test 1"): + return + staffing_plan = frappe.new_doc("Staffing Plan") + staffing_plan.company = "_Test Company 10" + staffing_plan.name = "Test 1" + staffing_plan.from_date = nowdate() + staffing_plan.to_date = add_days(nowdate(), 10) + staffing_plan.append( + "staffing_details", + {"designation": "Designer", "vacancies": 7, "estimated_cost_per_position": 60000}, + ) + staffing_plan.insert() + self.assertRaises(ParentCompanyError, staffing_plan.submit) + + +def _set_up(): + for doctype in ["Staffing Plan", "Staffing Plan Detail"]: + frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) + make_company() + + +def make_company(name=None, abbr=None): + if not name: + name = "_Test Company 10" + + if frappe.db.exists("Company", name): + return + + company = frappe.new_doc("Company") + company.company_name = name + company.abbr = abbr or "_TC10" + company.parent_company = "_Test Company 3" + company.default_currency = "INR" + company.country = "Pakistan" + company.insert() diff --git a/hrms/hr/doctype/staffing_plan_detail/__init__.py b/hrms/hr/doctype/staffing_plan_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.json b/hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.json new file mode 100644 index 0000000..77164c4 --- /dev/null +++ b/hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.json @@ -0,0 +1,79 @@ +{ + "creation": "2018-04-13 18:04:20.978931", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "designation", + "vacancies", + "estimated_cost_per_position", + "total_estimated_cost", + "column_break_5", + "current_count", + "current_openings", + "number_of_positions" + ], + "fields": [ + { + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation", + "options": "Designation", + "reqd": 1 + }, + { + "fieldname": "number_of_positions", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Number Of Positions", + "read_only": 1 + }, + { + "fieldname": "estimated_cost_per_position", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Estimated Cost Per Position" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "current_count", + "fieldtype": "Int", + "label": "Current Count", + "read_only": 1 + }, + { + "fieldname": "current_openings", + "fieldtype": "Int", + "label": "Current Openings", + "read_only": 1 + }, + { + "fieldname": "vacancies", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Vacancies" + }, + { + "fieldname": "total_estimated_cost", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Estimated Cost", + "read_only": 1 + } + ], + "istable": 1, + "modified": "2019-06-24 18:40:37.140178", + "modified_by": "Administrator", + "module": "HR", + "name": "Staffing Plan Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.py b/hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.py new file mode 100644 index 0000000..6749690 --- /dev/null +++ b/hrms/hr/doctype/staffing_plan_detail/staffing_plan_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class StaffingPlanDetail(Document): + pass diff --git a/hrms/hr/doctype/training_event/__init__.py b/hrms/hr/doctype/training_event/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/training_event/test_training_event.py b/hrms/hr/doctype/training_event/test_training_event.py new file mode 100644 index 0000000..22da9c9 --- /dev/null +++ b/hrms/hr/doctype/training_event/test_training_event.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import add_days, today + +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_employee + + +class TestTrainingEvent(unittest.TestCase): + def setUp(self): + create_training_program("Basic Training") + employee = make_employee("robert_loan@trainig.com") + employee2 = make_employee("suzie.tan@trainig.com") + self.attendees = [{"employee": employee}, {"employee": employee2}] + + def test_training_event_status_update(self): + training_event = create_training_event(self.attendees) + training_event.submit() + + training_event.event_status = "Completed" + training_event.save() + training_event.reload() + + for entry in training_event.employees: + self.assertEqual(entry.status, "Completed") + + training_event.event_status = "Scheduled" + training_event.save() + training_event.reload() + + for entry in training_event.employees: + self.assertEqual(entry.status, "Open") + + def tearDown(self): + frappe.db.rollback() + + +def create_training_program(training_program): + if not frappe.db.get_value("Training Program", training_program): + frappe.get_doc( + { + "doctype": "Training Program", + "training_program": training_program, + "description": training_program, + } + ).insert() + + +def create_training_event(attendees): + return frappe.get_doc( + { + "doctype": "Training Event", + "event_name": "Basic Training Event", + "training_program": "Basic Training", + "location": "Union Square", + "start_time": add_days(today(), 5), + "end_time": add_days(today(), 6), + "introduction": "Welcome to the Basic Training Event", + "employees": attendees, + } + ).insert() diff --git a/hrms/hr/doctype/training_event/training_event.js b/hrms/hr/doctype/training_event/training_event.js new file mode 100644 index 0000000..642e6a1 --- /dev/null +++ b/hrms/hr/doctype/training_event/training_event.js @@ -0,0 +1,48 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Training Event', { + onload_post_render: function (frm) { + frm.get_field("employees").grid.set_multiple_add("employee"); + }, + refresh: function (frm) { + if (!frm.doc.__islocal) { + frm.add_custom_button(__("Training Result"), function () { + frappe.route_options = { + training_event: frm.doc.name + }; + frappe.set_route("List", "Training Result"); + }); + frm.add_custom_button(__("Training Feedback"), function () { + frappe.route_options = { + training_event: frm.doc.name + }; + frappe.set_route("List", "Training Feedback"); + }); + } + frm.events.set_employee_query(frm); + }, + + set_employee_query: function(frm) { + let emp = []; + for (let d in frm.doc.employees) { + if (frm.doc.employees[d].employee) { + emp.push(frm.doc.employees[d].employee); + } + } + frm.set_query("employee", "employees", function () { + return { + filters: { + name: ["NOT IN", emp], + status: "Active" + } + }; + }); + } +}); + +frappe.ui.form.on("Training Event Employee", { + employee: function(frm) { + frm.events.set_employee_query(frm); + } +}); diff --git a/hrms/hr/doctype/training_event/training_event.json b/hrms/hr/doctype/training_event/training_event.json new file mode 100644 index 0000000..42e02ca --- /dev/null +++ b/hrms/hr/doctype/training_event/training_event.json @@ -0,0 +1,231 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:event_name", + "creation": "2016-08-08 04:53:58.355206", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "event_name", + "training_program", + "event_status", + "has_certificate", + "column_break_2", + "type", + "level", + "company", + "section_break_4", + "trainer_name", + "trainer_email", + "column_break_7", + "supplier", + "contact_number", + "section_break_9", + "course", + "location", + "column_break_12", + "start_time", + "end_time", + "section_break_15", + "introduction", + "section_break_18", + "employees", + "amended_from", + "employee_emails" + ], + "fields": [ + { + "fieldname": "event_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Event Name", + "no_copy": 1, + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "training_program", + "fieldtype": "Link", + "label": "Training Program", + "options": "Training Program" + }, + { + "allow_on_submit": 1, + "fieldname": "event_status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Event Status", + "options": "Scheduled\nCompleted\nCancelled", + "reqd": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Conference' || doc.type == 'Exam'", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Type", + "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet\nSelf-Study", + "reqd": 1 + }, + { + "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Exam'", + "fieldname": "level", + "fieldtype": "Select", + "label": "Level", + "options": "\nBeginner\nIntermediate\nAdvance" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "trainer_name", + "fieldtype": "Data", + "label": "Trainer Name" + }, + { + "fieldname": "trainer_email", + "fieldtype": "Data", + "label": "Trainer Email" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, + { + "fieldname": "contact_number", + "fieldtype": "Data", + "label": "Contact Number" + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "fieldname": "course", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Course" + }, + { + "fieldname": "location", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Location", + "reqd": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "start_time", + "fieldtype": "Datetime", + "label": "Start Time", + "reqd": 1 + }, + { + "fieldname": "end_time", + "fieldtype": "Datetime", + "label": "End Time", + "reqd": 1 + }, + { + "fieldname": "section_break_15", + "fieldtype": "Section Break" + }, + { + "fieldname": "introduction", + "fieldtype": "Text Editor", + "label": "Introduction", + "reqd": 1 + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break", + "label": "Attendees" + }, + { + "allow_on_submit": 1, + "fieldname": "employees", + "fieldtype": "Table", + "label": "Employees", + "options": "Training Event Employee" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Training Event", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "employee_emails", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Employee Emails", + "options": "Email" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-04-28 13:29:35.139497", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Event", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "event_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "event_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/training_event/training_event.py b/hrms/hr/doctype/training_event/training_event.py new file mode 100644 index 0000000..c976328 --- /dev/null +++ b/hrms/hr/doctype/training_event/training_event.py @@ -0,0 +1,38 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import time_diff_in_seconds + +from erpnext.setup.doctype.employee.employee import get_employee_emails + + +class TrainingEvent(Document): + def validate(self): + self.set_employee_emails() + self.validate_period() + + def on_update_after_submit(self): + self.set_status_for_attendees() + + def set_employee_emails(self): + self.employee_emails = ", ".join(get_employee_emails([d.employee for d in self.employees])) + + def validate_period(self): + if time_diff_in_seconds(self.end_time, self.start_time) <= 0: + frappe.throw(_("End time cannot be before start time")) + + def set_status_for_attendees(self): + if self.event_status == "Completed": + for employee in self.employees: + if employee.attendance == "Present" and employee.status != "Feedback Submitted": + employee.status = "Completed" + + elif self.event_status == "Scheduled": + for employee in self.employees: + employee.status = "Open" + + self.db_update_all() diff --git a/hrms/hr/doctype/training_event/training_event_calendar.js b/hrms/hr/doctype/training_event/training_event_calendar.js new file mode 100644 index 0000000..cb168c0 --- /dev/null +++ b/hrms/hr/doctype/training_event/training_event_calendar.js @@ -0,0 +1,14 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.views.calendar["Training Event"] = { + field_map: { + "start": "start_time", + "end": "end_time", + "id": "name", + "title": "event_name", + "allDay": "allDay" + }, + gantt: true, + get_events_method: "frappe.desk.calendar.get_events", +} diff --git a/hrms/hr/doctype/training_event/training_event_dashboard.py b/hrms/hr/doctype/training_event/training_event_dashboard.py new file mode 100644 index 0000000..ca13938 --- /dev/null +++ b/hrms/hr/doctype/training_event/training_event_dashboard.py @@ -0,0 +1,7 @@ +def get_data(): + return { + "fieldname": "training_event", + "transactions": [ + {"items": ["Training Result", "Training Feedback"]}, + ], + } diff --git a/hrms/hr/doctype/training_event_employee/__init__.py b/hrms/hr/doctype/training_event_employee/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/training_event_employee/training_event_employee.json b/hrms/hr/doctype/training_event_employee/training_event_employee.json new file mode 100644 index 0000000..bcb7d5e --- /dev/null +++ b/hrms/hr/doctype/training_event_employee/training_event_employee.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "creation": "2016-08-08 05:33:39.965305", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_3", + "status", + "attendance", + "is_mandatory" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "no_copy": 1, + "options": "Employee" + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "label": "Employee Name" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "no_copy": 1, + "options": "Open\nInvited\nCompleted\nFeedback Submitted" + }, + { + "fieldname": "attendance", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Attendance", + "options": "Present\nAbsent" + }, + { + "columns": 2, + "default": "1", + "fieldname": "is_mandatory", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Mandatory" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-02 17:20:27.630176", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Event Employee", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/hr/doctype/training_event_employee/training_event_employee.py b/hrms/hr/doctype/training_event_employee/training_event_employee.py new file mode 100644 index 0000000..5dce6e1 --- /dev/null +++ b/hrms/hr/doctype/training_event_employee/training_event_employee.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class TrainingEventEmployee(Document): + pass diff --git a/hrms/hr/doctype/training_feedback/__init__.py b/hrms/hr/doctype/training_feedback/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/training_feedback/test_training_feedback.py b/hrms/hr/doctype/training_feedback/test_training_feedback.py new file mode 100644 index 0000000..6ed4509 --- /dev/null +++ b/hrms/hr/doctype/training_feedback/test_training_feedback.py @@ -0,0 +1,72 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe + +from hrms.hr.doctype.training_event.test_training_event import ( + create_training_event, + create_training_program, +) +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_employee + + +class TestTrainingFeedback(unittest.TestCase): + def setUp(self): + create_training_program("Basic Training") + self.employee = make_employee("robert_loan@trainig.com") + self.employee2 = make_employee("suzie.tan@trainig.com") + self.attendees = [{"employee": self.employee}] + + def test_employee_validations_for_feedback(self): + training_event = create_training_event(self.attendees) + training_event.submit() + + training_event.event_status = "Completed" + training_event.save() + training_event.reload() + + # should not allow creating feedback since employee2 was not part of the event + feedback = create_training_feedback(training_event.name, self.employee2) + self.assertRaises(frappe.ValidationError, feedback.save) + + # cannot record feedback for absent employee + employee = frappe.db.get_value( + "Training Event Employee", {"parent": training_event.name, "employee": self.employee}, "name" + ) + + frappe.db.set_value("Training Event Employee", employee, "attendance", "Absent") + feedback = create_training_feedback(training_event.name, self.employee) + self.assertRaises(frappe.ValidationError, feedback.save) + + def test_training_feedback_status(self): + training_event = create_training_event(self.attendees) + training_event.submit() + + training_event.event_status = "Completed" + training_event.save() + training_event.reload() + + feedback = create_training_feedback(training_event.name, self.employee) + feedback.submit() + + status = frappe.db.get_value( + "Training Event Employee", {"parent": training_event.name, "employee": self.employee}, "status" + ) + + self.assertEqual(status, "Feedback Submitted") + + def tearDown(self): + frappe.db.rollback() + + +def create_training_feedback(event, employee): + return frappe.get_doc( + { + "doctype": "Training Feedback", + "training_event": event, + "employee": employee, + "feedback": "Test", + } + ) diff --git a/hrms/hr/doctype/training_feedback/training_feedback.js b/hrms/hr/doctype/training_feedback/training_feedback.js new file mode 100644 index 0000000..5e875c1 --- /dev/null +++ b/hrms/hr/doctype/training_feedback/training_feedback.js @@ -0,0 +1,10 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Training Feedback', { + onload: function(frm) { + frm.add_fetch("training_event", "course", "course"); + frm.add_fetch("training_event", "event_name", "event_name"); + frm.add_fetch("training_event", "trainer_name", "trainer_name"); + } +}); diff --git a/hrms/hr/doctype/training_feedback/training_feedback.json b/hrms/hr/doctype/training_feedback/training_feedback.json new file mode 100644 index 0000000..e968911 --- /dev/null +++ b/hrms/hr/doctype/training_feedback/training_feedback.json @@ -0,0 +1,143 @@ +{ + "actions": [], + "autoname": "HR-TRF-.YYYY.-.#####", + "creation": "2022-01-27 13:14:35.935580", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "course", + "column_break_3", + "training_event", + "event_name", + "trainer_name", + "section_break_6", + "feedback", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "in_global_search": 1, + "label": "Employee Name" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "training_event.course", + "fieldname": "course", + "fieldtype": "Data", + "label": "Course", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "training_event", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Training Event", + "options": "Training Event", + "reqd": 1 + }, + { + "fetch_from": "training_event.event_name", + "fieldname": "event_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Event Name", + "read_only": 1 + }, + { + "fetch_from": "training_event.trainer_name", + "fieldname": "trainer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Trainer Name", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "feedback", + "fieldtype": "Text", + "label": "Feedback", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Training Feedback", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-04-28 13:32:29.261421", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Feedback", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee_name, training_event, event_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/hr/doctype/training_feedback/training_feedback.py b/hrms/hr/doctype/training_feedback/training_feedback.py new file mode 100644 index 0000000..d5de28e --- /dev/null +++ b/hrms/hr/doctype/training_feedback/training_feedback.py @@ -0,0 +1,47 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class TrainingFeedback(Document): + def validate(self): + training_event = frappe.get_doc("Training Event", self.training_event) + if training_event.docstatus != 1: + frappe.throw(_("{0} must be submitted").format(_("Training Event"))) + + emp_event_details = frappe.db.get_value( + "Training Event Employee", + {"parent": self.training_event, "employee": self.employee}, + ["name", "attendance"], + as_dict=True, + ) + + if not emp_event_details: + frappe.throw( + _("Employee {0} not found in Training Event Participants.").format( + frappe.bold(self.employee_name) + ) + ) + + if emp_event_details.attendance == "Absent": + frappe.throw(_("Feedback cannot be recorded for an absent Employee.")) + + def on_submit(self): + employee = frappe.db.get_value( + "Training Event Employee", {"parent": self.training_event, "employee": self.employee} + ) + + if employee: + frappe.db.set_value("Training Event Employee", employee, "status", "Feedback Submitted") + + def on_cancel(self): + employee = frappe.db.get_value( + "Training Event Employee", {"parent": self.training_event, "employee": self.employee} + ) + + if employee: + frappe.db.set_value("Training Event Employee", employee, "status", "Completed") diff --git a/hrms/hr/doctype/training_program/__init__.py b/hrms/hr/doctype/training_program/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/training_program/test_training_program.py b/hrms/hr/doctype/training_program/test_training_program.py new file mode 100644 index 0000000..5000705 --- /dev/null +++ b/hrms/hr/doctype/training_program/test_training_program.py @@ -0,0 +1,8 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestTrainingProgram(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/training_program/training_program.js b/hrms/hr/doctype/training_program/training_program.js new file mode 100644 index 0000000..a4ccf54 --- /dev/null +++ b/hrms/hr/doctype/training_program/training_program.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Training Program', { +}); diff --git a/hrms/hr/doctype/training_program/training_program.json b/hrms/hr/doctype/training_program/training_program.json new file mode 100644 index 0000000..522d5e9 --- /dev/null +++ b/hrms/hr/doctype/training_program/training_program.json @@ -0,0 +1,454 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "autoname": "field:training_program", + "beta": 0, + "creation": "2017-10-11 04:43:17.230065", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "training_program", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Training Program", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 1, + "collapsible": 0, + "columns": 0, + "default": "Scheduled", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Status", + "length": 0, + "no_copy": 0, + "options": "Scheduled\nCompleted\nCancelled", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "trainer_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Trainer Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "trainer_email", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Trainer Email", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_8", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "supplier", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Supplier", + "length": 0, + "no_copy": 0, + "options": "Supplier", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "contact_number", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Contact Number", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_11", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Training Program", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-11-27 08:12:03.649247", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Program", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "training_program", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/training_program/training_program.py b/hrms/hr/doctype/training_program/training_program.py new file mode 100644 index 0000000..96b2fd7 --- /dev/null +++ b/hrms/hr/doctype/training_program/training_program.py @@ -0,0 +1,9 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class TrainingProgram(Document): + pass diff --git a/hrms/hr/doctype/training_program/training_program_dashboard.py b/hrms/hr/doctype/training_program/training_program_dashboard.py new file mode 100644 index 0000000..1735db1 --- /dev/null +++ b/hrms/hr/doctype/training_program/training_program_dashboard.py @@ -0,0 +1,10 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "training_program", + "transactions": [ + {"label": _("Training Events"), "items": ["Training Event"]}, + ], + } diff --git a/hrms/hr/doctype/training_result/__init__.py b/hrms/hr/doctype/training_result/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/training_result/test_training_result.py b/hrms/hr/doctype/training_result/test_training_result.py new file mode 100644 index 0000000..136543c --- /dev/null +++ b/hrms/hr/doctype/training_result/test_training_result.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +# test_records = frappe.get_test_records('Training Result') + + +class TestTrainingResult(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/training_result/training_result.js b/hrms/hr/doctype/training_result/training_result.js new file mode 100644 index 0000000..a8358a0 --- /dev/null +++ b/hrms/hr/doctype/training_result/training_result.js @@ -0,0 +1,34 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Training Result', { + onload: function(frm) { + frm.trigger("training_event"); + }, + + training_event: function(frm) { + frm.trigger("training_event"); + }, + + training_event: function(frm) { + if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) { + frappe.call({ + method: "hrms.hr.doctype.training_result.training_result.get_employees", + args: { + "training_event": frm.doc.training_event + }, + callback: function(r) { + frm.set_value("employees" ,""); + if (r.message) { + $.each(r.message, function(i, d) { + var row = frappe.model.add_child(frm.doc, "Training Result Employee", "employees"); + row.employee = d.employee; + row.employee_name = d.employee_name; + }); + } + refresh_field("employees"); + } + }); + } + } +}); diff --git a/hrms/hr/doctype/training_result/training_result.json b/hrms/hr/doctype/training_result/training_result.json new file mode 100644 index 0000000..f28669e --- /dev/null +++ b/hrms/hr/doctype/training_result/training_result.json @@ -0,0 +1,83 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "HR-TRR-.YYYY.-.#####", + "creation": "2016-11-04 02:13:48.407576", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "training_event", + "section_break_3", + "employees", + "amended_from", + "employee_emails" + ], + "fields": [ + { + "fieldname": "training_event", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Training Event", + "options": "Training Event", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "employees", + "fieldtype": "Table", + "label": "Employees", + "options": "Training Result Employee" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Training Result", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "employee_emails", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Employee Emails", + "options": "Email" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-18 19:31:44.900034", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Result", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "training_event", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "training_event" +} \ No newline at end of file diff --git a/hrms/hr/doctype/training_result/training_result.py b/hrms/hr/doctype/training_result/training_result.py new file mode 100644 index 0000000..ffcf1d3 --- /dev/null +++ b/hrms/hr/doctype/training_result/training_result.py @@ -0,0 +1,34 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + +from erpnext.setup.doctype.employee.employee import get_employee_emails + + +class TrainingResult(Document): + def validate(self): + training_event = frappe.get_doc("Training Event", self.training_event) + if training_event.docstatus != 1: + frappe.throw(_("{0} must be submitted").format(_("Training Event"))) + + self.employee_emails = ", ".join(get_employee_emails([d.employee for d in self.employees])) + + def on_submit(self): + training_event = frappe.get_doc("Training Event", self.training_event) + training_event.status = "Completed" + for e in self.employees: + for e1 in training_event.employees: + if e1.employee == e.employee: + e1.status = "Completed" + break + + training_event.save() + + +@frappe.whitelist() +def get_employees(training_event): + return frappe.get_doc("Training Event", training_event).employees diff --git a/hrms/hr/doctype/training_result_employee/__init__.py b/hrms/hr/doctype/training_result_employee/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/training_result_employee/training_result_employee.json b/hrms/hr/doctype/training_result_employee/training_result_employee.json new file mode 100644 index 0000000..c474754 --- /dev/null +++ b/hrms/hr/doctype/training_result_employee/training_result_employee.json @@ -0,0 +1,334 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-11-04 02:39:12.825569", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee Name", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "hours", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Hours", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "grade", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Grade", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "comments", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Comments", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-01-30 11:28:14.337778", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Result Employee", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/training_result_employee/training_result_employee.py b/hrms/hr/doctype/training_result_employee/training_result_employee.py new file mode 100644 index 0000000..e048ff5 --- /dev/null +++ b/hrms/hr/doctype/training_result_employee/training_result_employee.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class TrainingResultEmployee(Document): + pass diff --git a/hrms/hr/doctype/travel_itinerary/__init__.py b/hrms/hr/doctype/travel_itinerary/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/travel_itinerary/travel_itinerary.json b/hrms/hr/doctype/travel_itinerary/travel_itinerary.json new file mode 100644 index 0000000..f887027 --- /dev/null +++ b/hrms/hr/doctype/travel_itinerary/travel_itinerary.json @@ -0,0 +1,512 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-05-15 07:40:59.181192", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "travel_from", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Travel From", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "travel_to", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Travel To", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mode_of_travel", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Mode of Travel", + "length": 0, + "no_copy": 0, + "options": "\nFlight\nTrain\nTaxi\nRented Car", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "meal_preference", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Meal Preference", + "length": 0, + "no_copy": 0, + "options": "\nVegetarian\nNon-Vegetarian\nGluten Free\nNon Diary", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "travel_advance_required", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Travel Advance Required", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "travel_advance_required", + "fieldname": "advance_amount", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Advance Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_6", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "departure_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Departure Datetime", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "arrival_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Arrival Datetime", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "lodging_required", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Lodging Required", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "lodging_required", + "fieldname": "preferred_area_for_lodging", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Preferred Area for Lodging", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "lodging_required", + "fieldname": "check_in_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Check-in Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "lodging_required", + "fieldname": "check_out_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Check-out Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_14", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "other_details", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Other Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-05-15 09:55:20.138108", + "modified_by": "Administrator", + "module": "HR", + "name": "Travel Itinerary", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/travel_itinerary/travel_itinerary.py b/hrms/hr/doctype/travel_itinerary/travel_itinerary.py new file mode 100644 index 0000000..529909b --- /dev/null +++ b/hrms/hr/doctype/travel_itinerary/travel_itinerary.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class TravelItinerary(Document): + pass diff --git a/hrms/hr/doctype/travel_request/__init__.py b/hrms/hr/doctype/travel_request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/travel_request/test_travel_request.py b/hrms/hr/doctype/travel_request/test_travel_request.py new file mode 100644 index 0000000..e29a1ca --- /dev/null +++ b/hrms/hr/doctype/travel_request/test_travel_request.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestTravelRequest(unittest.TestCase): + pass diff --git a/hrms/hr/doctype/travel_request/travel_request.js b/hrms/hr/doctype/travel_request/travel_request.js new file mode 100644 index 0000000..9dd48eb --- /dev/null +++ b/hrms/hr/doctype/travel_request/travel_request.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Travel Request', { + refresh: function(frm) { + + } +}); diff --git a/hrms/hr/doctype/travel_request/travel_request.json b/hrms/hr/doctype/travel_request/travel_request.json new file mode 100644 index 0000000..7908e1a --- /dev/null +++ b/hrms/hr/doctype/travel_request/travel_request.json @@ -0,0 +1,245 @@ +{ + "actions": [], + "autoname": "HR-TRQ-.YYYY.-.#####", + "creation": "2018-05-15 06:32:33.950356", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "travel_type", + "travel_funding", + "travel_proof", + "column_break_2", + "purpose_of_travel", + "details_of_sponsor", + "employee_details", + "employee", + "employee_name", + "cell_number", + "prefered_email", + "column_break_7", + "date_of_birth", + "personal_id_type", + "personal_id_number", + "passport_number", + "section_break_4", + "description", + "travel_itinerary", + "itinerary", + "costing_details", + "costings", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "event_details", + "name_of_organizer", + "address_of_organizer", + "other_details", + "amended_from" + ], + "fields": [ + { + "fieldname": "travel_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Travel Type", + "options": "\nDomestic\nInternational", + "reqd": 1 + }, + { + "fieldname": "travel_funding", + "fieldtype": "Select", + "label": "Travel Funding", + "options": "\nRequire Full Funding\nFully Sponsored\nPartially Sponsored, Require Partial Funding" + }, + { + "fieldname": "travel_proof", + "fieldtype": "Attach", + "label": "Copy of Invitation/Announcement" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "purpose_of_travel", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Purpose of Travel", + "options": "Purpose of Travel", + "reqd": 1 + }, + { + "fieldname": "details_of_sponsor", + "fieldtype": "Data", + "label": "Details of Sponsor (Name, Location)" + }, + { + "collapsible": 1, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Description" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Any other details" + }, + { + "collapsible": 1, + "fieldname": "employee_details", + "fieldtype": "Section Break", + "label": "Employee Details" + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.cell_number", + "fieldname": "cell_number", + "fieldtype": "Data", + "label": "Contact Number" + }, + { + "fetch_from": "employee.prefered_email", + "fieldname": "prefered_email", + "fieldtype": "Data", + "label": "Contact Email" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.date_of_birth", + "fieldname": "date_of_birth", + "fieldtype": "Date", + "label": "Date of Birth", + "read_only": 1 + }, + { + "fieldname": "personal_id_type", + "fieldtype": "Link", + "label": "Identification Document Type", + "options": "Identification Document Type" + }, + { + "fieldname": "personal_id_number", + "fieldtype": "Data", + "label": "Identification Document Number" + }, + { + "fetch_from": "employee.passport_number", + "fieldname": "passport_number", + "fieldtype": "Data", + "label": "Passport Number" + }, + { + "fieldname": "travel_itinerary", + "fieldtype": "Section Break", + "label": "Travel Itinerary" + }, + { + "fieldname": "itinerary", + "fieldtype": "Table", + "options": "Travel Itinerary" + }, + { + "fieldname": "costing_details", + "fieldtype": "Section Break", + "label": "Costing Details" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "costings", + "fieldtype": "Table", + "label": "Costing", + "options": "Travel Request Costing" + }, + { + "collapsible": 1, + "fieldname": "event_details", + "fieldtype": "Section Break", + "label": "Event Details" + }, + { + "fieldname": "name_of_organizer", + "fieldtype": "Data", + "label": "Name of Organizer" + }, + { + "fieldname": "address_of_organizer", + "fieldtype": "Data", + "label": "Address of Organizer" + }, + { + "fieldname": "other_details", + "fieldtype": "Text", + "label": "Other Details" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Travel Request", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-18 19:19:33.678664", + "modified_by": "Administrator", + "module": "HR", + "name": "Travel Request", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/travel_request/travel_request.py b/hrms/hr/doctype/travel_request/travel_request.py new file mode 100644 index 0000000..ecac86c --- /dev/null +++ b/hrms/hr/doctype/travel_request/travel_request.py @@ -0,0 +1,12 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + +from hrms.hr.utils import validate_active_employee + + +class TravelRequest(Document): + def validate(self): + validate_active_employee(self.employee) diff --git a/hrms/hr/doctype/travel_request_costing/__init__.py b/hrms/hr/doctype/travel_request_costing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/travel_request_costing/travel_request_costing.json b/hrms/hr/doctype/travel_request_costing/travel_request_costing.json new file mode 100644 index 0000000..b64b1a9 --- /dev/null +++ b/hrms/hr/doctype/travel_request_costing/travel_request_costing.json @@ -0,0 +1,257 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-05-15 10:28:37.429581", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "expense_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Expense Type", + "length": 0, + "no_copy": 0, + "options": "Expense Claim Type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sponsored_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Sponsored Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "funded_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Funded Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Total Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "comments", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Comments", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-05-15 10:42:07.960530", + "modified_by": "Administrator", + "module": "HR", + "name": "Travel Request Costing", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/travel_request_costing/travel_request_costing.py b/hrms/hr/doctype/travel_request_costing/travel_request_costing.py new file mode 100644 index 0000000..0d1a592 --- /dev/null +++ b/hrms/hr/doctype/travel_request_costing/travel_request_costing.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class TravelRequestCosting(Document): + pass diff --git a/hrms/hr/doctype/upload_attendance/README.md b/hrms/hr/doctype/upload_attendance/README.md new file mode 100644 index 0000000..d0b3721 --- /dev/null +++ b/hrms/hr/doctype/upload_attendance/README.md @@ -0,0 +1 @@ +Tool to upload attendance via csv file. \ No newline at end of file diff --git a/hrms/hr/doctype/upload_attendance/__init__.py b/hrms/hr/doctype/upload_attendance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/upload_attendance/test_upload_attendance.py b/hrms/hr/doctype/upload_attendance/test_upload_attendance.py new file mode 100644 index 0000000..1e354a5 --- /dev/null +++ b/hrms/hr/doctype/upload_attendance/test_upload_attendance.py @@ -0,0 +1,43 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import getdate + +import erpnext +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.upload_attendance.upload_attendance import get_data + +test_dependencies = ["Holiday List"] + + +class TestUploadAttendance(unittest.TestCase): + @classmethod + def setUpClass(cls): + frappe.db.set_value( + "Company", erpnext.get_default_company(), "default_holiday_list", "_Test Holiday List" + ) + + def test_date_range(self): + employee = make_employee("test_employee@company.com") + employee_doc = frappe.get_doc("Employee", employee) + date_of_joining = "2018-01-02" + relieving_date = "2018-01-03" + from_date = "2018-01-01" + to_date = "2018-01-04" + employee_doc.date_of_joining = date_of_joining + employee_doc.relieving_date = relieving_date + employee_doc.save() + args = {"from_date": from_date, "to_date": to_date} + data = get_data(args) + filtered_data = [] + for row in data: + if row[1] == employee: + filtered_data.append(row) + for row in filtered_data: + self.assertTrue( + getdate(row[3]) >= getdate(date_of_joining) and getdate(row[3]) <= getdate(relieving_date) + ) diff --git a/hrms/hr/doctype/upload_attendance/upload_attendance.js b/hrms/hr/doctype/upload_attendance/upload_attendance.js new file mode 100644 index 0000000..cd5c15b --- /dev/null +++ b/hrms/hr/doctype/upload_attendance/upload_attendance.js @@ -0,0 +1,69 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + + + +frappe.provide("hrms.hr"); + +hrms.hr.AttendanceControlPanel = class AttendanceControlPanel extends frappe.ui.form.Controller { + onload() { + this.frm.set_value("att_fr_date", frappe.datetime.get_today()); + this.frm.set_value("att_to_date", frappe.datetime.get_today()); + } + + refresh() { + this.frm.disable_save(); + this.show_upload(); + this.setup_import_progress(); + } + + get_template() { + if(!this.frm.doc.att_fr_date || !this.frm.doc.att_to_date) { + frappe.msgprint(__("Attendance From Date and Attendance To Date is mandatory")); + return; + } + window.location.href = repl(frappe.request.url + + '?cmd=%(cmd)s&from_date=%(from_date)s&to_date=%(to_date)s', { + cmd: "hrms.hr.doctype.upload_attendance.upload_attendance.get_template", + from_date: this.frm.doc.att_fr_date, + to_date: this.frm.doc.att_to_date, + }); + } + + show_upload() { + var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty(); + new frappe.ui.FileUploader({ + wrapper: $wrapper, + method: 'hrms.hr.doctype.upload_attendance.upload_attendance.upload' + }); + } + + setup_import_progress() { + var $log_wrapper = $(this.frm.fields_dict.import_log.wrapper).empty(); + + frappe.realtime.on('import_attendance', (data) => { + if (data.progress) { + this.frm.dashboard.show_progress('Import Attendance', data.progress / data.total * 100, + __('Importing {0} of {1}', [data.progress, data.total])); + if (data.progress === data.total) { + this.frm.dashboard.hide_progress('Import Attendance'); + } + } else if (data.error) { + this.frm.dashboard.hide(); + let messages = [`${__('Error in some rows')}`].concat(data.messages + .filter(message => message.includes('Error')) + .map(message => `${message}`)) + .join(''); + $log_wrapper.append('' + messages); + } else if (data.messages) { + this.frm.dashboard.hide(); + let messages = [``].concat(data.messages + .map(message => ``)) + .join(''); + $log_wrapper.append('
${__('Import Successful')}
${message}
' + messages); + } + }); + } +} + +cur_frm.cscript = new hrms.hr.AttendanceControlPanel({frm: cur_frm}); diff --git a/hrms/hr/doctype/upload_attendance/upload_attendance.json b/hrms/hr/doctype/upload_attendance/upload_attendance.json new file mode 100644 index 0000000..a1451fd --- /dev/null +++ b/hrms/hr/doctype/upload_attendance/upload_attendance.json @@ -0,0 +1,285 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2013-01-25 11:34:53", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "download_template", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Download Template", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "att_fr_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Attendance From Date", + "length": 0, + "no_copy": 0, + "oldfieldname": "attenadnce_date", + "oldfieldtype": "Date", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "att_to_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Attendance To Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "get_template", + "fieldtype": "Button", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Get Template", + "length": 0, + "no_copy": 0, + "oldfieldtype": "Button", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "upload_attendance_data", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Import Attendance", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "upload_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Upload HTML", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "import_log", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Import Log", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 1, + "icon": "fa fa-upload-alt", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 1, + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "HR", + "name": "Upload Attendance", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "HR User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/hrms/hr/doctype/upload_attendance/upload_attendance.py b/hrms/hr/doctype/upload_attendance/upload_attendance.py new file mode 100644 index 0000000..68eef4b --- /dev/null +++ b/hrms/hr/doctype/upload_attendance/upload_attendance.py @@ -0,0 +1,223 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import add_days, cstr, date_diff, getdate +from frappe.utils.csvutils import UnicodeWriter + +from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee + +from hrms.hr.utils import get_holiday_dates_for_employee + + +class UploadAttendance(Document): + pass + + +@frappe.whitelist() +def get_template(): + if not frappe.has_permission("Attendance", "create"): + raise frappe.PermissionError + + args = frappe.local.form_dict + + if getdate(args.from_date) > getdate(args.to_date): + frappe.throw(_("To Date should be greater than From Date")) + + w = UnicodeWriter() + w = add_header(w) + + try: + w = add_data(w, args) + except Exception as e: + frappe.clear_messages() + frappe.respond_as_web_page("Holiday List Missing", html=e) + return + + # write out response as a type csv + frappe.response["result"] = cstr(w.getvalue()) + frappe.response["type"] = "csv" + frappe.response["doctype"] = "Attendance" + + +def add_header(w): + status = ", ".join( + (frappe.get_meta("Attendance").get_field("status").options or "").strip().split("\n") + ) + w.writerow(["Notes:"]) + w.writerow(["Please do not change the template headings"]) + w.writerow(["Status should be one of these values: " + status]) + w.writerow(["If you are overwriting existing attendance records, 'ID' column mandatory"]) + w.writerow( + ["ID", "Employee", "Employee Name", "Date", "Status", "Leave Type", "Company", "Naming Series"] + ) + return w + + +def add_data(w, args): + data = get_data(args) + writedata(w, data) + return w + + +def get_data(args): + dates = get_dates(args) + employees = get_active_employees() + holidays = get_holidays_for_employees( + [employee.name for employee in employees], args["from_date"], args["to_date"] + ) + existing_attendance_records = get_existing_attendance_records(args) + data = [] + for date in dates: + for employee in employees: + if getdate(date) < getdate(employee.date_of_joining): + continue + if employee.relieving_date: + if getdate(date) > getdate(employee.relieving_date): + continue + existing_attendance = {} + if ( + existing_attendance_records + and tuple([getdate(date), employee.name]) in existing_attendance_records + and getdate(employee.date_of_joining) <= getdate(date) + and getdate(employee.relieving_date) >= getdate(date) + ): + existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])] + + employee_holiday_list = get_holiday_list_for_employee(employee.name) + + row = [ + existing_attendance and existing_attendance.name or "", + employee.name, + employee.employee_name, + date, + existing_attendance and existing_attendance.status or "", + existing_attendance and existing_attendance.leave_type or "", + employee.company, + existing_attendance and existing_attendance.naming_series or get_naming_series(), + ] + if date in holidays[employee_holiday_list]: + row[4] = "Holiday" + data.append(row) + + return data + + +def get_holidays_for_employees(employees, from_date, to_date): + holidays = {} + for employee in employees: + holiday_list = get_holiday_list_for_employee(employee) + holiday = get_holiday_dates_for_employee(employee, getdate(from_date), getdate(to_date)) + if holiday_list not in holidays: + holidays[holiday_list] = holiday + + return holidays + + +def writedata(w, data): + for row in data: + w.writerow(row) + + +def get_dates(args): + """get list of dates in between from date and to date""" + no_of_days = date_diff(add_days(args["to_date"], 1), args["from_date"]) + dates = [add_days(args["from_date"], i) for i in range(0, no_of_days)] + return dates + + +def get_active_employees(): + employees = frappe.db.get_all( + "Employee", + fields=["name", "employee_name", "date_of_joining", "company", "relieving_date"], + filters={"docstatus": ["<", 2], "status": "Active"}, + ) + return employees + + +def get_existing_attendance_records(args): + attendance = frappe.db.sql( + """select name, attendance_date, employee, status, leave_type, naming_series + from `tabAttendance` where attendance_date between %s and %s and docstatus < 2""", + (args["from_date"], args["to_date"]), + as_dict=1, + ) + + existing_attendance = {} + for att in attendance: + existing_attendance[tuple([att.attendance_date, att.employee])] = att + + return existing_attendance + + +def get_naming_series(): + series = frappe.get_meta("Attendance").get_field("naming_series").options.strip().split("\n") + if not series: + frappe.throw(_("Please setup numbering series for Attendance via Setup > Numbering Series")) + return series[0] + + +@frappe.whitelist() +def upload(): + if not frappe.has_permission("Attendance", "create"): + raise frappe.PermissionError + + from frappe.utils.csvutils import read_csv_content + + rows = read_csv_content(frappe.local.uploaded_file) + if not rows: + frappe.throw(_("Please select a csv file")) + frappe.enqueue(import_attendances, rows=rows, now=True if len(rows) < 200 else False) + + +def import_attendances(rows): + def remove_holidays(rows): + rows = [row for row in rows if row[4] != "Holiday"] + return rows + + from frappe.modules import scrub + + rows = list(filter(lambda x: x and any(x), rows)) + columns = [scrub(f) for f in rows[4]] + columns[0] = "name" + columns[3] = "attendance_date" + rows = rows[5:] + ret = [] + error = False + + rows = remove_holidays(rows) + + from frappe.utils.csvutils import check_record, import_doc + + for i, row in enumerate(rows): + if not row: + continue + row_idx = i + 5 + d = frappe._dict(zip(columns, row)) + + d["doctype"] = "Attendance" + if d.name: + d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus") + + try: + check_record(d) + ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) + frappe.publish_realtime("import_attendance", dict(progress=i, total=len(rows))) + except AttributeError: + pass + except Exception as e: + error = True + ret.append("Error for row (#%d) %s : %s" % (row_idx, len(row) > 1 and row[1] or "", cstr(e))) + frappe.errprint(frappe.get_traceback()) + + if error: + frappe.db.rollback() + else: + frappe.db.commit() + + frappe.publish_realtime("import_attendance", dict(messages=ret, error=error)) diff --git a/hrms/hr/doctype/vehicle_log/__init__.py b/hrms/hr/doctype/vehicle_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/vehicle_log/test_vehicle_log.py b/hrms/hr/doctype/vehicle_log/test_vehicle_log.py new file mode 100644 index 0000000..c78b868 --- /dev/null +++ b/hrms/hr/doctype/vehicle_log/test_vehicle_log.py @@ -0,0 +1,132 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.utils import cstr, flt, nowdate, random_string + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.vehicle_log.vehicle_log import make_expense_claim + + +class TestVehicleLog(unittest.TestCase): + def setUp(self): + employee_id = frappe.db.sql( + """select name from `tabEmployee` where name='testdriver@example.com'""" + ) + self.employee_id = employee_id[0][0] if employee_id else None + + if not self.employee_id: + self.employee_id = make_employee("testdriver@example.com", company="_Test Company") + + self.license_plate = get_vehicle(self.employee_id) + + def tearDown(self): + frappe.delete_doc("Vehicle", self.license_plate, force=1) + frappe.delete_doc("Employee", self.employee_id, force=1) + + def test_make_vehicle_log_and_syncing_of_odometer_value(self): + vehicle_log = make_vehicle_log(self.license_plate, self.employee_id) + + # checking value of vehicle odometer value on submit. + vehicle = frappe.get_doc("Vehicle", self.license_plate) + self.assertEqual(vehicle.last_odometer, vehicle_log.odometer) + + # checking value vehicle odometer on vehicle log cancellation. + last_odometer = vehicle_log.last_odometer + current_odometer = vehicle_log.odometer + distance_travelled = current_odometer - last_odometer + + vehicle_log.cancel() + vehicle.reload() + + self.assertEqual(vehicle.last_odometer, current_odometer - distance_travelled) + + vehicle_log.delete() + + def test_vehicle_log_fuel_expense(self): + vehicle_log = make_vehicle_log(self.license_plate, self.employee_id) + + expense_claim = make_expense_claim(vehicle_log.name) + fuel_expense = expense_claim.expenses[0].amount + self.assertEqual(fuel_expense, 50 * 500) + + vehicle_log.cancel() + frappe.delete_doc("Expense Claim", expense_claim.name) + frappe.delete_doc("Vehicle Log", vehicle_log.name) + + def test_vehicle_log_with_service_expenses(self): + vehicle_log = make_vehicle_log(self.license_plate, self.employee_id, with_services=True) + + expense_claim = make_expense_claim(vehicle_log.name) + expenses = expense_claim.expenses[0].amount + self.assertEqual(expenses, 27000) + + vehicle_log.cancel() + frappe.delete_doc("Expense Claim", expense_claim.name) + frappe.delete_doc("Vehicle Log", vehicle_log.name) + + +def get_vehicle(employee_id): + license_plate = random_string(10).upper() + vehicle = frappe.get_doc( + { + "doctype": "Vehicle", + "license_plate": cstr(license_plate), + "make": "Maruti", + "model": "PCM", + "employee": employee_id, + "last_odometer": 5000, + "acquisition_date": nowdate(), + "location": "Mumbai", + "chassis_no": "1234ABCD", + "uom": "Litre", + "vehicle_value": flt(500000), + } + ) + try: + vehicle.insert(ignore_if_duplicate=True) + except frappe.DuplicateEntryError: + pass + return license_plate + + +def make_vehicle_log(license_plate, employee_id, with_services=False): + vehicle_log = frappe.get_doc( + { + "doctype": "Vehicle Log", + "license_plate": cstr(license_plate), + "employee": employee_id, + "date": nowdate(), + "odometer": 5010, + "fuel_qty": flt(50), + "price": flt(500), + } + ) + + if with_services: + vehicle_log.append( + "service_detail", + { + "service_item": "Oil Change", + "type": "Inspection", + "frequency": "Mileage", + "expense_amount": flt(500), + }, + ) + vehicle_log.append( + "service_detail", + { + "service_item": "Wheels", + "type": "Change", + "frequency": "Half Yearly", + "expense_amount": flt(1500), + }, + ) + + vehicle_log.save() + vehicle_log.submit() + + return vehicle_log diff --git a/hrms/hr/doctype/vehicle_log/vehicle_log.js b/hrms/hr/doctype/vehicle_log/vehicle_log.js new file mode 100644 index 0000000..fa0dfad --- /dev/null +++ b/hrms/hr/doctype/vehicle_log/vehicle_log.js @@ -0,0 +1,26 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Vehicle Log", { + refresh: function(frm) { + if(frm.doc.docstatus == 1) { + frm.add_custom_button(__('Expense Claim'), function() { + frm.events.expense_claim(frm); + }, __('Create')); + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + }, + + expense_claim: function(frm){ + frappe.call({ + method: "hrms.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", + args:{ + docname: frm.doc.name + }, + callback: function(r){ + var doc = frappe.model.sync(r.message); + frappe.set_route('Form', 'Expense Claim', r.message.name); + } + }); + } +}); diff --git a/hrms/hr/doctype/vehicle_log/vehicle_log.json b/hrms/hr/doctype/vehicle_log/vehicle_log.json new file mode 100644 index 0000000..4ea9045 --- /dev/null +++ b/hrms/hr/doctype/vehicle_log/vehicle_log.json @@ -0,0 +1,191 @@ +{ + "actions": [], + "autoname": "naming_series:", + "creation": "2016-09-03 14:14:51.788550", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "vehicle_section", + "naming_series", + "license_plate", + "employee", + "column_break_7", + "model", + "make", + "odometer_reading", + "date", + "odometer", + "column_break_12", + "last_odometer", + "refuelling_details", + "fuel_qty", + "price", + "column_break_15", + "supplier", + "invoice", + "service_details", + "service_detail", + "amended_from" + ], + "fields": [ + { + "fieldname": "vehicle_section", + "fieldtype": "Section Break", + "options": "fa fa-user" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-VLOG-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "license_plate", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "License Plate", + "options": "Vehicle", + "reqd": 1 + }, + { + "fetch_from": "license_plate.employee", + "fetch_if_empty": 1, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fetch_from": "license_plate.model", + "fieldname": "model", + "fieldtype": "Read Only", + "label": "Model" + }, + { + "fetch_from": "license_plate.make", + "fieldname": "make", + "fieldtype": "Read Only", + "label": "Make" + }, + { + "fieldname": "odometer_reading", + "fieldtype": "Section Break", + "label": "Odometer Reading" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, + { + "fieldname": "odometer", + "fieldtype": "Int", + "label": "Current Odometer value ", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "refuelling_details", + "fieldtype": "Section Break", + "label": "Refuelling Details" + }, + { + "fieldname": "fuel_qty", + "fieldtype": "Float", + "label": "Fuel Qty" + }, + { + "fieldname": "price", + "fieldtype": "Currency", + "label": "Fuel Price" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, + { + "fieldname": "invoice", + "fieldtype": "Data", + "label": "Invoice Ref" + }, + { + "collapsible": 1, + "fieldname": "service_details", + "fieldtype": "Section Break", + "label": "Service Details" + }, + { + "fieldname": "service_detail", + "fieldtype": "Table", + "options": "Vehicle Service" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Vehicle Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "license_plate.last_odometer", + "fieldname": "last_odometer", + "fieldtype": "Int", + "label": "Last Odometer Value ", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2021-05-17 00:10:21.188352", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Log", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Fleet Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/vehicle_log/vehicle_log.py b/hrms/hr/doctype/vehicle_log/vehicle_log.py new file mode 100644 index 0000000..2c1d9a4 --- /dev/null +++ b/hrms/hr/doctype/vehicle_log/vehicle_log.py @@ -0,0 +1,53 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt + + +class VehicleLog(Document): + def validate(self): + if flt(self.odometer) < flt(self.last_odometer): + frappe.throw( + _("Current Odometer Value should be greater than Last Odometer Value {0}").format( + self.last_odometer + ) + ) + + def on_submit(self): + frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) + + def on_cancel(self): + distance_travelled = self.odometer - self.last_odometer + if distance_travelled > 0: + updated_odometer_value = ( + int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled + ) + frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) + + +@frappe.whitelist() +def make_expense_claim(docname): + expense_claim = frappe.db.exists("Expense Claim", {"vehicle_log": docname}) + if expense_claim: + frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(expense_claim)) + + vehicle_log = frappe.get_doc("Vehicle Log", docname) + service_expense = sum([flt(d.expense_amount) for d in vehicle_log.service_detail]) + + claim_amount = service_expense + (flt(vehicle_log.price) * flt(vehicle_log.fuel_qty) or 1) + if not claim_amount: + frappe.throw(_("No additional expenses has been added")) + + exp_claim = frappe.new_doc("Expense Claim") + exp_claim.employee = vehicle_log.employee + exp_claim.vehicle_log = vehicle_log.name + exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) + exp_claim.append( + "expenses", + {"expense_date": vehicle_log.date, "description": _("Vehicle Expenses"), "amount": claim_amount}, + ) + return exp_claim.as_dict() diff --git a/hrms/hr/doctype/vehicle_service/__init__.py b/hrms/hr/doctype/vehicle_service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/doctype/vehicle_service/vehicle_service.json b/hrms/hr/doctype/vehicle_service/vehicle_service.json new file mode 100644 index 0000000..e0bce2b --- /dev/null +++ b/hrms/hr/doctype/vehicle_service/vehicle_service.json @@ -0,0 +1,57 @@ +{ + "creation": "2016-09-03 19:20:14.561962", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "service_item", + "type", + "frequency", + "expense_amount" + ], + "fields": [ + { + "fieldname": "service_item", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Service Item", + "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", + "reqd": 1 + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "options": "\nInspection\nService\nChange", + "reqd": 1 + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Frequency", + "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", + "reqd": 1 + }, + { + "fieldname": "expense_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Expense", + "reqd": 1 + } + ], + "istable": 1, + "modified": "2020-03-18 16:49:46.645004", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Service", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/hr/doctype/vehicle_service/vehicle_service.py b/hrms/hr/doctype/vehicle_service/vehicle_service.py new file mode 100644 index 0000000..fdd4e39 --- /dev/null +++ b/hrms/hr/doctype/vehicle_service/vehicle_service.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class VehicleService(Document): + pass diff --git a/hrms/hr/employee_property_update.js b/hrms/hr/employee_property_update.js new file mode 100644 index 0000000..a540402 --- /dev/null +++ b/hrms/hr/employee_property_update.js @@ -0,0 +1,174 @@ +frappe.ui.form.on(cur_frm.doctype, { + setup: function(frm) { + frm.set_query("employee", function() { + return { + filters: { + "status": "Active" + } + }; + }); + }, + + onload: function(frm) { + if (frm.doc.__islocal && !frm.doc.amended_from) + frm.trigger("clear_property_table"); + }, + + employee: function(frm) { + frm.trigger("clear_property_table"); + }, + + clear_property_table: function(frm) { + let table = (frm.doctype == "Employee Promotion") ? "promotion_details" : "transfer_details"; + frm.clear_table(table); + frm.refresh_field(table); + + frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide(); + }, + + refresh: function(frm) { + let table; + if (frm.doctype == "Employee Promotion") { + table = "promotion_details"; + } else if (frm.doctype == "Employee Transfer") { + table = "transfer_details"; + } + + if (!table) + return; + + frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide(); + frm.events.setup_employee_property_button(frm, table); + }, + + setup_employee_property_button: function(frm, table) { + frm.fields_dict[table].grid.add_custom_button(__("Add Employee Property"), () => { + if (!frm.doc.employee) { + frappe.msgprint(__("Please select Employee first.")); + return; + } + + const allowed_fields = []; + const exclude_fields = ["naming_series", "employee", "first_name", "middle_name", "last_name", "marital_status", "ctc", + "employee_name", "status", "image", "gender", "date_of_birth", "date_of_joining", "lft", "rgt", "old_parent"]; + + const exclude_field_types = ["HTML", "Section Break", "Column Break", "Button", "Read Only", "Tab Break", "Table"]; + + frappe.model.with_doctype("Employee", () => { + const field_label_map = {}; + frappe.get_meta("Employee").fields.forEach(d => { + field_label_map[d.fieldname] = __(d.label) + ` (${d.fieldname})`; + if (!in_list(exclude_field_types, d.fieldtype) && !in_list(exclude_fields, d.fieldname)) { + allowed_fields.push({ + label: field_label_map[d.fieldname], + value: d.fieldname, + }); + } + }); + + show_dialog(frm, table, allowed_fields); + }); + }); + } +}); + +var show_dialog = function(frm, table, field_labels) { + var d = new frappe.ui.Dialog({ + title: "Update Property", + fields: [ + {fieldname: "property", label: __("Select Property"), fieldtype: "Autocomplete", options: field_labels}, + {fieldname: "current", fieldtype: "Data", label: __("Current"), read_only: true}, + {fieldname: "new_value", fieldtype: "Data", label: __("New")} + ], + primary_action_label: __("Add to Details"), + primary_action: () => { + d.get_primary_btn().attr("disabled", true); + if (d.data) { + d.data.new = d.get_values().new_value; + add_to_details(frm, d, table); + } + } + }); + + d.fields_dict["property"].df.onchange = () => { + let property = d.get_values().property; + d.data.fieldname = property; + if(!property){return;} + frappe.call({ + method: 'hrms.hr.utils.get_employee_field_property', + args: {employee: frm.doc.employee, fieldname: property}, + callback: function(r) { + if (r.message) { + d.data.current = r.message.value; + d.data.property = r.message.label; + + d.set_value('current', r.message.value); + render_dynamic_field(d, r.message.datatype, r.message.options, property); + d.get_primary_btn().attr('disabled', false); + } + } + }); + }; + d.get_primary_btn().attr('disabled', true); + d.data = {}; + d.show(); +}; + +var render_dynamic_field = function(d, fieldtype, options, fieldname) { + d.data.new = null; + var dynamic_field = frappe.ui.form.make_control({ + df: { + "fieldtype": fieldtype, + "fieldname": fieldname, + "options": options || '', + "label": __("New") + }, + parent: d.fields_dict.new_value.wrapper, + only_input: false + }); + dynamic_field.make_input(); + d.replace_field("new_value", dynamic_field.df); +}; + +var add_to_details = function(frm, d, table) { + let data = d.data; + if (data.fieldname) { + if (validate_duplicate(frm, table, data.fieldname)) { + frappe.show_alert({message: __("Property already added"), indicator: "orange"}); + return false; + } + if (data.current == data.new) { + frappe.show_alert({message: __("Nothing to change"), indicator: "orange"}); + d.get_primary_btn().attr("disabled", false); + return false; + } + frm.add_child(table, { + fieldname: data.fieldname, + property: data.property, + current: data.current, + new: data.new + }); + frm.refresh_field(table); + + frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide(); + + d.fields_dict.new_value.$wrapper.html(""); + d.set_value("property", ""); + d.set_value("current", ""); + frappe.show_alert({message: __("Added to details"), indicator: "green"}); + d.data = {}; + } else { + frappe.show_alert({message: __("Value missing"), indicator: "red"}); + } +}; + +var validate_duplicate = function(frm, table, fieldname){ + let duplicate = false; + $.each(frm.doc[table], function(i, detail) { + if(detail.fieldname === fieldname){ + duplicate = true; + return; + } + }); + return duplicate; +}; diff --git a/hrms/hr/hr_dashboard/attendance/attendance.json b/hrms/hr/hr_dashboard/attendance/attendance.json new file mode 100644 index 0000000..4864245 --- /dev/null +++ b/hrms/hr/hr_dashboard/attendance/attendance.json @@ -0,0 +1,46 @@ +{ + "cards": [ + { + "card": "Total Present (This Month)" + }, + { + "card": "Total Absent (This Month)" + }, + { + "card": "Late Entry (This Month)" + }, + { + "card": "Early Exit (This Month)" + } + ], + "charts": [ + { + "chart": "Attendance Count", + "width": "Full" + }, + { + "chart": "Timesheet Activity Breakup", + "width": "Half" + }, + { + "chart": "Shift Assignment Breakup", + "width": "Half" + }, + { + "chart": "Department wise Timesheet Hours", + "width": "Full" + } + ], + "creation": "2022-08-21 14:13:18.835357", + "dashboard_name": "Attendance", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2022-08-21 18:12:38.531243", + "modified_by": "Administrator", + "module": "HR", + "name": "Attendance", + "owner": "Administrator" +} \ No newline at end of file diff --git a/hrms/hr/hr_dashboard/employee_lifecycle/employee_lifecycle.json b/hrms/hr/hr_dashboard/employee_lifecycle/employee_lifecycle.json new file mode 100644 index 0000000..4fe0fb5 --- /dev/null +++ b/hrms/hr/hr_dashboard/employee_lifecycle/employee_lifecycle.json @@ -0,0 +1,49 @@ +{ + "cards": [ + { + "card": "Onboardings (This Month)" + }, + { + "card": "Separations (This Month)" + }, + { + "card": "Promotions (This Month)" + }, + { + "card": "Transfers (This Month)" + }, + { + "card": "Trainings (This Month)" + } + ], + "charts": [ + { + "chart": "Grievance Type", + "width": "Half" + }, + { + "chart": "Training Type", + "width": "Half" + }, + { + "chart": "Y-O-Y Transfers", + "width": "Half" + }, + { + "chart": "Y-O-Y Promotions", + "width": "Half" + } + ], + "creation": "2022-08-21 12:07:21.614561", + "dashboard_name": "Employee Lifecycle", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2022-08-22 11:05:25.627559", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Lifecycle", + "owner": "Administrator" +} \ No newline at end of file diff --git a/hrms/hr/hr_dashboard/expense_claims/expense_claims.json b/hrms/hr/hr_dashboard/expense_claims/expense_claims.json new file mode 100644 index 0000000..f56321a --- /dev/null +++ b/hrms/hr/hr_dashboard/expense_claims/expense_claims.json @@ -0,0 +1,43 @@ +{ + "cards": [ + { + "card": "Expense Claims (This Month)" + }, + { + "card": "Approved Claims (This Month)" + }, + { + "card": "Rejected Claims (This Month)" + } + ], + "charts": [ + { + "chart": "Expense Claims", + "width": "Full" + }, + { + "chart": "Claims by Type", + "width": "Half" + }, + { + "chart": "Employee Advance Status", + "width": "Half" + }, + { + "chart": "Department wise Expense Claims", + "width": "Full" + } + ], + "creation": "2022-08-31 23:00:07.341160", + "dashboard_name": "Expense Claims", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2022-09-16 11:36:44.704033", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claims", + "owner": "Administrator" +} \ No newline at end of file diff --git a/hrms/hr/hr_dashboard/human_resource/human_resource.json b/hrms/hr/hr_dashboard/human_resource/human_resource.json new file mode 100644 index 0000000..0cddec4 --- /dev/null +++ b/hrms/hr/hr_dashboard/human_resource/human_resource.json @@ -0,0 +1,65 @@ +{ + "cards": [ + { + "card": "Total Employees" + }, + { + "card": "New Hires (This Year)" + }, + { + "card": "Employee Exits (This Year)" + }, + { + "card": "Employees Joining (Next Quarter)" + }, + { + "card": "Employees Relieving (Next Quarter)" + } + ], + "charts": [ + { + "chart": "Hiring vs Attrition Count", + "width": "Full" + }, + { + "chart": "Employees by Age", + "width": "Full" + }, + { + "chart": "Gender Diversity Ratio", + "width": "Half" + }, + { + "chart": "Employees by Type", + "width": "Half" + }, + { + "chart": "Employees by Grade", + "width": "Half" + }, + { + "chart": "Employees by Branch", + "width": "Half" + }, + { + "chart": "Designation Wise Employee Count", + "width": "Half" + }, + { + "chart": "Department Wise Employee Count", + "width": "Half" + } + ], + "creation": "2020-07-22 11:56:33.015888", + "dashboard_name": "Human Resource", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2022-08-22 19:10:33.594097", + "modified_by": "Administrator", + "module": "HR", + "name": "Human Resource", + "owner": "Administrator" +} \ No newline at end of file diff --git a/hrms/hr/hr_dashboard/recruitment/recruitment.json b/hrms/hr/hr_dashboard/recruitment/recruitment.json new file mode 100644 index 0000000..7e6fdb7 --- /dev/null +++ b/hrms/hr/hr_dashboard/recruitment/recruitment.json @@ -0,0 +1,75 @@ +{ + "cards": [ + { + "card": "Job Openings" + }, + { + "card": "Total Applicants (This month)" + }, + { + "card": "Accepted Job Applicants" + }, + { + "card": "Rejected Job Applicants" + }, + { + "card": "Job Offers (This Month)" + }, + { + "card": "Applicant-to-Hire Percentage" + }, + { + "card": "Job Offer Acceptance Rate" + } + ], + "charts": [ + { + "chart": "Job Applicant Pipeline", + "width": "Half" + }, + { + "chart": "Job Applicant Source", + "width": "Half" + }, + { + "chart": "Job Applicants by Country", + "width": "Half" + }, + { + "chart": "Job Application Status", + "width": "Half" + }, + { + "chart": "Job Offer Status", + "width": "Half" + }, + { + "chart": "Interview Status", + "width": "Half" + }, + { + "chart": "Job Application Frequency", + "width": "Full" + }, + { + "chart": "Department Wise Openings", + "width": "Half" + }, + { + "chart": "Designation Wise Openings", + "width": "Half" + } + ], + "creation": "2022-08-20 21:07:07.337973", + "dashboard_name": "Recruitment", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2022-08-22 12:18:44.691596", + "modified_by": "Administrator", + "module": "HR", + "name": "Recruitment", + "owner": "Administrator" +} \ No newline at end of file diff --git a/hrms/hr/module_onboarding/human_resource/human_resource.json b/hrms/hr/module_onboarding/human_resource/human_resource.json new file mode 100644 index 0000000..cd11bd1 --- /dev/null +++ b/hrms/hr/module_onboarding/human_resource/human_resource.json @@ -0,0 +1,47 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-05-14 11:51:45.050242", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources", + "idx": 0, + "is_complete": 0, + "modified": "2021-05-19 05:32:01.794628", + "modified_by": "Administrator", + "module": "HR", + "name": "Human Resource", + "owner": "Administrator", + "steps": [ + { + "step": "HR Settings" + }, + { + "step": "Create Holiday list" + }, + { + "step": "Create Employee" + }, + { + "step": "Data import" + }, + { + "step": "Create Leave Type" + }, + { + "step": "Create Leave Allocation" + }, + { + "step": "Create Leave Application" + } + ], + "subtitle": "Employee, Leaves, and more.", + "success_message": "The Human Resource Module is all set up!", + "title": "Let's Set Up the Human Resource Module. " +} \ No newline at end of file diff --git a/hrms/hr/notification/__init__.py b/hrms/hr/notification/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/notification/exit_interview_scheduled/__init__.py b/hrms/hr/notification/exit_interview_scheduled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json b/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json new file mode 100644 index 0000000..8323ef0 --- /dev/null +++ b/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json @@ -0,0 +1,29 @@ +{ + "attach_print": 0, + "channel": "Email", + "condition": "doc.date and doc.email and doc.docstatus != 2 and doc.status == 'Scheduled'", + "creation": "2021-12-05 22:11:47.263933", + "date_changed": "date", + "days_in_advance": 1, + "docstatus": 0, + "doctype": "Notification", + "document_type": "Exit Interview", + "enabled": 1, + "event": "Days Before", + "idx": 0, + "is_standard": 1, + "message": "
\n\t\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n
\n\t\t\t
\n\t\t\t\t{{_(\"Exit Interview Scheduled:\")}} {{ doc.name }}\n\t\t\t
\n\t\t
\n\n\n\t\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n
\n\t\t\t
\n\t\t\t\t
    \n\t\t\t\t\t
  • {{_(\"Employee\")}}: {{ doc.employee }} - {{ doc.employee_name }}
  • \n\t\t\t\t\t
  • {{_(\"Date\")}}: {{ doc.date }}
  • \n\t\t\t\t\t
  • {{_(\"Interviewers\")}}:
  • \n\t\t\t\t\t{% for entry in doc.interviewers %}\n\t\t\t\t\t\t
      \n\t\t\t\t\t\t\t
    • {{ entry.user }}
    • \n\t\t\t\t\t\t
    \n\t\t\t\t\t{% endfor %}\n\t\t\t\t\t
  • {{ _(\"Interview Document\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • \n\t\t\t\t
\n\t\t\t
\n\t\t
\n", + "modified": "2021-12-05 22:26:57.096159", + "modified_by": "Administrator", + "module": "HR", + "name": "Exit Interview Scheduled", + "owner": "Administrator", + "recipients": [ + { + "receiver_by_document_field": "email" + } + ], + "send_system_notification": 0, + "send_to_all_assignees": 1, + "subject": "Exit Interview Scheduled: {{ doc.name }}" +} \ No newline at end of file diff --git a/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md b/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md new file mode 100644 index 0000000..6d6db40 --- /dev/null +++ b/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md @@ -0,0 +1,37 @@ + + + + + + + + +
+
+

{{_("Exit Interview Scheduled:")}} {{ doc.name }}

+
+
+ + + + + + + + + +
+
+
    +
  • {{_("Employee")}}: {{ doc.employee }} - {{ doc.employee_name }}
  • +
  • {{_("Date")}}: {{ frappe.utils.formatdate(doc.date) }}
  • +
  • {{_("Interviewers")}}:
  • + {% for entry in doc.interviewers %} +
      +
    • {{ entry.user }}
    • +
    + {% endfor %} +
  • {{ _("Interview Document") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • +
+
+
diff --git a/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py b/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py new file mode 100644 index 0000000..5f697c9 --- /dev/null +++ b/hrms/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py @@ -0,0 +1,6 @@ +# import frappe + + +def get_context(context): + # do your magic here + pass diff --git a/hrms/hr/notification/training_feedback/__init__.py b/hrms/hr/notification/training_feedback/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/notification/training_feedback/training_feedback.html b/hrms/hr/notification/training_feedback/training_feedback.html new file mode 100644 index 0000000..b49662a --- /dev/null +++ b/hrms/hr/notification/training_feedback/training_feedback.html @@ -0,0 +1,6 @@ +

{{ _("Hello") }},

+ +

You attended training {{ frappe.utils.get_link_to_form( + "Training Event", doc.training_event) }}

+ +

{{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}

diff --git a/hrms/hr/notification/training_feedback/training_feedback.json b/hrms/hr/notification/training_feedback/training_feedback.json new file mode 100644 index 0000000..92b68a9 --- /dev/null +++ b/hrms/hr/notification/training_feedback/training_feedback.json @@ -0,0 +1,25 @@ +{ + "attach_print": 0, + "channel": "Email", + "creation": "2017-08-11 03:17:11.769210", + "days_in_advance": 0, + "docstatus": 0, + "doctype": "Notification", + "document_type": "Training Result", + "enabled": 1, + "event": "Submit", + "idx": 0, + "is_standard": 1, + "message": "

{{_(\"Training Event\")}}

\n

{{ message }}

\n\n

{{_(\"Details\")}}

\n{{_(\"Event Name\")}}: {{ name }}\n
{{_(\"Event Location\")}}: {{ location }}\n
{{_(\"Start Time\")}}: {{ start_time }}\n
{{_(\"End Time\")}}: {{ end_time }}\n
{{_(\"Attendance\")}}: {{ attendance }}\n", + "modified": "2017-08-11 04:26:58.194793", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Feedback", + "owner": "Administrator", + "recipients": [ + { + "email_by_document_field": "employee_emails" + } + ], + "subject": "Please Share your Feedback For {{ doc.training_event }}" +} \ No newline at end of file diff --git a/hrms/hr/notification/training_feedback/training_feedback.md b/hrms/hr/notification/training_feedback/training_feedback.md new file mode 100644 index 0000000..bcadf7d --- /dev/null +++ b/hrms/hr/notification/training_feedback/training_feedback.md @@ -0,0 +1,9 @@ +

{{_("Training Event")}}

+

{{ message }}

+ +

{{_("Details")}}

+{{_("Event Name")}}: {{ name }} +
{{_("Event Location")}}: {{ location }} +
{{_("Start Time")}}: {{ start_time }} +
{{_("End Time")}}: {{ end_time }} +
{{_("Attendance")}}: {{ attendance }} diff --git a/hrms/hr/notification/training_feedback/training_feedback.py b/hrms/hr/notification/training_feedback/training_feedback.py new file mode 100644 index 0000000..02e3e93 --- /dev/null +++ b/hrms/hr/notification/training_feedback/training_feedback.py @@ -0,0 +1,3 @@ +def get_context(context): + # do your magic here + pass diff --git a/hrms/hr/notification/training_scheduled/__init__.py b/hrms/hr/notification/training_scheduled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/notification/training_scheduled/training_scheduled.html b/hrms/hr/notification/training_scheduled/training_scheduled.html new file mode 100644 index 0000000..50f6d07 --- /dev/null +++ b/hrms/hr/notification/training_scheduled/training_scheduled.html @@ -0,0 +1,44 @@ + + + + + + + + +
+
+ {{_("Training Event:")}} {{ doc.event_name }} +
+
+ + + + + + + + + +
+
+ {{ doc.introduction }} +
    +
  • {{_("Event Location")}}: {{ doc.location }}
  • + {% set start = frappe.utils.get_datetime(doc.start_time) %} + {% set end = frappe.utils.get_datetime(doc.end_time) %} + {% if start.date() == end.date() %} +
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • +
  • + {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} +
  • + {% else %} +
  • {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • +
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • + {% endif %} +
  • {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • +
+
+
diff --git a/hrms/hr/notification/training_scheduled/training_scheduled.json b/hrms/hr/notification/training_scheduled/training_scheduled.json new file mode 100644 index 0000000..f365003 --- /dev/null +++ b/hrms/hr/notification/training_scheduled/training_scheduled.json @@ -0,0 +1,28 @@ +{ + "attach_print": 0, + "channel": "Email", + "condition": "", + "creation": "2017-08-11 03:13:40.519614", + "days_in_advance": 0, + "docstatus": 0, + "doctype": "Notification", + "document_type": "Training Event", + "enabled": 1, + "event": "Submit", + "idx": 0, + "is_standard": 1, + "message": "\n \n \n \n \n \n \n \n
\n
\n {{_(\"Training Event:\")}} {{ doc.event_name }}\n
\n
\n\n\n \n \n \n \n \n \n \n
\n
\n {{ doc.introduction }}\n
    \n
  • {{_(\"Event Location\")}}: {{ doc.location }}
  • \n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n
  • {{_(\"Date\")}}: {{ start.strftime(\"%A, %d %b %Y\") }}
  • \n
  • \n {{_(\"Timing\")}}: {{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}\n
  • \n {% else %}\n
  • \n {{_(\"Start Time\")}}: {{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n
  • {{_(\"End Time\")}}: {{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}
  • \n {% endif %}\n
  • {{ _(\"Event Link\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • \n {% if doc.is_mandatory %}\n
  • {{ _(\"Note: This Training Event is mandatory\") }}
  • \n {% endif %}\n
\n
\n
", + "modified": "2021-06-16 14:08:12.933367", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Scheduled", + "owner": "Administrator", + "recipients": [ + { + "receiver_by_document_field": "employee_emails" + } + ], + "send_system_notification": 0, + "send_to_all_assignees": 0, + "subject": "Training Scheduled: {{ doc.name }}" +} \ No newline at end of file diff --git a/hrms/hr/notification/training_scheduled/training_scheduled.md b/hrms/hr/notification/training_scheduled/training_scheduled.md new file mode 100644 index 0000000..b9ba846 --- /dev/null +++ b/hrms/hr/notification/training_scheduled/training_scheduled.md @@ -0,0 +1,47 @@ + + + + + + + + +
+
+ {{_("Training Event:")}} {{ doc.event_name }} +
+
+ + + + + + + + + +
+
+ {{ doc.introduction }} +
    +
  • {{_("Event Location")}}: {{ doc.location }}
  • + {% set start = frappe.utils.get_datetime(doc.start_time) %} + {% set end = frappe.utils.get_datetime(doc.end_time) %} + {% if start.date() == end.date() %} +
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • +
  • + {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} +
  • + {% else %} +
  • + {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • +
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }}
  • + {% endif %} +
  • {{ _("Event Link") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • + {% if doc.is_mandatory %} +
  • {{ _("Note: This Training Event is mandatory") }}
  • + {% endif %} +
+
+
diff --git a/hrms/hr/notification/training_scheduled/training_scheduled.py b/hrms/hr/notification/training_scheduled/training_scheduled.py new file mode 100644 index 0000000..02e3e93 --- /dev/null +++ b/hrms/hr/notification/training_scheduled/training_scheduled.py @@ -0,0 +1,3 @@ +def get_context(context): + # do your magic here + pass diff --git a/hrms/hr/number_card/accepted_job_applicants/accepted_job_applicants.json b/hrms/hr/number_card/accepted_job_applicants/accepted_job_applicants.json new file mode 100644 index 0000000..6d09fc8 --- /dev/null +++ b/hrms/hr/number_card/accepted_job_applicants/accepted_job_applicants.json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#29CD42", + "creation": "2022-08-20 21:26:43.114245", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Job Applicant", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Job Applicant\",\"status\",\"=\",\"Accepted\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Accepted Job Applicants", + "modified": "2022-08-20 21:30:34.366431", + "modified_by": "Administrator", + "module": "HR", + "name": "Accepted Job Applicants", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/applicant_to_hire_percentage/applicant_to_hire_percentage.json b/hrms/hr/number_card/applicant_to_hire_percentage/applicant_to_hire_percentage.json new file mode 100644 index 0000000..7f339fb --- /dev/null +++ b/hrms/hr/number_card/applicant_to_hire_percentage/applicant_to_hire_percentage.json @@ -0,0 +1,21 @@ +{ + "creation": "2022-08-21 21:05:23.450263", + "docstatus": 0, + "doctype": "Number Card", + "filters_json": "null", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Applicant-to-Hire Percentage", + "method": "hrms.hr.doctype.job_applicant.job_applicant.get_applicant_to_hire_percentage", + "modified": "2022-08-21 21:05:37.350629", + "modified_by": "Administrator", + "module": "HR", + "name": "Applicant-to-Hire Percentage", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Custom" +} \ No newline at end of file diff --git a/hrms/hr/number_card/approved_claims_(this_month)/approved_claims_(this_month).json b/hrms/hr/number_card/approved_claims_(this_month)/approved_claims_(this_month).json new file mode 100644 index 0000000..aa214e6 --- /dev/null +++ b/hrms/hr/number_card/approved_claims_(this_month)/approved_claims_(this_month).json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#29CD42", + "creation": "2022-08-31 23:03:18.120203", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Expense Claim", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Expense Claim\",\"approval_status\",\"=\",\"Approved\",false],[\"Expense Claim\",\"posting_date\",\"Timespan\",\"this month\",false],[\"Expense Claim\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Approved Claims (This Month)", + "modified": "2022-09-16 11:47:03.683495", + "modified_by": "Administrator", + "module": "HR", + "name": "Approved Claims (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/early_exit_(this_month)/early_exit_(this_month).json b/hrms/hr/number_card/early_exit_(this_month)/early_exit_(this_month).json new file mode 100644 index 0000000..4087052 --- /dev/null +++ b/hrms/hr/number_card/early_exit_(this_month)/early_exit_(this_month).json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#CB2929", + "creation": "2022-08-21 14:20:48.708808", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Attendance", + "dynamic_filters_json": "[[\"Attendance\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Attendance\",\"docstatus\",\"=\",\"1\",false],[\"Attendance\",\"early_exit\",\"=\",1,false],[\"Attendance\",\"attendance_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Early Exit (This Month)", + "modified": "2022-08-21 17:59:40.993874", + "modified_by": "Administrator", + "module": "HR", + "name": "Early Exit (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/employee_exits_(this_year)/employee_exits_(this_year).json b/hrms/hr/number_card/employee_exits_(this_year)/employee_exits_(this_year).json new file mode 100644 index 0000000..89a0d9a --- /dev/null +++ b/hrms/hr/number_card/employee_exits_(this_year)/employee_exits_(this_year).json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-22 11:56:32.947790", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"relieving_date\",\"Timespan\",\"this year\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Employee Exits (This Year)", + "modified": "2022-08-22 12:07:49.705454", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Exits (This Year)", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Yearly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/employees_joining_(next_quarter)/employees_joining_(next_quarter).json b/hrms/hr/number_card/employees_joining_(next_quarter)/employees_joining_(next_quarter).json new file mode 100644 index 0000000..1d4c85c --- /dev/null +++ b/hrms/hr/number_card/employees_joining_(next_quarter)/employees_joining_(next_quarter).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-22 12:25:05.129659", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"date_of_joining\",\"Timespan\",\"next quarter\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Employees Joining (Next Quarter)", + "modified": "2022-08-22 12:25:13.302161", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees Joining (Next Quarter)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 0, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/employees_relieving_(next_quarter)/employees_relieving_(next_quarter).json b/hrms/hr/number_card/employees_relieving_(next_quarter)/employees_relieving_(next_quarter).json new file mode 100644 index 0000000..e8a5126 --- /dev/null +++ b/hrms/hr/number_card/employees_relieving_(next_quarter)/employees_relieving_(next_quarter).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-22 12:25:36.324395", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"relieving_date\",\"Timespan\",\"this quarter\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Employees Relieving (Next Quarter)", + "modified": "2022-08-22 12:26:42.672235", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees Relieving (Next Quarter)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 0, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/expense_claims_(this_month)/expense_claims_(this_month).json b/hrms/hr/number_card/expense_claims_(this_month)/expense_claims_(this_month).json new file mode 100644 index 0000000..8993e34 --- /dev/null +++ b/hrms/hr/number_card/expense_claims_(this_month)/expense_claims_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-31 23:02:41.310392", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Expense Claim", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Expense Claim\",\"docstatus\",\"=\",\"1\",false],[\"Expense Claim\",\"posting_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Expense Claims (This Month)", + "modified": "2022-09-16 11:45:55.591335", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claims (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/job_offer_acceptance_rate/job_offer_acceptance_rate.json b/hrms/hr/number_card/job_offer_acceptance_rate/job_offer_acceptance_rate.json new file mode 100644 index 0000000..7ef0d3e --- /dev/null +++ b/hrms/hr/number_card/job_offer_acceptance_rate/job_offer_acceptance_rate.json @@ -0,0 +1,22 @@ +{ + "creation": "2022-08-21 21:21:00.295719", + "docstatus": 0, + "doctype": "Number Card", + "filters_config": "[{\n\tfieldname: \"company\",\n\tlabel: __(\"Company\"),\n\tfieldtype: \"Link\",\n\toptions: \"Company\",\n\tdefault: frappe.defaults.get_user_default(\"Company\")\n},\n{\n\tfieldname: \"department\",\n\tlabel: __(\"Department\"),\n\tfieldtype: \"Link\",\n\toptions: \"Department\"\n}]", + "filters_json": "{\"company\":\"Frappe\"}", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Job Offer Acceptance Rate", + "method": "hrms.hr.doctype.job_offer.job_offer.get_offer_acceptance_rate", + "modified": "2022-08-21 21:25:08.750550", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offer Acceptance Rate", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Custom" +} \ No newline at end of file diff --git a/hrms/hr/number_card/job_offers_(this_month)/job_offers_(this_month).json b/hrms/hr/number_card/job_offers_(this_month)/job_offers_(this_month).json new file mode 100644 index 0000000..0d35467 --- /dev/null +++ b/hrms/hr/number_card/job_offers_(this_month)/job_offers_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-20 21:38:34.963486", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Job Offer", + "dynamic_filters_json": "[[\"Job Offer\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Job Offer\",\"offer_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Job Offers (This Month)", + "modified": "2022-08-22 12:27:02.469892", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offers (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/job_openings/job_openings.json b/hrms/hr/number_card/job_openings/job_openings.json new file mode 100644 index 0000000..2d11cf1 --- /dev/null +++ b/hrms/hr/number_card/job_openings/job_openings.json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-20 21:24:35.929507", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Job Opening", + "dynamic_filters_json": "[[\"Job Opening\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Job Opening\",\"status\",\"=\",\"Open\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Job Openings", + "modified": "2022-08-20 23:55:32.154023", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Openings", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/late_entry_(this_month)/late_entry_(this_month).json b/hrms/hr/number_card/late_entry_(this_month)/late_entry_(this_month).json new file mode 100644 index 0000000..beea149 --- /dev/null +++ b/hrms/hr/number_card/late_entry_(this_month)/late_entry_(this_month).json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#CB2929", + "creation": "2022-08-21 14:20:15.678344", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Attendance", + "dynamic_filters_json": "[[\"Attendance\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Attendance\",\"docstatus\",\"=\",\"1\",false],[\"Attendance\",\"late_entry\",\"=\",1,false],[\"Attendance\",\"attendance_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Late Entry (This Month)", + "modified": "2022-08-21 18:57:35.451376", + "modified_by": "Administrator", + "module": "HR", + "name": "Late Entry (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/new_hires_(this_year)/new_hires_(this_year).json b/hrms/hr/number_card/new_hires_(this_year)/new_hires_(this_year).json new file mode 100644 index 0000000..d1d7a9e --- /dev/null +++ b/hrms/hr/number_card/new_hires_(this_year)/new_hires_(this_year).json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-22 11:56:32.914057", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"date_of_joining\",\"Timespan\",\"this year\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "New Hires (This Year)", + "modified": "2022-08-22 12:07:43.692448", + "modified_by": "Administrator", + "module": "HR", + "name": "New Hires (This Year)", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Yearly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/onboardings_(this_month)/onboardings_(this_month).json b/hrms/hr/number_card/onboardings_(this_month)/onboardings_(this_month).json new file mode 100644 index 0000000..8f6cf1e --- /dev/null +++ b/hrms/hr/number_card/onboardings_(this_month)/onboardings_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-21 13:11:40.726412", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee Onboarding", + "dynamic_filters_json": "[[\"Employee Onboarding\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Onboarding\",\"boarding_begins_on\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Onboardings (This Month)", + "modified": "2022-08-21 13:12:44.833053", + "modified_by": "Administrator", + "module": "HR", + "name": "Onboardings (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/promotions_(this_month)/promotions_(this_month).json b/hrms/hr/number_card/promotions_(this_month)/promotions_(this_month).json new file mode 100644 index 0000000..5f6555e --- /dev/null +++ b/hrms/hr/number_card/promotions_(this_month)/promotions_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-21 13:13:59.611516", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee Promotion", + "dynamic_filters_json": "[[\"Employee Promotion\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Promotion\",\"promotion_date\",\"Timespan\",\"last month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Promotions (This Month)", + "modified": "2022-08-21 14:13:59.611516", + "modified_by": "Administrator", + "module": "HR", + "name": "Promotions (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/rejected_claims_(this_month)/rejected_claims_(this_month).json b/hrms/hr/number_card/rejected_claims_(this_month)/rejected_claims_(this_month).json new file mode 100644 index 0000000..449f5b6 --- /dev/null +++ b/hrms/hr/number_card/rejected_claims_(this_month)/rejected_claims_(this_month).json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#CB2929", + "creation": "2022-08-31 23:03:51.721886", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Expense Claim", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Expense Claim\",\"approval_status\",\"=\",\"Rejected\",false],[\"Expense Claim\",\"posting_date\",\"Timespan\",\"this month\",false],[\"Expense Claim\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Rejected Claims (This Month)", + "modified": "2022-09-16 11:46:52.279901", + "modified_by": "Administrator", + "module": "HR", + "name": "Rejected Claims (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/rejected_job_applicants/rejected_job_applicants.json b/hrms/hr/number_card/rejected_job_applicants/rejected_job_applicants.json new file mode 100644 index 0000000..05a9a2f --- /dev/null +++ b/hrms/hr/number_card/rejected_job_applicants/rejected_job_applicants.json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#CB2929", + "creation": "2022-08-20 21:27:56.516752", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Job Applicant", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Job Applicant\",\"status\",\"=\",\"Rejected\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Rejected Job Applicants", + "modified": "2022-08-20 21:25:16.380640", + "modified_by": "Administrator", + "module": "HR", + "name": "Rejected Job Applicants", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/separations_(this_month)/separations_(this_month).json b/hrms/hr/number_card/separations_(this_month)/separations_(this_month).json new file mode 100644 index 0000000..3d18c25 --- /dev/null +++ b/hrms/hr/number_card/separations_(this_month)/separations_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-21 13:13:09.342388", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee Separation", + "dynamic_filters_json": "[[\"Employee Separation\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Separation\",\"boarding_begins_on\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Separations (This Month)", + "modified": "2022-08-21 14:13:09.342388", + "modified_by": "Administrator", + "module": "HR", + "name": "Separations (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/total_absent_(this_month)/total_absent_(this_month).json b/hrms/hr/number_card/total_absent_(this_month)/total_absent_(this_month).json new file mode 100644 index 0000000..fc4dcab --- /dev/null +++ b/hrms/hr/number_card/total_absent_(this_month)/total_absent_(this_month).json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#CB2929", + "creation": "2022-08-21 14:19:21.786541", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Attendance", + "dynamic_filters_json": "[[\"Attendance\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Attendance\",\"docstatus\",\"=\",\"1\",false],[\"Attendance\",\"status\",\"=\",\"Absent\",false],[\"Attendance\",\"attendance_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Absent (This Month)", + "modified": "2022-08-21 18:57:29.171250", + "modified_by": "Administrator", + "module": "HR", + "name": "Total Absent (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/total_applicants_(this_month)/total_applicants_(this_month).json b/hrms/hr/number_card/total_applicants_(this_month)/total_applicants_(this_month).json new file mode 100644 index 0000000..efcd7c3 --- /dev/null +++ b/hrms/hr/number_card/total_applicants_(this_month)/total_applicants_(this_month).json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-22 11:56:32.977716", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Job Applicant", + "dynamic_filters_json": "", + "filters_json": "[[\"Job Applicant\",\"creation\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Applicants (This Month)", + "modified": "2022-08-22 11:48:58.881371", + "modified_by": "Administrator", + "module": "HR", + "name": "Total Applicants (This month)", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/total_employees/total_employees.json b/hrms/hr/number_card/total_employees/total_employees.json new file mode 100644 index 0000000..da620dc --- /dev/null +++ b/hrms/hr/number_card/total_employees/total_employees.json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-22 11:56:32.874849", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee", + "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Active Employees", + "modified": "2022-08-22 11:59:22.098139", + "modified_by": "Administrator", + "module": "HR", + "name": "Total Employees", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/total_present_(this_month)/total_present_(this_month).json b/hrms/hr/number_card/total_present_(this_month)/total_present_(this_month).json new file mode 100644 index 0000000..482a3a0 --- /dev/null +++ b/hrms/hr/number_card/total_present_(this_month)/total_present_(this_month).json @@ -0,0 +1,25 @@ +{ + "aggregate_function_based_on": "", + "color": "#29CD42", + "creation": "2022-08-21 14:18:52.965856", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Attendance", + "dynamic_filters_json": "[[\"Attendance\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Attendance\",\"docstatus\",\"=\",\"1\",false],[\"Attendance\",\"status\",\"=\",\"Present\",false],[\"Attendance\",\"attendance_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Present (This Month)", + "modified": "2022-08-21 18:57:19.140144", + "modified_by": "Administrator", + "module": "HR", + "name": "Total Present (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/trainings_(this_month)/trainings_(this_month).json b/hrms/hr/number_card/trainings_(this_month)/trainings_(this_month).json new file mode 100644 index 0000000..b9fb6d3 --- /dev/null +++ b/hrms/hr/number_card/trainings_(this_month)/trainings_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-21 13:15:23.077773", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Training Event", + "dynamic_filters_json": "[[\"Training Event\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Training Event\",\"start_time\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Trainings (This Month)", + "modified": "2022-08-21 14:15:23.077773", + "modified_by": "Administrator", + "module": "HR", + "name": "Trainings (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/number_card/transfers_(this_month)/transfers_(this_month).json b/hrms/hr/number_card/transfers_(this_month)/transfers_(this_month).json new file mode 100644 index 0000000..9ea0069 --- /dev/null +++ b/hrms/hr/number_card/transfers_(this_month)/transfers_(this_month).json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "", + "creation": "2022-08-21 13:14:36.205338", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee Transfer", + "dynamic_filters_json": "[[\"Employee Transfer\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Transfer\",\"transfer_date\",\"Timespan\",\"this month\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Transfers (This Month)", + "modified": "2022-08-21 15:14:36.205338", + "modified_by": "Administrator", + "module": "HR", + "name": "Transfers (This Month)", + "owner": "Administrator", + "parent_document_type": "", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_department/create_department.json b/hrms/hr/onboarding_step/create_department/create_department.json new file mode 100644 index 0000000..66a54cf --- /dev/null +++ b/hrms/hr/onboarding_step/create_department/create_department.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:44:34.682115", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:26.448420", + "modified_by": "Administrator", + "name": "Create Department", + "owner": "Administrator", + "reference_document": "Department", + "show_full_form": 0, + "title": "Create Department", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_designation/create_designation.json b/hrms/hr/onboarding_step/create_designation/create_designation.json new file mode 100644 index 0000000..c4e9cc7 --- /dev/null +++ b/hrms/hr/onboarding_step/create_designation/create_designation.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:45:07.514193", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:41.500795", + "modified_by": "Administrator", + "name": "Create Designation", + "owner": "Administrator", + "reference_document": "Designation", + "show_full_form": 0, + "title": "Create Designation", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_employee/create_employee.json b/hrms/hr/onboarding_step/create_employee/create_employee.json new file mode 100644 index 0000000..4782818 --- /dev/null +++ b/hrms/hr/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,21 @@ +{ + "action": "Show Form Tour", + "action_label": "Show Tour", + "creation": "2020-05-14 11:43:25.561152", + "description": "

Employee

\n\nAn individual who works and is recognized for his rights and duties in your company is your Employee. You can manage the Employee master. It captures the demographic, personal and professional details, joining and leave details, etc.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-05-19 04:50:02.240321", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Create Employee", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/hrms/hr/onboarding_step/create_holiday_list/create_holiday_list.json new file mode 100644 index 0000000..a08e85f --- /dev/null +++ b/hrms/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -0,0 +1,21 @@ +{ + "action": "Show Form Tour", + "action_label": "Show Tour", + "creation": "2020-05-28 11:47:34.700174", + "description": "

Holiday List.

\n\nHoliday List is a list which contains the dates of holidays. Most organizations have a standard Holiday List for their employees. However, some of them may have different holiday lists based on different Locations or Departments. In ERPNext, you can configure multiple Holiday Lists.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-05-19 04:19:52.305199", + "modified_by": "Administrator", + "name": "Create Holiday list", + "owner": "Administrator", + "reference_document": "Holiday List", + "show_form_tour": 0, + "show_full_form": 1, + "title": "Create Holiday List", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json b/hrms/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json new file mode 100644 index 0000000..0b0ce3f --- /dev/null +++ b/hrms/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json @@ -0,0 +1,21 @@ +{ + "action": "Show Form Tour", + "action_label": "Show Tour", + "creation": "2020-05-14 11:48:56.123718", + "description": "

Leave Allocation

\n\nLeave Allocation enables you to allocate a specific number of leaves of a particular type to an Employee so that, an employee will be able to create a Leave Application only if Leaves are allocated. ", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-05-19 04:22:34.220238", + "modified_by": "Administrator", + "name": "Create Leave Allocation", + "owner": "Administrator", + "reference_document": "Leave Allocation", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Create Leave Allocation", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_leave_application/create_leave_application.json b/hrms/hr/onboarding_step/create_leave_application/create_leave_application.json new file mode 100644 index 0000000..af63aa5 --- /dev/null +++ b/hrms/hr/onboarding_step/create_leave_application/create_leave_application.json @@ -0,0 +1,21 @@ +{ + "action": "Show Form Tour", + "action_label": "Show Tour", + "creation": "2020-05-14 11:49:45.400764", + "description": "

Leave Application

\n\nLeave Application is a formal document created by an Employee to apply for Leaves for a particular time period based on there leave allocation and leave type according to there need.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-05-19 04:39:09.893474", + "modified_by": "Administrator", + "name": "Create Leave Application", + "owner": "Administrator", + "reference_document": "Leave Application", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Create Leave Application", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/create_leave_type/create_leave_type.json b/hrms/hr/onboarding_step/create_leave_type/create_leave_type.json new file mode 100644 index 0000000..397f5cd --- /dev/null +++ b/hrms/hr/onboarding_step/create_leave_type/create_leave_type.json @@ -0,0 +1,21 @@ +{ + "action": "Show Form Tour", + "action_label": "Show Tour", + "creation": "2020-05-27 11:17:31.119312", + "description": "

Leave Type

\n\nLeave type is defined based on many factors and features like encashment, earned leaves, partially paid, without pay and, a lot more. To check other options and to define your leave type click on Show Tour.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-05-19 04:32:48.135406", + "modified_by": "Administrator", + "name": "Create Leave Type", + "owner": "Administrator", + "reference_document": "Leave Type", + "show_form_tour": 0, + "show_full_form": 1, + "title": "Create Leave Type", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/data_import/data_import.json b/hrms/hr/onboarding_step/data_import/data_import.json new file mode 100644 index 0000000..ac343c6 --- /dev/null +++ b/hrms/hr/onboarding_step/data_import/data_import.json @@ -0,0 +1,21 @@ +{ + "action": "Watch Video", + "action_label": "", + "creation": "2021-05-19 05:29:16.809610", + "description": "

Data Import

\n\nData import is the tool to migrate your existing data like Employee, Customer, Supplier, and a lot more to our ERPNext system.\nGo through the video for a detailed explanation of this tool.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-05-19 05:29:16.809610", + "modified_by": "Administrator", + "name": "Data import", + "owner": "Administrator", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Data Import", + "validate_action": 1, + "video_url": "https://www.youtube.com/watch?v=DQyqeurPI64" +} \ No newline at end of file diff --git a/hrms/hr/onboarding_step/hr_settings/hr_settings.json b/hrms/hr/onboarding_step/hr_settings/hr_settings.json new file mode 100644 index 0000000..355664f --- /dev/null +++ b/hrms/hr/onboarding_step/hr_settings/hr_settings.json @@ -0,0 +1,21 @@ +{ + "action": "Show Form Tour", + "action_label": "Explore", + "creation": "2020-05-28 13:13:52.427711", + "description": "

HR Settings

\n\nHr Settings consists of major settings related to Employee Lifecycle, Leave Management, etc. Click on Explore, to explore Hr Settings.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2021-05-18 07:02:05.747548", + "modified_by": "Administrator", + "name": "HR Settings", + "owner": "Administrator", + "reference_document": "HR Settings", + "show_form_tour": 0, + "show_full_form": 0, + "title": "HR Settings", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/hr/page/__init__.py b/hrms/hr/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/page/organizational_chart/__init__.py b/hrms/hr/page/organizational_chart/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/page/organizational_chart/organizational_chart.js b/hrms/hr/page/organizational_chart/organizational_chart.js new file mode 100644 index 0000000..239f3c2 --- /dev/null +++ b/hrms/hr/page/organizational_chart/organizational_chart.js @@ -0,0 +1,23 @@ +frappe.pages['organizational-chart'].on_page_load = function(wrapper) { + frappe.ui.make_app_page({ + parent: wrapper, + title: __('Organizational Chart'), + single_column: true + }); + + $(wrapper).bind('show', () => { + frappe.require('hierarchy-chart.bundle.js', () => { + let organizational_chart = undefined; + let method = 'hrms.hr.page.organizational_chart.organizational_chart.get_children'; + + if (frappe.is_mobile()) { + organizational_chart = new erpnext.HierarchyChartMobile('Employee', wrapper, method); + } else { + organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method); + } + + frappe.breadcrumbs.add('HR'); + organizational_chart.show(); + }); + }); +}; diff --git a/hrms/hr/page/organizational_chart/organizational_chart.json b/hrms/hr/page/organizational_chart/organizational_chart.json new file mode 100644 index 0000000..d802781 --- /dev/null +++ b/hrms/hr/page/organizational_chart/organizational_chart.json @@ -0,0 +1,26 @@ +{ + "content": null, + "creation": "2021-05-25 10:53:10.107241", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2021-05-25 10:53:18.201931", + "modified_by": "Administrator", + "module": "HR", + "name": "organizational-chart", + "owner": "Administrator", + "page_name": "Organizational Chart", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Organizational Chart" +} \ No newline at end of file diff --git a/hrms/hr/page/organizational_chart/organizational_chart.py b/hrms/hr/page/organizational_chart/organizational_chart.py new file mode 100644 index 0000000..3674912 --- /dev/null +++ b/hrms/hr/page/organizational_chart/organizational_chart.py @@ -0,0 +1,45 @@ +import frappe + + +@frappe.whitelist() +def get_children(parent=None, company=None, exclude_node=None): + filters = [["status", "!=", "Left"]] + if company and company != "All Companies": + filters.append(["company", "=", company]) + + if parent and company and parent != company: + filters.append(["reports_to", "=", parent]) + else: + filters.append(["reports_to", "=", ""]) + + if exclude_node: + filters.append(["name", "!=", exclude_node]) + + employees = frappe.get_list( + "Employee", + fields=["employee_name as name", "name as id", "reports_to", "image", "designation as title"], + filters=filters, + order_by="name", + ) + + for employee in employees: + is_expandable = frappe.db.count("Employee", filters={"reports_to": employee.get("id")}) + employee.connections = get_connections(employee.id) + employee.expandable = 1 if is_expandable else 0 + + return employees + + +def get_connections(employee): + num_connections = 0 + + nodes_to_expand = frappe.get_list("Employee", filters=[["reports_to", "=", employee]]) + num_connections += len(nodes_to_expand) + + while nodes_to_expand: + parent = nodes_to_expand.pop(0) + descendants = frappe.get_list("Employee", filters=[["reports_to", "=", parent.name]]) + num_connections += len(descendants) + nodes_to_expand.extend(descendants) + + return num_connections diff --git a/hrms/hr/page/team_updates/__init__.py b/hrms/hr/page/team_updates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/page/team_updates/team_update_row.html b/hrms/hr/page/team_updates/team_update_row.html new file mode 100644 index 0000000..4f36754 --- /dev/null +++ b/hrms/hr/page/team_updates/team_update_row.html @@ -0,0 +1,15 @@ +
+
+ {%= date_sep || "" %}
+
+
+
+ {{ avatar }} +
+
+ {{ content }} +
+
+
+
diff --git a/hrms/hr/page/team_updates/team_updates.css b/hrms/hr/page/team_updates/team_updates.css new file mode 100644 index 0000000..d37e782 --- /dev/null +++ b/hrms/hr/page/team_updates/team_updates.css @@ -0,0 +1,57 @@ +.date-indicator { + background:none; + font-size:12px; + vertical-align:middle; + font-weight:bold; + color:#6c7680; +} +.date-indicator::after { + margin:0 -4px 0 12px; + content:''; + display:inline-block; + height:8px; + width:8px; + border-radius:8px; + background: #d1d8dd; +} + +.date-indicator.blue { + color: #5e64ff; +} + +.date-indicator.blue::after { + background: #5e64ff; +} + +.activity-row { +} + +.activity-message { + border-left: 1px solid #d1d8dd; +} + +.activity-message .row { + padding: 15px; + margin-right: 0px; + border-bottom: 1px solid #d1d8dd; +} + +.activity-row:last-child .activity-message .row { + border-bottom: none; +} + +.activity-row .content { + padding-left: 0px; + padding-right: 30px; +} + +.activity-date { + padding: 15px; + padding-right: 0px; + z-index: 1; +} + +.for-more { + border-top: 1px solid #d1d8dd; + padding: 10px; +} diff --git a/hrms/hr/page/team_updates/team_updates.js b/hrms/hr/page/team_updates/team_updates.js new file mode 100644 index 0000000..3ea7946 --- /dev/null +++ b/hrms/hr/page/team_updates/team_updates.js @@ -0,0 +1,80 @@ +frappe.pages['team-updates'].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: __('Team Updates'), + single_column: true + }); + + frappe.team_updates.make(page); + frappe.team_updates.run(); + + if(frappe.model.can_read('Daily Work Summary Group')) { + page.add_menu_item(__('Daily Work Summary Group'), function() { + frappe.set_route('Form', 'Daily Work Summary Group'); + }); + } +} + +frappe.team_updates = { + start: 0, + make: function(page) { + var me = frappe.team_updates; + me.page = page; + me.body = $('
').appendTo(me.page.main); + me.more = $('
').appendTo(me.page.main) + .find('.btn-more').on('click', function() { + me.start += 40; + me.run(); + }); + }, + run: function() { + var me = frappe.team_updates; + frappe.call({ + method: 'hrms.hr.page.team_updates.team_updates.get_data', + args: { + start: me.start + }, + callback: function(r) { + if (r.message && r.message.length > 0) { + r.message.forEach(function(d) { + me.add_row(d); + }); + } else { + frappe.show_alert({message: __('No more updates'), indicator: 'gray'}); + me.more.parent().addClass('hidden'); + } + } + }); + }, + add_row: function(data) { + var me = frappe.team_updates; + + data.by = frappe.user.full_name(data.sender); + data.avatar = frappe.avatar(data.sender); + data.when = comment_when(data.creation); + + var date = frappe.datetime.str_to_obj(data.creation); + var last = me.last_feed_date; + + if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) { + var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); + var pdate; + if(diff < 1) { + pdate = 'Today'; + } else if(diff < 2) { + pdate = 'Yesterday'; + } else { + pdate = frappe.datetime.global_date_format(date); + } + data.date_sep = pdate; + data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator"; + } else { + data.date_sep = null; + data.date_class = ""; + } + me.last_feed_date = date; + + $(frappe.render_template('team_update_row', data)).appendTo(me.body); + } +} diff --git a/hrms/hr/page/team_updates/team_updates.json b/hrms/hr/page/team_updates/team_updates.json new file mode 100644 index 0000000..167c67f --- /dev/null +++ b/hrms/hr/page/team_updates/team_updates.json @@ -0,0 +1,25 @@ +{ + "content": null, + "creation": "2017-01-31 11:02:31.614045", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2017-01-31 11:25:01.983200", + "modified_by": "Administrator", + "module": "HR", + "name": "team-updates", + "owner": "Administrator", + "page_name": "team-updates", + "roles": [ + { + "role": "Employee" + }, + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "title": "Team Updates" +} \ No newline at end of file diff --git a/hrms/hr/page/team_updates/team_updates.py b/hrms/hr/page/team_updates/team_updates.py new file mode 100644 index 0000000..432995c --- /dev/null +++ b/hrms/hr/page/team_updates/team_updates.py @@ -0,0 +1,25 @@ +from email_reply_parser import EmailReplyParser + +import frappe + + +@frappe.whitelist() +def get_data(start=0): + # frappe.only_for('Employee', 'System Manager') + data = frappe.get_all( + "Communication", + fields=("content", "text_content", "sender", "creation"), + filters=dict(reference_doctype="Daily Work Summary"), + order_by="creation desc", + limit=40, + start=start, + ) + + for d in data: + d.sender_name = ( + frappe.db.get_value("Employee", {"user_id": d.sender}, "employee_name") or d.sender + ) + if d.text_content: + d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content)) + + return data diff --git a/hrms/hr/print_format/__init__.py b/hrms/hr/print_format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/print_format/job_offer/__init__.py b/hrms/hr/print_format/job_offer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/print_format/job_offer/job_offer.json b/hrms/hr/print_format/job_offer/job_offer.json new file mode 100644 index 0000000..0a92230 --- /dev/null +++ b/hrms/hr/print_format/job_offer/job_offer.json @@ -0,0 +1,21 @@ +{ + "align_labels_right": 0, + "creation": "2015-03-05 14:34:26.751210", + "custom_format": 1, + "disabled": 0, + "doc_type": "Job Offer", + "docstatus": 0, + "doctype": "Print Format", + "html": "{% set terms_exist = doc.terms and (doc.terms|striptags).strip() or \"\" %}\n\n{% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n{%- endif %}\n\n
\n

\n\n\nDate: {{ doc.offer_date }}\n

\n\nDear {{ doc.applicant_name }}, \n\n

\n\nWe are pleased to appoint you in the services of {{ doc.company }} on the terms and conditions detailed in this letter.\n\n

\n\nYour designation shall be {{ doc.designation }}.\n\n

\n\n\n\n{%- if doc.offer_terms -%}\n {%- for row in doc.offer_terms -%}\n {{ row.offer_term }}: {{ row.value }}\n\n
\n {%- endfor -%}\n{%- endif -%}\n\n
\n\n\n\n\nPlease read the detailed terms as below. If you have any queries, feel free to get in touch with us.\nWe look forward to your long and fruitful career association with our organisation.\nIf you decide to join us, 'Welcome to {{ doc.company }} !'\n\n

\n\n

\n\nYours truly,\n\n



\n\nAuthorized Signatory\n\n
\n\n{{ doc.company }}\n\n\n\n

\n
\n\n\n{% if terms_exist %}\n
{{ doc.terms }}
\n{% endif %}", + "idx": 0, + "line_breaks": 0, + "modified": "2018-02-15 03:03:55.844085", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offer", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/hrms/hr/print_format/standard_appointment_letter/__init__.py b/hrms/hr/print_format/standard_appointment_letter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.html b/hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.html new file mode 100644 index 0000000..87daafc --- /dev/null +++ b/hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.html @@ -0,0 +1,38 @@ +{%- from "templates/print_formats/standard_macros.html" import add_header -%} + +

Appointment Letter

+
+
+ {{ doc.applicant_name }}, +
+
+ Date: {{ doc.appointment_date }} +
+
+
+ {{ doc.introduction }} +
+
+
    + {% for content in doc.terms %} +
  • + + {{ content.title }}: {{ content.description }} + +
  • + {% endfor %} +
+
+
+Your sincerely,
+For {{ doc.company }} +
+ +
+ {{ doc.closing_notes }} +
+ +
+ ________________
+ {{ doc.applicant_name }} +
diff --git a/hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.json b/hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.json new file mode 100644 index 0000000..1813e71 --- /dev/null +++ b/hrms/hr/print_format/standard_appointment_letter/standard_appointment_letter.json @@ -0,0 +1,23 @@ +{ + "align_labels_right": 0, + "creation": "2019-12-26 15:22:44.200332", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Appointment Letter", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "idx": 0, + "line_breaks": 0, + "modified": "2020-01-21 17:24:16.705082", + "modified_by": "Administrator", + "module": "HR", + "name": "Standard Appointment Letter", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/hrms/hr/report/__init__.py b/hrms/hr/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/daily_work_summary_replies/__init__.py b/hrms/hr/report/daily_work_summary_replies/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.js b/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.js new file mode 100644 index 0000000..0980c69 --- /dev/null +++ b/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.js @@ -0,0 +1,21 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* eslint-disable */ +frappe.query_reports["Daily Work Summary Replies"] = { + "filters": [ + { + "fieldname":"group", + "label": __("Group"), + "fieldtype": "Link", + "options": "Daily Work Summary Group", + "reqd": 1 + }, + { + "fieldname": "range", + "label": __("Date Range"), + "fieldtype": "DateRange", + "reqd": 1 + } + ] +} diff --git a/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.json b/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.json new file mode 100644 index 0000000..04c8850 --- /dev/null +++ b/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.json @@ -0,0 +1,25 @@ +{ + "add_total_row": 0, + "creation": "2018-06-04 10:30:25.673452", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2018-06-04 10:44:04.694509", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary Replies", + "owner": "Administrator", + "ref_doctype": "Daily Work Summary", + "report_name": "Daily Work Summary Replies", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.py b/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.py new file mode 100644 index 0000000..48072fd --- /dev/null +++ b/hrms/hr/report/daily_work_summary_replies/daily_work_summary_replies.py @@ -0,0 +1,61 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + +from hrms.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group + + +def execute(filters=None): + if not filters.group: + return [], [] + columns, data = get_columns(), get_data(filters) + return columns, data + + +def get_columns(filters=None): + columns = [ + {"label": _("User"), "fieldname": "user", "fieldtype": "Data", "width": 300}, + { + "label": _("Replies"), + "fieldname": "count", + "fieldtype": "data", + "width": 100, + "align": "right", + }, + { + "label": _("Total"), + "fieldname": "total", + "fieldtype": "data", + "width": 100, + "align": "right", + }, + ] + return columns + + +def get_data(filters): + daily_summary_emails = frappe.get_all( + "Daily Work Summary", fields=["name"], filters=[["creation", "Between", filters.range]] + ) + daily_summary_emails = [d.get("name") for d in daily_summary_emails] + replies = frappe.get_all( + "Communication", + fields=["content", "text_content", "sender"], + filters=[ + ["reference_doctype", "=", "Daily Work Summary"], + ["reference_name", "in", daily_summary_emails], + ["communication_type", "=", "Communication"], + ["sent_or_received", "=", "Received"], + ], + order_by="creation asc", + ) + data = [] + total = len(daily_summary_emails) + for user in get_user_emails_from_group(filters.group): + user_name = frappe.get_value("User", user, "full_name") + count = len([d for d in replies if d.sender == user]) + data.append([user_name, count, total]) + return data diff --git a/hrms/hr/report/employee_advance_summary/__init__.py b/hrms/hr/report/employee_advance_summary/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_advance_summary/employee_advance_summary.js b/hrms/hr/report/employee_advance_summary/employee_advance_summary.js new file mode 100644 index 0000000..8de4af5 --- /dev/null +++ b/hrms/hr/report/employee_advance_summary/employee_advance_summary.js @@ -0,0 +1,40 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Employee Advance Summary"] = { + "filters": [ + { + "fieldname":"employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee", + "width": "80" + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today() + }, + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company" + }, + { + "fieldname":"status", + "label": __("Status"), + "fieldtype": "Select", + "options": "\nDraft\nPaid\nUnpaid\nClaimed\nCancelled" + } + ] +}; diff --git a/hrms/hr/report/employee_advance_summary/employee_advance_summary.json b/hrms/hr/report/employee_advance_summary/employee_advance_summary.json new file mode 100644 index 0000000..60afd59 --- /dev/null +++ b/hrms/hr/report/employee_advance_summary/employee_advance_summary.json @@ -0,0 +1,26 @@ +{ + "add_total_row": 1, + "apply_user_permissions": 1, + "creation": "2018-02-21 07:12:37.299923", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2018-02-22 13:33:41.532005", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Advance Summary", + "owner": "Administrator", + "ref_doctype": "Employee Advance", + "report_name": "Employee Advance Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "Expense Approver" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_advance_summary/employee_advance_summary.py b/hrms/hr/report/employee_advance_summary/employee_advance_summary.py new file mode 100644 index 0000000..29532f7 --- /dev/null +++ b/hrms/hr/report/employee_advance_summary/employee_advance_summary.py @@ -0,0 +1,105 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _, msgprint + + +def execute(filters=None): + if not filters: + filters = {} + + advances_list = get_advances(filters) + columns = get_columns() + + if not advances_list: + msgprint(_("No record found")) + return columns, advances_list + + data = [] + for advance in advances_list: + row = [ + advance.name, + advance.employee, + advance.company, + advance.posting_date, + advance.advance_amount, + advance.paid_amount, + advance.claimed_amount, + advance.status, + ] + data.append(row) + + return columns, data + + +def get_columns(): + return [ + { + "label": _("Title"), + "fieldname": "title", + "fieldtype": "Link", + "options": "Employee Advance", + "width": 120, + }, + { + "label": _("Employee"), + "fieldname": "employee", + "fieldtype": "Link", + "options": "Employee", + "width": 120, + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120, + }, + {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120}, + { + "label": _("Advance Amount"), + "fieldname": "advance_amount", + "fieldtype": "Currency", + "width": 120, + }, + {"label": _("Paid Amount"), "fieldname": "paid_amount", "fieldtype": "Currency", "width": 120}, + { + "label": _("Claimed Amount"), + "fieldname": "claimed_amount", + "fieldtype": "Currency", + "width": 120, + }, + {"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 120}, + ] + + +def get_conditions(filters): + conditions = "" + + if filters.get("employee"): + conditions += "and employee = %(employee)s" + if filters.get("company"): + conditions += " and company = %(company)s" + if filters.get("status"): + conditions += " and status = %(status)s" + if filters.get("from_date"): + conditions += " and posting_date>=%(from_date)s" + if filters.get("to_date"): + conditions += " and posting_date<=%(to_date)s" + + return conditions + + +def get_advances(filters): + conditions = get_conditions(filters) + return frappe.db.sql( + """select name, employee, paid_amount, status, advance_amount, claimed_amount, company, + posting_date, purpose + from `tabEmployee Advance` + where docstatus<2 %s order by posting_date, name desc""" + % conditions, + filters, + as_dict=1, + ) diff --git a/hrms/hr/report/employee_analytics/__init__.py b/hrms/hr/report/employee_analytics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_analytics/employee_analytics.js b/hrms/hr/report/employee_analytics/employee_analytics.js new file mode 100644 index 0000000..8620a65 --- /dev/null +++ b/hrms/hr/report/employee_analytics/employee_analytics.js @@ -0,0 +1,23 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Employee Analytics"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname":"parameter", + "label": __("Parameter"), + "fieldtype": "Select", + "options": ["Branch","Grade","Department","Designation", "Employment Type"], + "reqd": 1 + } + ] +}; diff --git a/hrms/hr/report/employee_analytics/employee_analytics.json b/hrms/hr/report/employee_analytics/employee_analytics.json new file mode 100644 index 0000000..5a7ab9a --- /dev/null +++ b/hrms/hr/report/employee_analytics/employee_analytics.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-12 13:52:50.631086", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-12 13:52:50.631086", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Employee", + "report_name": "Employee Analytics", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_analytics/employee_analytics.py b/hrms/hr/report/employee_analytics/employee_analytics.py new file mode 100644 index 0000000..12be156 --- /dev/null +++ b/hrms/hr/report/employee_analytics/employee_analytics.py @@ -0,0 +1,99 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + + +def execute(filters=None): + if not filters: + filters = {} + + if not filters["company"]: + frappe.throw(_("{0} is mandatory").format(_("Company"))) + + columns = get_columns() + employees = get_employees(filters) + parameters_result = get_parameters(filters) + parameters = [] + if parameters_result: + for department in parameters_result: + parameters.append(department) + + chart = get_chart_data(parameters, employees, filters) + return columns, employees, None, chart + + +def get_columns(): + return [ + _("Employee") + ":Link/Employee:120", + _("Name") + ":Data:200", + _("Date of Birth") + ":Date:100", + _("Branch") + ":Link/Branch:120", + _("Department") + ":Link/Department:120", + _("Designation") + ":Link/Designation:120", + _("Gender") + "::100", + _("Company") + ":Link/Company:120", + ] + + +def get_conditions(filters): + conditions = " and " + filters.get("parameter").lower().replace(" ", "_") + " IS NOT NULL " + + if filters.get("company"): + conditions += " and company = '%s'" % filters["company"].replace("'", "\\'") + return conditions + + +def get_employees(filters): + conditions = get_conditions(filters) + return frappe.db.sql( + """select name, employee_name, date_of_birth, + branch, department, designation, + gender, company from `tabEmployee` where status = 'Active' %s""" + % conditions, + as_list=1, + ) + + +def get_parameters(filters): + if filters.get("parameter") == "Grade": + parameter = "Employee Grade" + else: + parameter = filters.get("parameter") + + return frappe.db.sql("""select name from `tab""" + parameter + """` """, as_list=1) + + +def get_chart_data(parameters, employees, filters): + if not parameters: + parameters = [] + datasets = [] + parameter_field_name = filters.get("parameter").lower().replace(" ", "_") + label = [] + for parameter in parameters: + if parameter: + total_employee = frappe.db.sql( + """select count(*) from + `tabEmployee` where """ + + parameter_field_name + + """ = %s and company = %s""", + (parameter[0], filters.get("company")), + as_list=1, + ) + if total_employee[0][0]: + label.append(parameter) + datasets.append(total_employee[0][0]) + + values = [value for value in datasets if value != 0] + + total_employee = frappe.db.count("Employee", {"status": "Active"}) + others = total_employee - sum(values) + + label.append(["Not Set"]) + values.append(others) + + chart = {"data": {"labels": label, "datasets": [{"name": "Employees", "values": values}]}} + chart["type"] = "donut" + return chart diff --git a/hrms/hr/report/employee_birthday/__init__.py b/hrms/hr/report/employee_birthday/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_birthday/employee_birthday.js b/hrms/hr/report/employee_birthday/employee_birthday.js new file mode 100644 index 0000000..bbe4a8d --- /dev/null +++ b/hrms/hr/report/employee_birthday/employee_birthday.js @@ -0,0 +1,22 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.query_reports["Employee Birthday"] = { + "filters": [ + { + "fieldname":"month", + "label": __("Month"), + "fieldtype": "Select", + "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", + "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"][frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth()], + }, + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company") + } + ] +} diff --git a/hrms/hr/report/employee_birthday/employee_birthday.json b/hrms/hr/report/employee_birthday/employee_birthday.json new file mode 100644 index 0000000..7946a6a --- /dev/null +++ b/hrms/hr/report/employee_birthday/employee_birthday.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2013-05-06 17:56:03", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2017-02-24 20:18:13.011024", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Birthday", + "owner": "Administrator", + "ref_doctype": "Employee", + "report_name": "Employee Birthday", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_birthday/employee_birthday.py b/hrms/hr/report/employee_birthday/employee_birthday.py new file mode 100644 index 0000000..a6a13d8 --- /dev/null +++ b/hrms/hr/report/employee_birthday/employee_birthday.py @@ -0,0 +1,65 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ + + +def execute(filters=None): + if not filters: + filters = {} + + columns = get_columns() + data = get_employees(filters) + + return columns, data + + +def get_columns(): + return [ + _("Employee") + ":Link/Employee:120", + _("Name") + ":Data:200", + _("Date of Birth") + ":Date:100", + _("Branch") + ":Link/Branch:120", + _("Department") + ":Link/Department:120", + _("Designation") + ":Link/Designation:120", + _("Gender") + "::60", + _("Company") + ":Link/Company:120", + ] + + +def get_employees(filters): + conditions = get_conditions(filters) + return frappe.db.sql( + """select name, employee_name, date_of_birth, + branch, department, designation, + gender, company from tabEmployee where status = 'Active' %s""" + % conditions, + as_list=1, + ) + + +def get_conditions(filters): + conditions = "" + if filters.get("month"): + month = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ].index(filters["month"]) + 1 + conditions += " and month(date_of_birth) = '%s'" % month + + if filters.get("company"): + conditions += " and company = '%s'" % filters["company"].replace("'", "\\'") + + return conditions diff --git a/hrms/hr/report/employee_exits/__init__.py b/hrms/hr/report/employee_exits/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_exits/employee_exits.js b/hrms/hr/report/employee_exits/employee_exits.js new file mode 100644 index 0000000..ac677d8 --- /dev/null +++ b/hrms/hr/report/employee_exits/employee_exits.js @@ -0,0 +1,77 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Employee Exits"] = { + filters: [ + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12) + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate() + }, + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company" + }, + { + "fieldname": "department", + "label": __("Department"), + "fieldtype": "Link", + "options": "Department" + }, + { + "fieldname": "designation", + "label": __("Designation"), + "fieldtype": "Link", + "options": "Designation" + }, + { + "fieldname": "employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee" + }, + { + "fieldname": "reports_to", + "label": __("Reports To"), + "fieldtype": "Link", + "options": "Employee" + }, + { + "fieldname": "interview_status", + "label": __("Interview Status"), + "fieldtype": "Select", + "options": ["", "Pending", "Scheduled", "Completed"] + }, + { + "fieldname": "final_decision", + "label": __("Final Decision"), + "fieldtype": "Select", + "options": ["", "Employee Retained", "Exit Confirmed"] + }, + { + "fieldname": "exit_interview_pending", + "label": __("Exit Interview Pending"), + "fieldtype": "Check" + }, + { + "fieldname": "questionnaire_pending", + "label": __("Exit Questionnaire Pending"), + "fieldtype": "Check" + }, + { + "fieldname": "fnf_pending", + "label": __("FnF Pending"), + "fieldtype": "Check" + } + ] +}; diff --git a/hrms/hr/report/employee_exits/employee_exits.json b/hrms/hr/report/employee_exits/employee_exits.json new file mode 100644 index 0000000..4fe9a85 --- /dev/null +++ b/hrms/hr/report/employee_exits/employee_exits.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-12-05 19:47:18.332319", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letter_head": "Test", + "modified": "2021-12-05 19:47:18.332319", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Exits", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Exit Interview", + "report_name": "Employee Exits", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_exits/employee_exits.py b/hrms/hr/report/employee_exits/employee_exits.py new file mode 100644 index 0000000..21203e9 --- /dev/null +++ b/hrms/hr/report/employee_exits/employee_exits.py @@ -0,0 +1,237 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +from pypika import functions as fn + +import frappe +from frappe import _ +from frappe.query_builder import Order +from frappe.utils import getdate + + +def execute(filters=None): + columns = get_columns() + data = get_data(filters) + chart = get_chart_data(data) + report_summary = get_report_summary(data) + + return columns, data, None, chart, report_summary + + +def get_columns(): + return [ + { + "label": _("Employee"), + "fieldname": "employee", + "fieldtype": "Link", + "options": "Employee", + "width": 150, + }, + {"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 150}, + { + "label": _("Date of Joining"), + "fieldname": "date_of_joining", + "fieldtype": "Date", + "width": 120, + }, + {"label": _("Relieving Date"), "fieldname": "relieving_date", "fieldtype": "Date", "width": 120}, + { + "label": _("Exit Interview"), + "fieldname": "exit_interview", + "fieldtype": "Link", + "options": "Exit Interview", + "width": 150, + }, + { + "label": _("Interview Status"), + "fieldname": "interview_status", + "fieldtype": "Data", + "width": 130, + }, + { + "label": _("Final Decision"), + "fieldname": "employee_status", + "fieldtype": "Data", + "width": 150, + }, + { + "label": _("Full and Final Statement"), + "fieldname": "full_and_final_statement", + "fieldtype": "Link", + "options": "Full and Final Statement", + "width": 180, + }, + { + "label": _("Department"), + "fieldname": "department", + "fieldtype": "Link", + "options": "Department", + "width": 120, + }, + { + "label": _("Designation"), + "fieldname": "designation", + "fieldtype": "Link", + "options": "Designation", + "width": 120, + }, + { + "label": _("Reports To"), + "fieldname": "reports_to", + "fieldtype": "Link", + "options": "Employee", + "width": 120, + }, + ] + + +def get_data(filters): + employee = frappe.qb.DocType("Employee") + interview = frappe.qb.DocType("Exit Interview") + fnf = frappe.qb.DocType("Full and Final Statement") + + query = ( + frappe.qb.from_(employee) + .left_join(interview) + .on(interview.employee == employee.name) + .left_join(fnf) + .on(fnf.employee == employee.name) + .select( + employee.name.as_("employee"), + employee.employee_name.as_("employee_name"), + employee.date_of_joining.as_("date_of_joining"), + employee.relieving_date.as_("relieving_date"), + employee.department.as_("department"), + employee.designation.as_("designation"), + employee.reports_to.as_("reports_to"), + interview.name.as_("exit_interview"), + interview.status.as_("interview_status"), + interview.employee_status.as_("employee_status"), + interview.reference_document_name.as_("questionnaire"), + fnf.name.as_("full_and_final_statement"), + ) + .distinct() + .where( + (fn.Coalesce(fn.Cast(employee.relieving_date, "char"), "") != "") + & ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2))) + & ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2))) + ) + .orderby(employee.relieving_date, order=Order.asc) + ) + + query = get_conditions(filters, query, employee, interview, fnf) + result = query.run(as_dict=True) + + return result + + +def get_conditions(filters, query, employee, interview, fnf): + if filters.get("from_date") and filters.get("to_date"): + query = query.where( + employee.relieving_date[getdate(filters.get("from_date")) : getdate(filters.get("to_date"))] + ) + + elif filters.get("from_date"): + query = query.where(employee.relieving_date >= filters.get("from_date")) + + elif filters.get("to_date"): + query = query.where(employee.relieving_date <= filters.get("to_date")) + + if filters.get("company"): + query = query.where(employee.company == filters.get("company")) + + if filters.get("department"): + query = query.where(employee.department == filters.get("department")) + + if filters.get("designation"): + query = query.where(employee.designation == filters.get("designation")) + + if filters.get("employee"): + query = query.where(employee.name == filters.get("employee")) + + if filters.get("reports_to"): + query = query.where(employee.reports_to == filters.get("reports_to")) + + if filters.get("interview_status"): + query = query.where(interview.status == filters.get("interview_status")) + + if filters.get("final_decision"): + query = query.where(interview.employee_status == filters.get("final_decision")) + + if filters.get("exit_interview_pending"): + query = query.where((interview.name == "") | (interview.name.isnull())) + + if filters.get("questionnaire_pending"): + query = query.where( + (interview.reference_document_name == "") | (interview.reference_document_name.isnull()) + ) + + if filters.get("fnf_pending"): + query = query.where((fnf.name == "") | (fnf.name.isnull())) + + return query + + +def get_chart_data(data): + if not data: + return None + + retained = 0 + exit_confirmed = 0 + pending = 0 + + for entry in data: + if entry.employee_status == "Employee Retained": + retained += 1 + elif entry.employee_status == "Exit Confirmed": + exit_confirmed += 1 + else: + pending += 1 + + chart = { + "data": { + "labels": [_("Retained"), _("Exit Confirmed"), _("Decision Pending")], + "datasets": [{"name": _("Employee Status"), "values": [retained, exit_confirmed, pending]}], + }, + "type": "donut", + "colors": ["green", "red", "blue"], + } + + return chart + + +def get_report_summary(data): + if not data: + return None + + total_resignations = len(data) + interviews_pending = len([entry.name for entry in data if not entry.exit_interview]) + fnf_pending = len([entry.name for entry in data if not entry.full_and_final_statement]) + questionnaires_pending = len([entry.name for entry in data if not entry.questionnaire]) + + return [ + { + "value": total_resignations, + "label": _("Total Resignations"), + "indicator": "Red" if total_resignations > 0 else "Green", + "datatype": "Int", + }, + { + "value": interviews_pending, + "label": _("Pending Interviews"), + "indicator": "Blue" if interviews_pending > 0 else "Green", + "datatype": "Int", + }, + { + "value": fnf_pending, + "label": _("Pending FnF"), + "indicator": "Blue" if fnf_pending > 0 else "Green", + "datatype": "Int", + }, + { + "value": questionnaires_pending, + "label": _("Pending Questionnaires"), + "indicator": "Blue" if questionnaires_pending > 0 else "Green", + "datatype": "Int", + }, + ] diff --git a/hrms/hr/report/employee_exits/test_employee_exits.py b/hrms/hr/report/employee_exits/test_employee_exits.py new file mode 100644 index 0000000..ec1aaba --- /dev/null +++ b/hrms/hr/report/employee_exits/test_employee_exits.py @@ -0,0 +1,246 @@ +import unittest + +import frappe +from frappe.utils import add_days, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.exit_interview.test_exit_interview import create_exit_interview +from hrms.hr.doctype.full_and_final_statement.test_full_and_final_statement import ( + create_full_and_final_statement, +) +from hrms.hr.report.employee_exits.employee_exits import execute + + +class TestEmployeeExits(unittest.TestCase): + @classmethod + def setUpClass(cls): + create_company() + frappe.db.sql("delete from `tabEmployee` where company='Test Company'") + frappe.db.sql("delete from `tabFull and Final Statement` where company='Test Company'") + frappe.db.sql("delete from `tabExit Interview` where company='Test Company'") + + cls.create_records() + + @classmethod + def tearDownClass(cls): + frappe.db.rollback() + + @classmethod + def create_records(cls): + cls.emp1 = make_employee( + "employeeexit1@example.com", + company="Test Company", + date_of_joining=getdate("01-10-2021"), + relieving_date=add_days(getdate(), 14), + designation="Accountant", + ) + cls.emp2 = make_employee( + "employeeexit2@example.com", + company="Test Company", + date_of_joining=getdate("01-12-2021"), + relieving_date=add_days(getdate(), 15), + designation="Accountant", + ) + + cls.emp3 = make_employee( + "employeeexit3@example.com", + company="Test Company", + date_of_joining=getdate("02-12-2021"), + relieving_date=add_days(getdate(), 29), + designation="Engineer", + ) + cls.emp4 = make_employee( + "employeeexit4@example.com", + company="Test Company", + date_of_joining=getdate("01-12-2021"), + relieving_date=add_days(getdate(), 30), + designation="Engineer", + ) + + # exit interview for 3 employees only + cls.interview1 = create_exit_interview(cls.emp1) + cls.interview2 = create_exit_interview(cls.emp2) + cls.interview3 = create_exit_interview(cls.emp3) + + # create fnf for some records + cls.fnf1 = create_full_and_final_statement(cls.emp1) + cls.fnf2 = create_full_and_final_statement(cls.emp2) + + # link questionnaire for a few records + # setting employee doctype as reference instead of creating a questionnaire + # since this is just for a test + frappe.db.set_value( + "Exit Interview", + cls.interview1.name, + {"ref_doctype": "Employee", "reference_document_name": cls.emp1}, + ) + + frappe.db.set_value( + "Exit Interview", + cls.interview2.name, + {"ref_doctype": "Employee", "reference_document_name": cls.emp2}, + ) + + frappe.db.set_value( + "Exit Interview", + cls.interview3.name, + {"ref_doctype": "Employee", "reference_document_name": cls.emp3}, + ) + + def test_employee_exits_summary(self): + filters = { + "company": "Test Company", + "from_date": getdate(), + "to_date": add_days(getdate(), 15), + "designation": "Accountant", + } + + report = execute(filters) + + employee1 = frappe.get_doc("Employee", self.emp1) + employee2 = frappe.get_doc("Employee", self.emp2) + expected_data = [ + { + "employee": employee1.name, + "employee_name": employee1.employee_name, + "date_of_joining": employee1.date_of_joining, + "relieving_date": employee1.relieving_date, + "department": employee1.department, + "designation": employee1.designation, + "reports_to": None, + "exit_interview": self.interview1.name, + "interview_status": self.interview1.status, + "employee_status": "", + "questionnaire": employee1.name, + "full_and_final_statement": self.fnf1.name, + }, + { + "employee": employee2.name, + "employee_name": employee2.employee_name, + "date_of_joining": employee2.date_of_joining, + "relieving_date": employee2.relieving_date, + "department": employee2.department, + "designation": employee2.designation, + "reports_to": None, + "exit_interview": self.interview2.name, + "interview_status": self.interview2.status, + "employee_status": "", + "questionnaire": employee2.name, + "full_and_final_statement": self.fnf2.name, + }, + ] + + self.assertEqual(expected_data, report[1]) # rows + + def test_pending_exit_interviews_summary(self): + filters = { + "company": "Test Company", + "from_date": getdate(), + "to_date": add_days(getdate(), 30), + "exit_interview_pending": 1, + } + + report = execute(filters) + + employee4 = frappe.get_doc("Employee", self.emp4) + expected_data = [ + { + "employee": employee4.name, + "employee_name": employee4.employee_name, + "date_of_joining": employee4.date_of_joining, + "relieving_date": employee4.relieving_date, + "department": employee4.department, + "designation": employee4.designation, + "reports_to": None, + "exit_interview": None, + "interview_status": None, + "employee_status": None, + "questionnaire": None, + "full_and_final_statement": None, + } + ] + + self.assertEqual(expected_data, report[1]) # rows + + def test_pending_exit_questionnaire_summary(self): + filters = { + "company": "Test Company", + "from_date": getdate(), + "to_date": add_days(getdate(), 30), + "questionnaire_pending": 1, + } + + report = execute(filters) + + employee4 = frappe.get_doc("Employee", self.emp4) + expected_data = [ + { + "employee": employee4.name, + "employee_name": employee4.employee_name, + "date_of_joining": employee4.date_of_joining, + "relieving_date": employee4.relieving_date, + "department": employee4.department, + "designation": employee4.designation, + "reports_to": None, + "exit_interview": None, + "interview_status": None, + "employee_status": None, + "questionnaire": None, + "full_and_final_statement": None, + } + ] + + self.assertEqual(expected_data, report[1]) # rows + + def test_pending_fnf_summary(self): + filters = {"company": "Test Company", "fnf_pending": 1} + + report = execute(filters) + + employee3 = frappe.get_doc("Employee", self.emp3) + employee4 = frappe.get_doc("Employee", self.emp4) + expected_data = [ + { + "employee": employee3.name, + "employee_name": employee3.employee_name, + "date_of_joining": employee3.date_of_joining, + "relieving_date": employee3.relieving_date, + "department": employee3.department, + "designation": employee3.designation, + "reports_to": None, + "exit_interview": self.interview3.name, + "interview_status": self.interview3.status, + "employee_status": "", + "questionnaire": employee3.name, + "full_and_final_statement": None, + }, + { + "employee": employee4.name, + "employee_name": employee4.employee_name, + "date_of_joining": employee4.date_of_joining, + "relieving_date": employee4.relieving_date, + "department": employee4.department, + "designation": employee4.designation, + "reports_to": None, + "exit_interview": None, + "interview_status": None, + "employee_status": None, + "questionnaire": None, + "full_and_final_statement": None, + }, + ] + + self.assertEqual(expected_data, report[1]) # rows + + +def create_company(): + if not frappe.db.exists("Company", "Test Company"): + frappe.get_doc( + { + "doctype": "Company", + "company_name": "Test Company", + "default_currency": "INR", + "country": "India", + } + ).insert() diff --git a/hrms/hr/report/employee_hours_utilization_based_on_timesheet/__init__.py b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.js b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.js new file mode 100644 index 0000000..9a30b99 --- /dev/null +++ b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.js @@ -0,0 +1,48 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Employee Hours Utilization Based On Timesheet"] = { + "filters": [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1 + }, + { + fieldname:"to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.now_date(), + reqd: 1 + }, + { + fieldname: "employee", + label: __("Employee"), + fieldtype: "Link", + options: "Employee" + }, + { + fieldname: "department", + label: __("Department"), + fieldtype: "Link", + options: "Department" + }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project" + } + ] +}; diff --git a/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.json b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.json new file mode 100644 index 0000000..6b80943 --- /dev/null +++ b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-04-05 19:23:43.838623", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2022-06-23 20:11:16.295495", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Hours Utilization Based On Timesheet", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Timesheet", + "report_name": "Employee Hours Utilization Based On Timesheet", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py new file mode 100644 index 0000000..a89e6f0 --- /dev/null +++ b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py @@ -0,0 +1,261 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.utils import flt, getdate + + +def execute(filters=None): + return EmployeeHoursReport(filters).run() + + +class EmployeeHoursReport: + """Employee Hours Utilization Report Based On Timesheet""" + + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + + self.from_date = getdate(self.filters.from_date) + self.to_date = getdate(self.filters.to_date) + + self.validate_dates() + self.validate_standard_working_hours() + + def validate_dates(self): + self.day_span = (self.to_date - self.from_date).days + + if self.day_span <= 0: + frappe.throw(_("From Date must come before To Date")) + + def validate_standard_working_hours(self): + self.standard_working_hours = frappe.db.get_single_value("HR Settings", "standard_working_hours") + if not self.standard_working_hours: + msg = _( + "The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}." + ).format( + frappe.bold("Standard Working Hours"), + frappe.utils.get_link_to_form("HR Settings", "HR Settings"), + ) + + frappe.throw(msg) + + def run(self): + self.generate_columns() + self.generate_data() + self.generate_report_summary() + self.generate_chart_data() + + return self.columns, self.data, None, self.chart, self.report_summary + + def generate_columns(self): + self.columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 230, + }, + { + "label": _("Department"), + "options": "Department", + "fieldname": "department", + "fieldtype": "Link", + "width": 120, + }, + {"label": _("Total Hours (T)"), "fieldname": "total_hours", "fieldtype": "Float", "width": 120}, + { + "label": _("Billed Hours (B)"), + "fieldname": "billed_hours", + "fieldtype": "Float", + "width": 170, + }, + { + "label": _("Non-Billed Hours (NB)"), + "fieldname": "non_billed_hours", + "fieldtype": "Float", + "width": 170, + }, + { + "label": _("Untracked Hours (U)"), + "fieldname": "untracked_hours", + "fieldtype": "Float", + "width": 170, + }, + { + "label": _("% Utilization (B + NB) / T"), + "fieldname": "per_util", + "fieldtype": "Percentage", + "width": 200, + }, + { + "label": _("% Utilization (B / T)"), + "fieldname": "per_util_billed_only", + "fieldtype": "Percentage", + "width": 200, + }, + ] + + def generate_data(self): + self.generate_filtered_time_logs() + self.generate_stats_by_employee() + self.set_employee_department_and_name() + + if self.filters.department: + self.filter_stats_by_department() + + self.calculate_utilizations() + + self.data = [] + + for emp, data in self.stats_by_employee.items(): + row = frappe._dict() + row["employee"] = emp + row.update(data) + self.data.append(row) + + # Sort by descending order of percentage utilization + self.data.sort(key=lambda x: x["per_util"], reverse=True) + + def filter_stats_by_department(self): + filtered_data = frappe._dict() + for emp, data in self.stats_by_employee.items(): + if data["department"] == self.filters.department: + filtered_data[emp] = data + + # Update stats + self.stats_by_employee = filtered_data + + def generate_filtered_time_logs(self): + additional_filters = "" + + filter_fields = ["employee", "project", "company"] + + for field in filter_fields: + if self.filters.get(field): + if field == "project": + additional_filters += f"AND ttd.{field} = '{self.filters.get(field)}'" + else: + additional_filters += f"AND tt.{field} = '{self.filters.get(field)}'" + + self.filtered_time_logs = frappe.db.sql( + """ + SELECT tt.employee AS employee, ttd.hours AS hours, ttd.is_billable AS is_billable, ttd.project AS project + FROM `tabTimesheet Detail` AS ttd + JOIN `tabTimesheet` AS tt + ON ttd.parent = tt.name + WHERE tt.employee IS NOT NULL + AND tt.start_date BETWEEN '{0}' AND '{1}' + AND tt.end_date BETWEEN '{0}' AND '{1}' + {2} + """.format( + self.filters.from_date, self.filters.to_date, additional_filters + ) + ) + + def generate_stats_by_employee(self): + self.stats_by_employee = frappe._dict() + + for emp, hours, is_billable, project in self.filtered_time_logs: + self.stats_by_employee.setdefault(emp, frappe._dict()).setdefault("billed_hours", 0.0) + + self.stats_by_employee[emp].setdefault("non_billed_hours", 0.0) + + if is_billable: + self.stats_by_employee[emp]["billed_hours"] += flt(hours, 2) + else: + self.stats_by_employee[emp]["non_billed_hours"] += flt(hours, 2) + + def set_employee_department_and_name(self): + for emp in self.stats_by_employee: + emp_name = frappe.db.get_value("Employee", emp, "employee_name") + emp_dept = frappe.db.get_value("Employee", emp, "department") + + self.stats_by_employee[emp]["department"] = emp_dept + self.stats_by_employee[emp]["employee_name"] = emp_name + + def calculate_utilizations(self): + TOTAL_HOURS = flt(self.standard_working_hours * self.day_span, 2) + for emp, data in self.stats_by_employee.items(): + data["total_hours"] = TOTAL_HOURS + data["untracked_hours"] = flt(TOTAL_HOURS - data["billed_hours"] - data["non_billed_hours"], 2) + + # To handle overtime edge-case + if data["untracked_hours"] < 0: + data["untracked_hours"] = 0.0 + + data["per_util"] = flt( + ((data["billed_hours"] + data["non_billed_hours"]) / TOTAL_HOURS) * 100, 2 + ) + data["per_util_billed_only"] = flt((data["billed_hours"] / TOTAL_HOURS) * 100, 2) + + def generate_report_summary(self): + self.report_summary = [] + + if not self.data: + return + + avg_utilization = 0.0 + avg_utilization_billed_only = 0.0 + total_billed, total_non_billed = 0.0, 0.0 + total_untracked = 0.0 + + for row in self.data: + avg_utilization += row["per_util"] + avg_utilization_billed_only += row["per_util_billed_only"] + total_billed += row["billed_hours"] + total_non_billed += row["non_billed_hours"] + total_untracked += row["untracked_hours"] + + avg_utilization /= len(self.data) + avg_utilization = flt(avg_utilization, 2) + + avg_utilization_billed_only /= len(self.data) + avg_utilization_billed_only = flt(avg_utilization_billed_only, 2) + + THRESHOLD_PERCENTAGE = 70.0 + self.report_summary = [ + { + "value": f"{avg_utilization}%", + "indicator": "Red" if avg_utilization < THRESHOLD_PERCENTAGE else "Green", + "label": _("Avg Utilization"), + "datatype": "Percentage", + }, + { + "value": f"{avg_utilization_billed_only}%", + "indicator": "Red" if avg_utilization_billed_only < THRESHOLD_PERCENTAGE else "Green", + "label": _("Avg Utilization (Billed Only)"), + "datatype": "Percentage", + }, + {"value": total_billed, "label": _("Total Billed Hours"), "datatype": "Float"}, + {"value": total_non_billed, "label": _("Total Non-Billed Hours"), "datatype": "Float"}, + ] + + def generate_chart_data(self): + self.chart = {} + + labels = [] + billed_hours = [] + non_billed_hours = [] + untracked_hours = [] + + for row in self.data: + labels.append(row.get("employee_name")) + billed_hours.append(row.get("billed_hours")) + non_billed_hours.append(row.get("non_billed_hours")) + untracked_hours.append(row.get("untracked_hours")) + + self.chart = { + "data": { + "labels": labels[:30], + "datasets": [ + {"name": _("Billed Hours"), "values": billed_hours[:30]}, + {"name": _("Non-Billed Hours"), "values": non_billed_hours[:30]}, + {"name": _("Untracked Hours"), "values": untracked_hours[:30]}, + ], + }, + "type": "bar", + "barOptions": {"stacked": True}, + } diff --git a/hrms/hr/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py new file mode 100644 index 0000000..a3ba8a6 --- /dev/null +++ b/hrms/hr/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py @@ -0,0 +1,200 @@ +import unittest + +import frappe +from frappe.utils.make_random import get_random + +from erpnext.projects.doctype.project.test_project import make_project +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.report.employee_hours_utilization_based_on_timesheet.employee_hours_utilization_based_on_timesheet import ( + execute, +) + + +class TestEmployeeUtilization(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Create test employee + cls.test_emp1 = make_employee("test1@employeeutil.com", "_Test Company") + cls.test_emp2 = make_employee("test2@employeeutil.com", "_Test Company") + + # Create test project + cls.test_project = make_project({"project_name": "_Test Project"}) + + # Create test timesheets + cls.create_test_timesheets() + + frappe.db.set_value("HR Settings", "HR Settings", "standard_working_hours", 9) + + @classmethod + def create_test_timesheets(cls): + timesheet1 = frappe.new_doc("Timesheet") + timesheet1.employee = cls.test_emp1 + timesheet1.company = "_Test Company" + + timesheet1.append( + "time_logs", + { + "activity_type": get_random("Activity Type"), + "hours": 5, + "is_billable": 1, + "from_time": "2021-04-01 13:30:00.000000", + "to_time": "2021-04-01 18:30:00.000000", + }, + ) + + timesheet1.save() + timesheet1.submit() + + timesheet2 = frappe.new_doc("Timesheet") + timesheet2.employee = cls.test_emp2 + timesheet2.company = "_Test Company" + + timesheet2.append( + "time_logs", + { + "activity_type": get_random("Activity Type"), + "hours": 10, + "is_billable": 0, + "from_time": "2021-04-01 13:30:00.000000", + "to_time": "2021-04-01 23:30:00.000000", + "project": cls.test_project.name, + }, + ) + + timesheet2.save() + timesheet2.submit() + + @classmethod + def tearDownClass(cls): + # Delete time logs + frappe.db.sql( + """ + DELETE FROM `tabTimesheet Detail` + WHERE parent IN ( + SELECT name + FROM `tabTimesheet` + WHERE company = '_Test Company' + ) + """ + ) + + frappe.db.sql("DELETE FROM `tabTimesheet` WHERE company='_Test Company'") + frappe.db.sql(f"DELETE FROM `tabProject` WHERE name='{cls.test_project.name}'") + + def test_utilization_report_with_required_filters_only(self): + filters = {"company": "_Test Company", "from_date": "2021-04-01", "to_date": "2021-04-03"} + + report = execute(filters) + + expected_data = self.get_expected_data_for_test_employees() + self.assertEqual(report[1], expected_data) + + def test_utilization_report_for_single_employee(self): + filters = { + "company": "_Test Company", + "from_date": "2021-04-01", + "to_date": "2021-04-03", + "employee": self.test_emp1, + } + + report = execute(filters) + + emp1_data = frappe.get_doc("Employee", self.test_emp1) + expected_data = [ + { + "employee": self.test_emp1, + "employee_name": "test1@employeeutil.com", + "billed_hours": 5.0, + "non_billed_hours": 0.0, + "department": emp1_data.department, + "total_hours": 18.0, + "untracked_hours": 13.0, + "per_util": 27.78, + "per_util_billed_only": 27.78, + } + ] + + self.assertEqual(report[1], expected_data) + + def test_utilization_report_for_project(self): + filters = { + "company": "_Test Company", + "from_date": "2021-04-01", + "to_date": "2021-04-03", + "project": self.test_project.name, + } + + report = execute(filters) + + emp2_data = frappe.get_doc("Employee", self.test_emp2) + expected_data = [ + { + "employee": self.test_emp2, + "employee_name": "test2@employeeutil.com", + "billed_hours": 0.0, + "non_billed_hours": 10.0, + "department": emp2_data.department, + "total_hours": 18.0, + "untracked_hours": 8.0, + "per_util": 55.56, + "per_util_billed_only": 0.0, + } + ] + + self.assertEqual(report[1], expected_data) + + def test_utilization_report_for_department(self): + emp1_data = frappe.get_doc("Employee", self.test_emp1) + filters = { + "company": "_Test Company", + "from_date": "2021-04-01", + "to_date": "2021-04-03", + "department": emp1_data.department, + } + + report = execute(filters) + + expected_data = self.get_expected_data_for_test_employees() + self.assertEqual(report[1], expected_data) + + def test_report_summary_data(self): + filters = {"company": "_Test Company", "from_date": "2021-04-01", "to_date": "2021-04-03"} + + report = execute(filters) + summary = report[4] + expected_summary_values = ["41.67%", "13.89%", 5.0, 10.0] + + self.assertEqual(len(summary), 4) + + for i in range(4): + self.assertEqual(summary[i]["value"], expected_summary_values[i]) + + def get_expected_data_for_test_employees(self): + emp1_data = frappe.get_doc("Employee", self.test_emp1) + emp2_data = frappe.get_doc("Employee", self.test_emp2) + + return [ + { + "employee": self.test_emp2, + "employee_name": "test2@employeeutil.com", + "billed_hours": 0.0, + "non_billed_hours": 10.0, + "department": emp2_data.department, + "total_hours": 18.0, + "untracked_hours": 8.0, + "per_util": 55.56, + "per_util_billed_only": 0.0, + }, + { + "employee": self.test_emp1, + "employee_name": "test1@employeeutil.com", + "billed_hours": 5.0, + "non_billed_hours": 0.0, + "department": emp1_data.department, + "total_hours": 18.0, + "untracked_hours": 13.0, + "per_util": 27.78, + "per_util_billed_only": 27.78, + }, + ] diff --git a/hrms/hr/report/employee_information/__init__.py b/hrms/hr/report/employee_information/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_information/employee_information.json b/hrms/hr/report/employee_information/employee_information.json new file mode 100644 index 0000000..ee68af3 --- /dev/null +++ b/hrms/hr/report/employee_information/employee_information.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2013-05-06 18:43:53", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"Employee.bank_ac_no\", \"sort_order\": \"desc\", \"sort_by_next\": \"\", \"filters\": [], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Employee\"], [\"employee_number\", \"Employee\"], [\"date_of_joining\", \"Employee\"], [\"branch\", \"Employee\"], [\"department\", \"Employee\"], [\"designation\", \"Employee\"], [\"gender\", \"Employee\"], [\"status\", \"Employee\"], [\"company\", \"Employee\"], [\"employment_type\", \"Employee\"], [\"reports_to\", \"Employee\"], [\"company_email\", \"Employee\"]]}", + "modified": "2017-02-24 20:01:38.681441", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Information", + "owner": "Administrator", + "ref_doctype": "Employee", + "report_name": "Employee Information", + "report_type": "Report Builder", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_leave_balance/__init__.py b/hrms/hr/report/employee_leave_balance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_leave_balance/employee_leave_balance.js b/hrms/hr/report/employee_leave_balance/employee_leave_balance.js new file mode 100644 index 0000000..ad8965d --- /dev/null +++ b/hrms/hr/report/employee_leave_balance/employee_leave_balance.js @@ -0,0 +1,71 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.query_reports["Employee Leave Balance"] = { + "filters": [ + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.defaults.get_default("year_start_date") + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.defaults.get_default("year_end_date") + }, + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname": "department", + "label": __("Department"), + "fieldtype": "Link", + "options": "Department", + }, + { + "fieldname": "employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee", + }, + { + "fieldname": "employee_status", + "label": __("Employee Status"), + "fieldtype": "Select", + "options": [ + "", + { "value": "Active", "label": __("Active") }, + { "value": "Inactive", "label": __("Inactive") }, + { "value": "Suspended", "label": __("Suspended") }, + { "value": "Left", "label": __("Left") }, + ], + "default": "Active", + } + ], + + onload: () => { + frappe.call({ + type: "GET", + method: "hrms.hr.utils.get_leave_period", + args: { + "from_date": frappe.defaults.get_default("year_start_date"), + "to_date": frappe.defaults.get_default("year_end_date"), + "company": frappe.defaults.get_user_default("Company") + }, + freeze: true, + callback: (data) => { + frappe.query_report.set_filter_value("from_date", data.message[0].from_date); + frappe.query_report.set_filter_value("to_date", data.message[0].to_date); + } + }); + } +} diff --git a/hrms/hr/report/employee_leave_balance/employee_leave_balance.json b/hrms/hr/report/employee_leave_balance/employee_leave_balance.json new file mode 100644 index 0000000..8b47f7e --- /dev/null +++ b/hrms/hr/report/employee_leave_balance/employee_leave_balance.json @@ -0,0 +1,26 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2013-02-22 15:29:34", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2017-02-24 20:18:04.317397", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Leave Balance", + "owner": "Administrator", + "ref_doctype": "Employee", + "report_name": "Employee Leave Balance", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_leave_balance/employee_leave_balance.py b/hrms/hr/report/employee_leave_balance/employee_leave_balance.py new file mode 100644 index 0000000..376da25 --- /dev/null +++ b/hrms/hr/report/employee_leave_balance/employee_leave_balance.py @@ -0,0 +1,307 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from itertools import groupby +from typing import Dict, List, Optional, Tuple + +import frappe +from frappe import _ +from frappe.utils import add_days, getdate + +from hrms.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation +from hrms.hr.doctype.leave_application.leave_application import ( + get_leave_balance_on, + get_leaves_for_period, +) + +Filters = frappe._dict + + +def execute(filters: Optional[Filters] = None) -> Tuple: + if filters.to_date <= filters.from_date: + frappe.throw(_('"From Date" can not be greater than or equal to "To Date"')) + + columns = get_columns() + data = get_data(filters) + charts = get_chart_data(data) + return columns, data, None, charts + + +def get_columns() -> List[Dict]: + return [ + { + "label": _("Leave Type"), + "fieldtype": "Link", + "fieldname": "leave_type", + "width": 200, + "options": "Leave Type", + }, + { + "label": _("Employee"), + "fieldtype": "Link", + "fieldname": "employee", + "width": 100, + "options": "Employee", + }, + { + "label": _("Employee Name"), + "fieldtype": "Dynamic Link", + "fieldname": "employee_name", + "width": 100, + "options": "employee", + }, + { + "label": _("Opening Balance"), + "fieldtype": "float", + "fieldname": "opening_balance", + "width": 150, + }, + { + "label": _("New Leave(s) Allocated"), + "fieldtype": "float", + "fieldname": "leaves_allocated", + "width": 200, + }, + { + "label": _("Leave(s) Taken"), + "fieldtype": "float", + "fieldname": "leaves_taken", + "width": 150, + }, + { + "label": _("Leave(s) Expired"), + "fieldtype": "float", + "fieldname": "leaves_expired", + "width": 150, + }, + { + "label": _("Closing Balance"), + "fieldtype": "float", + "fieldname": "closing_balance", + "width": 150, + }, + ] + + +def get_data(filters: Filters) -> List: + leave_types = frappe.db.get_list("Leave Type", pluck="name", order_by="name") + conditions = get_conditions(filters) + + user = frappe.session.user + department_approver_map = get_department_leave_approver_map(filters.get("department")) + + active_employees = frappe.get_list( + "Employee", + filters=conditions, + fields=["name", "employee_name", "department", "user_id", "leave_approver"], + ) + + data = [] + + for leave_type in leave_types: + if len(active_employees) > 1: + data.append({"leave_type": leave_type}) + else: + row = frappe._dict({"leave_type": leave_type}) + + for employee in active_employees: + + leave_approvers = department_approver_map.get(employee.department_name, []).append( + employee.leave_approver + ) + + if ( + (leave_approvers and len(leave_approvers) and user in leave_approvers) + or (user in ["Administrator", employee.user_id]) + or ("HR Manager" in frappe.get_roles(user)) + ): + if len(active_employees) > 1: + row = frappe._dict() + row.employee = employee.name + row.employee_name = employee.employee_name + + leaves_taken = ( + get_leaves_for_period(employee.name, leave_type, filters.from_date, filters.to_date) * -1 + ) + + new_allocation, expired_leaves, carry_forwarded_leaves = get_allocated_and_expired_leaves( + filters.from_date, filters.to_date, employee.name, leave_type + ) + opening = get_opening_balance(employee.name, leave_type, filters, carry_forwarded_leaves) + + row.leaves_allocated = new_allocation + row.leaves_expired = expired_leaves + row.opening_balance = opening + row.leaves_taken = leaves_taken + + # not be shown on the basis of days left it create in user mind for carry_forward leave + row.closing_balance = new_allocation + opening - (row.leaves_expired + leaves_taken) + row.indent = 1 + data.append(row) + + return data + + +def get_opening_balance( + employee: str, leave_type: str, filters: Filters, carry_forwarded_leaves: float +) -> float: + # allocation boundary condition + # opening balance is the closing leave balance 1 day before the filter start date + opening_balance_date = add_days(filters.from_date, -1) + allocation = get_previous_allocation(filters.from_date, leave_type, employee) + + if ( + allocation + and allocation.get("to_date") + and opening_balance_date + and getdate(allocation.get("to_date")) == getdate(opening_balance_date) + ): + # if opening balance date is same as the previous allocation's expiry + # then opening balance should only consider carry forwarded leaves + opening_balance = carry_forwarded_leaves + else: + # else directly get leave balance on the previous day + opening_balance = get_leave_balance_on(employee, leave_type, opening_balance_date) + + return opening_balance + + +def get_conditions(filters: Filters) -> Dict: + conditions = {} + + if filters.get("employee"): + conditions["name"] = filters.get("employee") + + if filters.get("company"): + conditions["company"] = filters.get("company") + + if filters.get("department"): + conditions["department"] = filters.get("department") + + if filters.get("employee_status"): + conditions["status"] = filters.get("employee_status") + + return conditions + + +def get_department_leave_approver_map(department: Optional[str] = None): + # get current department and all its child + department_list = frappe.get_list( + "Department", + filters={"disabled": 0}, + or_filters={"name": department, "parent_department": department}, + pluck="name", + ) + # retrieve approvers list from current department and from its subsequent child departments + approver_list = frappe.get_all( + "Department Approver", + filters={"parentfield": "leave_approvers", "parent": ("in", department_list)}, + fields=["parent", "approver"], + as_list=True, + ) + + approvers = {} + + for k, v in approver_list: + approvers.setdefault(k, []).append(v) + + return approvers + + +def get_allocated_and_expired_leaves( + from_date: str, to_date: str, employee: str, leave_type: str +) -> Tuple[float, float, float]: + new_allocation = 0 + expired_leaves = 0 + carry_forwarded_leaves = 0 + + records = get_leave_ledger_entries(from_date, to_date, employee, leave_type) + + for record in records: + # new allocation records with `is_expired=1` are created when leave expires + # these new records should not be considered, else it leads to negative leave balance + if record.is_expired: + continue + + if record.to_date < getdate(to_date): + # leave allocations ending before to_date, reduce leaves taken within that period + # since they are already used, they won't expire + expired_leaves += record.leaves + expired_leaves += get_leaves_for_period(employee, leave_type, record.from_date, record.to_date) + + if record.from_date >= getdate(from_date): + if record.is_carry_forward: + carry_forwarded_leaves += record.leaves + else: + new_allocation += record.leaves + + return new_allocation, expired_leaves, carry_forwarded_leaves + + +def get_leave_ledger_entries( + from_date: str, to_date: str, employee: str, leave_type: str +) -> List[Dict]: + ledger = frappe.qb.DocType("Leave Ledger Entry") + records = ( + frappe.qb.from_(ledger) + .select( + ledger.employee, + ledger.leave_type, + ledger.from_date, + ledger.to_date, + ledger.leaves, + ledger.transaction_name, + ledger.transaction_type, + ledger.is_carry_forward, + ledger.is_expired, + ) + .where( + (ledger.docstatus == 1) + & (ledger.transaction_type == "Leave Allocation") + & (ledger.employee == employee) + & (ledger.leave_type == leave_type) + & ( + (ledger.from_date[from_date:to_date]) + | (ledger.to_date[from_date:to_date]) + | ((ledger.from_date < from_date) & (ledger.to_date > to_date)) + ) + ) + ).run(as_dict=True) + + return records + + +def get_chart_data(data: List) -> Dict: + labels = [] + datasets = [] + employee_data = data + + if data and data[0].get("employee_name"): + get_dataset_for_chart(employee_data, datasets, labels) + + chart = { + "data": {"labels": labels, "datasets": datasets}, + "type": "bar", + "colors": ["#456789", "#EE8888", "#7E77BF"], + } + + return chart + + +def get_dataset_for_chart(employee_data: List, datasets: List, labels: List) -> List: + leaves = [] + employee_data = sorted(employee_data, key=lambda k: k["employee_name"]) + + for key, group in groupby(employee_data, lambda x: x["employee_name"]): + for grp in group: + if grp.closing_balance: + leaves.append( + frappe._dict({"leave_type": grp.leave_type, "closing_balance": grp.closing_balance}) + ) + + if leaves: + labels.append(key) + + for leave in leaves: + datasets.append({"name": leave.leave_type, "values": [leave.closing_balance]}) diff --git a/hrms/hr/report/employee_leave_balance/test_employee_leave_balance.py b/hrms/hr/report/employee_leave_balance/test_employee_leave_balance.py new file mode 100644 index 0000000..21311ba --- /dev/null +++ b/hrms/hr/report/employee_leave_balance/test_employee_leave_balance.py @@ -0,0 +1,245 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import unittest + +import frappe +from frappe.utils import add_days, add_months, flt, get_year_ending, get_year_start, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.leave_application.test_leave_application import make_allocation_record +from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation +from hrms.hr.doctype.leave_type.test_leave_type import create_leave_type +from hrms.hr.report.employee_leave_balance.employee_leave_balance import execute +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_holiday_list, + make_leave_application, +) +from hrms.tests.test_utils import get_first_sunday + +test_records = frappe.get_test_records("Leave Type") + + +class TestEmployeeLeaveBalance(unittest.TestCase): + def setUp(self): + for dt in [ + "Leave Application", + "Leave Allocation", + "Salary Slip", + "Leave Ledger Entry", + "Leave Type", + ]: + frappe.db.delete(dt) + + frappe.set_user("Administrator") + + self.employee_id = make_employee("test_emp_leave_balance@example.com", company="_Test Company") + + self.date = getdate() + self.year_start = getdate(get_year_start(self.date)) + self.mid_year = add_months(self.year_start, 6) + self.year_end = getdate(get_year_ending(self.date)) + + self.holiday_list = make_holiday_list( + "_Test Emp Balance Holiday List", self.year_start, self.year_end + ) + + def tearDown(self): + frappe.db.rollback() + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_employee_leave_balance(self): + frappe.get_doc(test_records[0]).insert() + + # 5 leaves + allocation1 = make_allocation_record( + employee=self.employee_id, + from_date=add_days(self.year_start, -11), + to_date=add_days(self.year_start, -1), + leaves=5, + ) + # 30 leaves + allocation2 = make_allocation_record( + employee=self.employee_id, from_date=self.year_start, to_date=self.year_end + ) + # expires 5 leaves + process_expired_allocation() + + # 4 days leave + first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) + leave_application = make_leave_application( + self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" + ) + leave_application.reload() + + filters = frappe._dict( + { + "from_date": allocation1.from_date, + "to_date": allocation2.to_date, + "employee": self.employee_id, + } + ) + + report = execute(filters) + + expected_data = [ + { + "leave_type": "_Test Leave Type", + "employee": self.employee_id, + "employee_name": "test_emp_leave_balance@example.com", + "leaves_allocated": flt(allocation1.new_leaves_allocated + allocation2.new_leaves_allocated), + "leaves_expired": flt(allocation1.new_leaves_allocated), + "opening_balance": flt(0), + "leaves_taken": flt(leave_application.total_leave_days), + "closing_balance": flt(allocation2.new_leaves_allocated - leave_application.total_leave_days), + "indent": 1, + } + ] + + self.assertEqual(report[1], expected_data) + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_opening_balance_on_alloc_boundary_dates(self): + frappe.get_doc(test_records[0]).insert() + + # 30 leaves allocated + allocation1 = make_allocation_record( + employee=self.employee_id, from_date=self.year_start, to_date=self.year_end + ) + # 4 days leave application in the first allocation + first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) + leave_application = make_leave_application( + self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" + ) + leave_application.reload() + + # Case 1: opening balance for first alloc boundary + filters = frappe._dict( + {"from_date": self.year_start, "to_date": self.year_end, "employee": self.employee_id} + ) + report = execute(filters) + self.assertEqual(report[1][0].opening_balance, 0) + + # Case 2: opening balance after leave application date + filters = frappe._dict( + { + "from_date": add_days(leave_application.to_date, 1), + "to_date": self.year_end, + "employee": self.employee_id, + } + ) + report = execute(filters) + self.assertEqual( + report[1][0].opening_balance, + (allocation1.new_leaves_allocated - leave_application.total_leave_days), + ) + + # Case 3: leave balance shows actual balance and not consumption balance as per remaining days near alloc end date + # eg: 3 days left for alloc to end, leave balance should still be 26 and not 3 + filters = frappe._dict( + { + "from_date": add_days(self.year_end, -3), + "to_date": self.year_end, + "employee": self.employee_id, + } + ) + report = execute(filters) + self.assertEqual( + report[1][0].opening_balance, + (allocation1.new_leaves_allocated - leave_application.total_leave_days), + ) + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_opening_balance_considers_carry_forwarded_leaves(self): + leave_type = create_leave_type(leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1) + leave_type.insert() + + # 30 leaves allocated for first half of the year + allocation1 = make_allocation_record( + employee=self.employee_id, + from_date=self.year_start, + to_date=self.mid_year, + leave_type=leave_type.name, + ) + # 4 days leave application in the first allocation + first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) + leave_application = make_leave_application( + self.employee_id, first_sunday, add_days(first_sunday, 3), leave_type.name + ) + leave_application.reload() + # 30 leaves allocated for second half of the year + carry forward leaves (26) from the previous allocation + allocation2 = make_allocation_record( + employee=self.employee_id, + from_date=add_days(self.mid_year, 1), + to_date=self.year_end, + carry_forward=True, + leave_type=leave_type.name, + ) + + # Case 1: carry forwarded leaves considered in opening balance for second alloc + filters = frappe._dict( + { + "from_date": add_days(self.mid_year, 1), + "to_date": self.year_end, + "employee": self.employee_id, + } + ) + report = execute(filters) + # available leaves from old alloc + opening_balance = allocation1.new_leaves_allocated - leave_application.total_leave_days + self.assertEqual(report[1][0].opening_balance, opening_balance) + + # Case 2: opening balance one day after alloc boundary = carry forwarded leaves + new leaves alloc + filters = frappe._dict( + { + "from_date": add_days(self.mid_year, 2), + "to_date": self.year_end, + "employee": self.employee_id, + } + ) + report = execute(filters) + # available leaves from old alloc + opening_balance = allocation2.new_leaves_allocated + ( + allocation1.new_leaves_allocated - leave_application.total_leave_days + ) + self.assertEqual(report[1][0].opening_balance, opening_balance) + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_employee_status_filter(self): + frappe.get_doc(test_records[0]).insert() + inactive_emp = make_employee("test_emp_status@example.com", company="_Test Company") + + allocation = make_allocation_record( + employee=inactive_emp, + from_date=self.year_start, + to_date=self.year_end, + leaves=5, + ) + + # set employee as inactive + frappe.db.set_value("Employee", inactive_emp, "status", "Inactive") + + filters = frappe._dict( + { + "from_date": allocation.from_date, + "to_date": allocation.to_date, + "employee": inactive_emp, + "employee_status": "Active", + } + ) + report = execute(filters) + self.assertEqual(len(report[1]), 0) + + filters = frappe._dict( + { + "from_date": allocation.from_date, + "to_date": allocation.to_date, + "employee": inactive_emp, + "employee_status": "Inactive", + } + ) + report = execute(filters) + self.assertEqual(len(report[1]), 1) diff --git a/hrms/hr/report/employee_leave_balance_summary/__init__.py b/hrms/hr/report/employee_leave_balance_summary/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js new file mode 100644 index 0000000..26dd782 --- /dev/null +++ b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js @@ -0,0 +1,48 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports['Employee Leave Balance Summary'] = { + filters: [ + { + fieldname:'date', + label: __('Date'), + fieldtype: 'Date', + reqd: 1, + default: frappe.datetime.now_date() + }, + { + fieldname:'company', + label: __('Company'), + fieldtype: 'Link', + options: 'Company', + reqd: 1, + default: frappe.defaults.get_user_default('Company') + }, + { + fieldname:'employee', + label: __('Employee'), + fieldtype: 'Link', + options: 'Employee', + }, + { + fieldname:'department', + label: __('Department'), + fieldtype: 'Link', + options: 'Department', + }, + { + fieldname: "employee_status", + label: __("Employee Status"), + fieldtype: "Select", + options: [ + "", + { "value": "Active", "label": __("Active") }, + { "value": "Inactive", "label": __("Inactive") }, + { "value": "Suspended", "label": __("Suspended") }, + { "value": "Left", "label": __("Left") }, + ], + default: "Active", + } + ] +}; diff --git a/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json new file mode 100644 index 0000000..1c22ece --- /dev/null +++ b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 0, + "creation": "2019-09-05 11:18:06.209397", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-09-06 11:18:06.209397", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Leave Balance Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Employee", + "report_name": "Employee Leave Balance Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR Manager" + }, + { + "role": "HR User" + }, + { + "role": "Leave Approver" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py new file mode 100644 index 0000000..7cfe622 --- /dev/null +++ b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -0,0 +1,85 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ + +from hrms.hr.doctype.leave_application.leave_application import get_leave_details +from hrms.hr.report.employee_leave_balance.employee_leave_balance import ( + get_department_leave_approver_map, +) + + +def execute(filters=None): + leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc") + + columns = get_columns(leave_types) + data = get_data(filters, leave_types) + + return columns, data + + +def get_columns(leave_types): + columns = [ + _("Employee") + ":Link/Employee:150", + _("Employee Name") + "::200", + _("Department") + ":Link/Department:150", + ] + + for leave_type in leave_types: + columns.append(_(leave_type) + ":Float:160") + + return columns + + +def get_conditions(filters): + conditions = { + "company": filters.company, + } + if filters.get("employee_status"): + conditions.update({"status": filters.get("employee_status")}) + if filters.get("department"): + conditions.update({"department": filters.get("department")}) + if filters.get("employee"): + conditions.update({"employee": filters.get("employee")}) + + return conditions + + +def get_data(filters, leave_types): + user = frappe.session.user + conditions = get_conditions(filters) + + active_employees = frappe.get_list( + "Employee", + filters=conditions, + fields=["name", "employee_name", "department", "user_id", "leave_approver"], + ) + + department_approver_map = get_department_leave_approver_map(filters.get("department")) + + data = [] + for employee in active_employees: + leave_approvers = department_approver_map.get(employee.department_name, []) + if employee.leave_approver: + leave_approvers.append(employee.leave_approver) + + if ( + (len(leave_approvers) and user in leave_approvers) + or (user in ["Administrator", employee.user_id]) + or ("HR Manager" in frappe.get_roles(user)) + ): + row = [employee.name, employee.employee_name, employee.department] + available_leave = get_leave_details(employee.name, filters.date) + for leave_type in leave_types: + remaining = 0 + if leave_type in available_leave["leave_allocation"]: + # opening balance + remaining = available_leave["leave_allocation"][leave_type]["remaining_leaves"] + + row += [remaining] + + data.append(row) + + return data diff --git a/hrms/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py b/hrms/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py new file mode 100644 index 0000000..3ea06f3 --- /dev/null +++ b/hrms/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py @@ -0,0 +1,180 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import unittest + +import frappe +from frappe.utils import add_days, flt, get_year_ending, get_year_start, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.leave_application.test_leave_application import make_allocation_record +from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation +from hrms.hr.report.employee_leave_balance_summary.employee_leave_balance_summary import execute +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_holiday_list, + make_leave_application, +) +from hrms.tests.test_utils import get_first_sunday + +test_records = frappe.get_test_records("Leave Type") + + +class TestEmployeeLeaveBalance(unittest.TestCase): + def setUp(self): + for dt in [ + "Leave Application", + "Leave Allocation", + "Salary Slip", + "Leave Ledger Entry", + "Leave Type", + ]: + frappe.db.delete(dt) + + frappe.set_user("Administrator") + + self.employee_id = make_employee("test_emp_leave_balance@example.com", company="_Test Company") + + self.date = getdate() + self.year_start = getdate(get_year_start(self.date)) + self.year_end = getdate(get_year_ending(self.date)) + + self.holiday_list = make_holiday_list( + "_Test Emp Balance Holiday List", self.year_start, self.year_end + ) + + def tearDown(self): + frappe.db.rollback() + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_employee_leave_balance_summary(self): + frappe.get_doc(test_records[0]).insert() + + # 5 leaves + allocation1 = make_allocation_record( + employee=self.employee_id, + from_date=add_days(self.year_start, -11), + to_date=add_days(self.year_start, -1), + leaves=5, + ) + # 30 leaves + allocation2 = make_allocation_record( + employee=self.employee_id, from_date=self.year_start, to_date=self.year_end + ) + + # 2 days leave within the first allocation + leave_application1 = make_leave_application( + self.employee_id, + add_days(self.year_start, -11), + add_days(self.year_start, -10), + "_Test Leave Type", + ) + leave_application1.reload() + + # expires 3 leaves + process_expired_allocation() + + # 4 days leave within the second allocation + first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) + leave_application2 = make_leave_application( + self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" + ) + leave_application2.reload() + + filters = frappe._dict( + { + "date": add_days(leave_application2.to_date, 1), + "company": "_Test Company", + "employee": self.employee_id, + } + ) + + report = execute(filters) + + expected_data = [ + [ + self.employee_id, + "test_emp_leave_balance@example.com", + frappe.db.get_value("Employee", self.employee_id, "department"), + flt( + allocation1.new_leaves_allocated # allocated = 5 + + allocation2.new_leaves_allocated # allocated = 30 + - leave_application1.total_leave_days # leaves taken in the 1st alloc = 2 + - ( + allocation1.new_leaves_allocated - leave_application1.total_leave_days + ) # leaves expired from 1st alloc = 3 + - leave_application2.total_leave_days # leaves taken in the 2nd alloc = 4 + ), + ] + ] + + self.assertEqual(report[1], expected_data) + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_get_leave_balance_near_alloc_expiry(self): + frappe.get_doc(test_records[0]).insert() + + # 30 leaves allocated + allocation = make_allocation_record( + employee=self.employee_id, from_date=self.year_start, to_date=self.year_end + ) + # 4 days leave application in the first allocation + first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) + leave_application = make_leave_application( + self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" + ) + leave_application.reload() + + # Leave balance should show actual balance, and not "consumption balance as per remaining days", near alloc end date + # eg: 3 days left for alloc to end, leave balance should still be 26 and not 3 + filters = frappe._dict( + {"date": add_days(self.year_end, -3), "company": "_Test Company", "employee": self.employee_id} + ) + report = execute(filters) + + expected_data = [ + [ + self.employee_id, + "test_emp_leave_balance@example.com", + frappe.db.get_value("Employee", self.employee_id, "department"), + flt(allocation.new_leaves_allocated - leave_application.total_leave_days), + ] + ] + + self.assertEqual(report[1], expected_data) + + @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") + def test_employee_status_filter(self): + frappe.get_doc(test_records[0]).insert() + + inactive_emp = make_employee("test_emp_status@example.com", company="_Test Company") + allocation = make_allocation_record( + employee=inactive_emp, from_date=self.year_start, to_date=self.year_end + ) + + # set employee as inactive + frappe.db.set_value("Employee", inactive_emp, "status", "Inactive") + + filters = frappe._dict( + { + "date": allocation.from_date, + "company": "_Test Company", + "employee": inactive_emp, + "employee_status": "Active", + } + ) + report = execute(filters) + self.assertEqual(len(report[1]), 0) + + filters = frappe._dict( + { + "date": allocation.from_date, + "company": "_Test Company", + "employee": inactive_emp, + "employee_status": "Inactive", + } + ) + report = execute(filters) + self.assertEqual(len(report[1]), 1) diff --git a/hrms/hr/report/employees_working_on_a_holiday/__init__.py b/hrms/hr/report/employees_working_on_a_holiday/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js b/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js new file mode 100644 index 0000000..97108e8 --- /dev/null +++ b/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js @@ -0,0 +1,27 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Employees working on a holiday"] = { + "filters": [ + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.year_start() + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.year_end() + }, + { + "fieldname":"holiday_list", + "label": __("Holiday List"), + "fieldtype": "Link", + "options": "Holiday List" + } + ] +} diff --git a/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json b/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json new file mode 100644 index 0000000..6c9f7ff --- /dev/null +++ b/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2016-07-14 12:03:56.967739", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2017-02-24 20:05:17.833885", + "modified_by": "Administrator", + "module": "HR", + "name": "Employees working on a holiday", + "owner": "Administrator", + "ref_doctype": "Attendance", + "report_name": "Employees working on a holiday", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py b/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py new file mode 100644 index 0000000..f13fabf --- /dev/null +++ b/hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py @@ -0,0 +1,70 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + + +def execute(filters=None): + if not filters: + filters = {} + + columns = get_columns() + data = get_employees(filters) + return columns, data + + +def get_columns(): + return [ + _("Employee") + ":Link/Employee:120", + _("Name") + ":Data:200", + _("Date") + ":Date:100", + _("Status") + ":Data:70", + _("Holiday") + ":Data:200", + ] + + +def get_employees(filters): + holiday_filter = [ + ["holiday_date", ">=", filters.from_date], + ["holiday_date", "<=", filters.to_date], + ] + if filters.holiday_list: + holiday_filter.append(["parent", "=", filters.holiday_list]) + + holidays = frappe.get_all( + "Holiday", fields=["holiday_date", "description"], filters=holiday_filter + ) + + holiday_names = {} + holidays_list = [] + + for holiday in holidays: + holidays_list.append(holiday.holiday_date) + holiday_names[holiday.holiday_date] = holiday.description + + if holidays_list: + cond = " attendance_date in %(holidays_list)s" + + if filters.holiday_list: + cond += ( + """ and (employee in (select employee from tabEmployee where holiday_list = %(holidays)s))""" + ) + + employee_list = frappe.db.sql( + """select + employee, employee_name, attendance_date, status + from tabAttendance + where %s""" + % cond.format(", ".join(["%s"] * len(holidays_list))), + {"holidays_list": holidays_list, "holidays": filters.holiday_list}, + as_list=True, + ) + + for employee_data in employee_list: + employee_data.append(holiday_names[employee_data[2]]) + + return employee_list + else: + return [] diff --git a/hrms/hr/report/monthly_attendance_sheet/__init__.py b/hrms/hr/report/monthly_attendance_sheet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js new file mode 100644 index 0000000..f9d57b3 --- /dev/null +++ b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js @@ -0,0 +1,101 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + + +frappe.query_reports["Monthly Attendance Sheet"] = { + "filters": [ + { + "fieldname": "month", + "label": __("Month"), + "fieldtype": "Select", + "reqd": 1 , + "options": [ + { "value": 1, "label": __("Jan") }, + { "value": 2, "label": __("Feb") }, + { "value": 3, "label": __("Mar") }, + { "value": 4, "label": __("Apr") }, + { "value": 5, "label": __("May") }, + { "value": 6, "label": __("June") }, + { "value": 7, "label": __("July") }, + { "value": 8, "label": __("Aug") }, + { "value": 9, "label": __("Sep") }, + { "value": 10, "label": __("Oct") }, + { "value": 11, "label": __("Nov") }, + { "value": 12, "label": __("Dec") }, + ], + "default": frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1 + }, + { + "fieldname":"year", + "label": __("Year"), + "fieldtype": "Select", + "reqd": 1 + }, + { + "fieldname":"employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company + } + }; + } + }, + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname":"group_by", + "label": __("Group By"), + "fieldtype": "Select", + "options": ["","Branch","Grade","Department","Designation"] + }, + { + "fieldname":"summarized_view", + "label": __("Summarized View"), + "fieldtype": "Check", + "Default": 0, + } + ], + onload: function() { + return frappe.call({ + method: "hrms.hr.report.monthly_attendance_sheet.monthly_attendance_sheet.get_attendance_years", + callback: function(r) { + var year_filter = frappe.query_report.get_filter('year'); + year_filter.df.options = r.message; + year_filter.df.default = r.message.split("\n")[0]; + year_filter.refresh(); + year_filter.set_input(year_filter.df.default); + } + }); + }, + formatter: function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + const summarized_view = frappe.query_report.get_filter_value('summarized_view'); + const group_by = frappe.query_report.get_filter_value('group_by'); + + if (!summarized_view) { + if ((group_by && column.colIndex > 3) || (!group_by && column.colIndex > 2)) { + if (value == 'P' || value == 'WFH') + value = "" + value + ""; + else if (value == 'A') + value = "" + value + ""; + else if (value == 'HD') + value = "" + value + ""; + else if (value == 'L') + value = "" + value + ""; + } + } + + return value; + } +} diff --git a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json new file mode 100644 index 0000000..4daab81 --- /dev/null +++ b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2013-05-13 14:04:03", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2017-02-24 20:16:50.550242", + "modified_by": "Administrator", + "module": "HR", + "name": "Monthly Attendance Sheet", + "owner": "Administrator", + "ref_doctype": "Attendance", + "report_name": "Monthly Attendance Sheet", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py new file mode 100644 index 0000000..efd2d38 --- /dev/null +++ b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -0,0 +1,620 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from calendar import monthrange +from itertools import groupby +from typing import Dict, List, Optional, Tuple + +import frappe +from frappe import _ +from frappe.query_builder.functions import Count, Extract, Sum +from frappe.utils import cint, cstr, getdate + +Filters = frappe._dict + +status_map = { + "Present": "P", + "Absent": "A", + "Half Day": "HD", + "Work From Home": "WFH", + "On Leave": "L", + "Holiday": "H", + "Weekly Off": "WO", +} + +day_abbr = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] + + +def execute(filters: Optional[Filters] = None) -> Tuple: + filters = frappe._dict(filters or {}) + + if not (filters.month and filters.year): + frappe.throw(_("Please select month and year.")) + + attendance_map = get_attendance_map(filters) + if not attendance_map: + frappe.msgprint(_("No attendance records found."), alert=True, indicator="orange") + return [], [], None, None + + columns = get_columns(filters) + data = get_data(filters, attendance_map) + + if not data: + frappe.msgprint( + _("No attendance records found for this criteria."), alert=True, indicator="orange" + ) + return columns, [], None, None + + message = get_message() if not filters.summarized_view else "" + chart = get_chart_data(attendance_map, filters) + + return columns, data, message, chart + + +def get_message() -> str: + message = "" + colors = ["green", "red", "orange", "green", "#318AD8", "", ""] + + count = 0 + for status, abbr in status_map.items(): + message += f""" + + {status} - {abbr} + + """ + count += 1 + + return message + + +def get_columns(filters: Filters) -> List[Dict]: + columns = [] + + if filters.group_by: + columns.append( + { + "label": _(filters.group_by), + "fieldname": frappe.scrub(filters.group_by), + "fieldtype": "Link", + "options": "Branch", + "width": 120, + } + ) + + columns.extend( + [ + { + "label": _("Employee"), + "fieldname": "employee", + "fieldtype": "Link", + "options": "Employee", + "width": 135, + }, + {"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 120}, + ] + ) + + if filters.summarized_view: + columns.extend( + [ + { + "label": _("Total Present"), + "fieldname": "total_present", + "fieldtype": "Float", + "width": 110, + }, + {"label": _("Total Leaves"), "fieldname": "total_leaves", "fieldtype": "Float", "width": 110}, + {"label": _("Total Absent"), "fieldname": "total_absent", "fieldtype": "Float", "width": 110}, + { + "label": _("Total Holidays"), + "fieldname": "total_holidays", + "fieldtype": "Float", + "width": 120, + }, + { + "label": _("Unmarked Days"), + "fieldname": "unmarked_days", + "fieldtype": "Float", + "width": 130, + }, + ] + ) + columns.extend(get_columns_for_leave_types()) + columns.extend( + [ + { + "label": _("Total Late Entries"), + "fieldname": "total_late_entries", + "fieldtype": "Float", + "width": 140, + }, + { + "label": _("Total Early Exits"), + "fieldname": "total_early_exits", + "fieldtype": "Float", + "width": 140, + }, + ] + ) + else: + columns.append({"label": _("Shift"), "fieldname": "shift", "fieldtype": "Data", "width": 120}) + columns.extend(get_columns_for_days(filters)) + + return columns + + +def get_columns_for_leave_types() -> List[Dict]: + leave_types = frappe.db.get_all("Leave Type", pluck="name") + types = [] + for entry in leave_types: + types.append( + {"label": entry, "fieldname": frappe.scrub(entry), "fieldtype": "Float", "width": 120} + ) + + return types + + +def get_columns_for_days(filters: Filters) -> List[Dict]: + total_days = get_total_days_in_month(filters) + days = [] + + for day in range(1, total_days + 1): + # forms the dates from selected year and month from filters + date = "{}-{}-{}".format(cstr(filters.year), cstr(filters.month), cstr(day)) + # gets abbr from weekday number + weekday = day_abbr[getdate(date).weekday()] + # sets days as 1 Mon, 2 Tue, 3 Wed + label = "{} {}".format(cstr(day), weekday) + days.append({"label": label, "fieldtype": "Data", "fieldname": day, "width": 65}) + + return days + + +def get_total_days_in_month(filters: Filters) -> int: + return monthrange(cint(filters.year), cint(filters.month))[1] + + +def get_data(filters: Filters, attendance_map: Dict) -> List[Dict]: + employee_details, group_by_param_values = get_employee_related_details( + filters.group_by, filters.company + ) + holiday_map = get_holiday_map(filters) + data = [] + + if filters.group_by: + group_by_column = frappe.scrub(filters.group_by) + + for value in group_by_param_values: + if not value: + continue + + records = get_rows(employee_details[value], filters, holiday_map, attendance_map) + + if records: + data.append({group_by_column: frappe.bold(value)}) + data.extend(records) + else: + data = get_rows(employee_details, filters, holiday_map, attendance_map) + + return data + + +def get_attendance_map(filters: Filters) -> Dict: + """Returns a dictionary of employee wise attendance map as per shifts for all the days of the month like + { + 'employee1': { + 'Morning Shift': {1: 'Present', 2: 'Absent', ...} + 'Evening Shift': {1: 'Absent', 2: 'Present', ...} + }, + 'employee2': { + 'Afternoon Shift': {1: 'Present', 2: 'Absent', ...} + 'Night Shift': {1: 'Absent', 2: 'Absent', ...} + } + } + """ + Attendance = frappe.qb.DocType("Attendance") + query = ( + frappe.qb.from_(Attendance) + .select( + Attendance.employee, + Extract("day", Attendance.attendance_date).as_("day_of_month"), + Attendance.status, + Attendance.shift, + ) + .where( + (Attendance.docstatus == 1) + & (Attendance.company == filters.company) + & (Extract("month", Attendance.attendance_date) == filters.month) + & (Extract("year", Attendance.attendance_date) == filters.year) + ) + ) + if filters.employee: + query = query.where(Attendance.employee == filters.employee) + query = query.orderby(Attendance.employee, Attendance.attendance_date) + + attendance_list = query.run(as_dict=1) + attendance_map = {} + + for d in attendance_list: + attendance_map.setdefault(d.employee, frappe._dict()).setdefault(d.shift, frappe._dict()) + attendance_map[d.employee][d.shift][d.day_of_month] = d.status + + return attendance_map + + +def get_employee_related_details(group_by: str, company: str) -> Tuple[Dict, List]: + """Returns + 1. nested dict for employee details + 2. list of values for the group by filter + """ + Employee = frappe.qb.DocType("Employee") + query = ( + frappe.qb.from_(Employee) + .select( + Employee.name, + Employee.employee_name, + Employee.designation, + Employee.grade, + Employee.department, + Employee.branch, + Employee.company, + Employee.holiday_list, + ) + .where(Employee.company == company) + ) + + if group_by: + group_by = group_by.lower() + query = query.orderby(group_by) + + employee_details = query.run(as_dict=True) + + group_by_param_values = [] + emp_map = {} + + if group_by: + for parameter, employees in groupby(employee_details, key=lambda d: d[group_by]): + group_by_param_values.append(parameter) + emp_map.setdefault(parameter, frappe._dict()) + + for emp in employees: + emp_map[parameter][emp.name] = emp + else: + for emp in employee_details: + emp_map[emp.name] = emp + + return emp_map, group_by_param_values + + +def get_holiday_map(filters: Filters) -> Dict[str, List[Dict]]: + """ + Returns a dict of holidays falling in the filter month and year + with list name as key and list of holidays as values like + { + 'Holiday List 1': [ + {'day_of_month': '0' , 'weekly_off': 1}, + {'day_of_month': '1', 'weekly_off': 0} + ], + 'Holiday List 2': [ + {'day_of_month': '0' , 'weekly_off': 1}, + {'day_of_month': '1', 'weekly_off': 0} + ] + } + """ + # add default holiday list too + holiday_lists = frappe.db.get_all("Holiday List", pluck="name") + default_holiday_list = frappe.get_cached_value("Company", filters.company, "default_holiday_list") + holiday_lists.append(default_holiday_list) + + holiday_map = frappe._dict() + Holiday = frappe.qb.DocType("Holiday") + + for d in holiday_lists: + if not d: + continue + + holidays = ( + frappe.qb.from_(Holiday) + .select(Extract("day", Holiday.holiday_date).as_("day_of_month"), Holiday.weekly_off) + .where( + (Holiday.parent == d) + & (Extract("month", Holiday.holiday_date) == filters.month) + & (Extract("year", Holiday.holiday_date) == filters.year) + ) + ).run(as_dict=True) + + holiday_map.setdefault(d, holidays) + + return holiday_map + + +def get_rows( + employee_details: Dict, filters: Filters, holiday_map: Dict, attendance_map: Dict +) -> List[Dict]: + records = [] + default_holiday_list = frappe.get_cached_value("Company", filters.company, "default_holiday_list") + + for employee, details in employee_details.items(): + emp_holiday_list = details.holiday_list or default_holiday_list + holidays = holiday_map.get(emp_holiday_list) + + if filters.summarized_view: + attendance = get_attendance_status_for_summarized_view(employee, filters, holidays) + if not attendance: + continue + + leave_summary = get_leave_summary(employee, filters) + entry_exits_summary = get_entry_exits_summary(employee, filters) + + row = {"employee": employee, "employee_name": details.employee_name} + set_defaults_for_summarized_view(filters, row) + row.update(attendance) + row.update(leave_summary) + row.update(entry_exits_summary) + + records.append(row) + else: + employee_attendance = attendance_map.get(employee) + if not employee_attendance: + continue + + attendance_for_employee = get_attendance_status_for_detailed_view( + employee, filters, employee_attendance, holidays + ) + # set employee details in the first row + attendance_for_employee[0].update( + {"employee": employee, "employee_name": details.employee_name} + ) + + records.extend(attendance_for_employee) + + return records + + +def set_defaults_for_summarized_view(filters, row): + for entry in get_columns(filters): + if entry.get("fieldtype") == "Float": + row[entry.get("fieldname")] = 0.0 + + +def get_attendance_status_for_summarized_view( + employee: str, filters: Filters, holidays: List +) -> Dict: + """Returns dict of attendance status for employee like + {'total_present': 1.5, 'total_leaves': 0.5, 'total_absent': 13.5, 'total_holidays': 8, 'unmarked_days': 5} + """ + summary, attendance_days = get_attendance_summary_and_days(employee, filters) + if not any(summary.values()): + return {} + + total_days = get_total_days_in_month(filters) + total_holidays = total_unmarked_days = 0 + + for day in range(1, total_days + 1): + if day in attendance_days: + continue + + status = get_holiday_status(day, holidays) + if status in ["Weekly Off", "Holiday"]: + total_holidays += 1 + elif not status: + total_unmarked_days += 1 + + return { + "total_present": summary.total_present + summary.total_half_days, + "total_leaves": summary.total_leaves + summary.total_half_days, + "total_absent": summary.total_absent + summary.total_half_days, + "total_holidays": total_holidays, + "unmarked_days": total_unmarked_days, + } + + +def get_attendance_summary_and_days(employee: str, filters: Filters) -> Tuple[Dict, List]: + Attendance = frappe.qb.DocType("Attendance") + + present_case = ( + frappe.qb.terms.Case() + .when(((Attendance.status == "Present") | (Attendance.status == "Work From Home")), 1) + .else_(0) + ) + sum_present = Sum(present_case).as_("total_present") + + absent_case = frappe.qb.terms.Case().when(Attendance.status == "Absent", 1).else_(0) + sum_absent = Sum(absent_case).as_("total_absent") + + leave_case = frappe.qb.terms.Case().when(Attendance.status == "On Leave", 1).else_(0) + sum_leave = Sum(leave_case).as_("total_leaves") + + half_day_case = frappe.qb.terms.Case().when(Attendance.status == "Half Day", 0.5).else_(0) + sum_half_day = Sum(half_day_case).as_("total_half_days") + + summary = ( + frappe.qb.from_(Attendance) + .select( + sum_present, + sum_absent, + sum_leave, + sum_half_day, + ) + .where( + (Attendance.docstatus == 1) + & (Attendance.employee == employee) + & (Attendance.company == filters.company) + & (Extract("month", Attendance.attendance_date) == filters.month) + & (Extract("year", Attendance.attendance_date) == filters.year) + ) + ).run(as_dict=True) + + days = ( + frappe.qb.from_(Attendance) + .select(Extract("day", Attendance.attendance_date).as_("day_of_month")) + .distinct() + .where( + (Attendance.docstatus == 1) + & (Attendance.employee == employee) + & (Attendance.company == filters.company) + & (Extract("month", Attendance.attendance_date) == filters.month) + & (Extract("year", Attendance.attendance_date) == filters.year) + ) + ).run(pluck=True) + + return summary[0], days + + +def get_attendance_status_for_detailed_view( + employee: str, filters: Filters, employee_attendance: Dict, holidays: List +) -> List[Dict]: + """Returns list of shift-wise attendance status for employee + [ + {'shift': 'Morning Shift', 1: 'A', 2: 'P', 3: 'A'....}, + {'shift': 'Evening Shift', 1: 'P', 2: 'A', 3: 'P'....} + ] + """ + total_days = get_total_days_in_month(filters) + attendance_values = [] + + for shift, status_dict in employee_attendance.items(): + row = {"shift": shift} + + for day in range(1, total_days + 1): + status = status_dict.get(day) + if status is None and holidays: + status = get_holiday_status(day, holidays) + + abbr = status_map.get(status, "") + row[day] = abbr + + attendance_values.append(row) + + return attendance_values + + +def get_holiday_status(day: int, holidays: List) -> str: + status = None + for holiday in holidays: + if day == holiday.get("day_of_month"): + if holiday.get("weekly_off"): + status = "Weekly Off" + else: + status = "Holiday" + break + return status + + +def get_leave_summary(employee: str, filters: Filters) -> Dict[str, float]: + """Returns a dict of leave type and corresponding leaves taken by employee like: + {'leave_without_pay': 1.0, 'sick_leave': 2.0} + """ + Attendance = frappe.qb.DocType("Attendance") + day_case = frappe.qb.terms.Case().when(Attendance.status == "Half Day", 0.5).else_(1) + sum_leave_days = Sum(day_case).as_("leave_days") + + leave_details = ( + frappe.qb.from_(Attendance) + .select(Attendance.leave_type, sum_leave_days) + .where( + (Attendance.employee == employee) + & (Attendance.docstatus == 1) + & (Attendance.company == filters.company) + & ((Attendance.leave_type.isnotnull()) | (Attendance.leave_type != "")) + & (Extract("month", Attendance.attendance_date) == filters.month) + & (Extract("year", Attendance.attendance_date) == filters.year) + ) + .groupby(Attendance.leave_type) + ).run(as_dict=True) + + leaves = {} + for d in leave_details: + leave_type = frappe.scrub(d.leave_type) + leaves[leave_type] = d.leave_days + + return leaves + + +def get_entry_exits_summary(employee: str, filters: Filters) -> Dict[str, float]: + """Returns total late entries and total early exits for employee like: + {'total_late_entries': 5, 'total_early_exits': 2} + """ + Attendance = frappe.qb.DocType("Attendance") + + late_entry_case = frappe.qb.terms.Case().when(Attendance.late_entry == "1", "1") + count_late_entries = Count(late_entry_case).as_("total_late_entries") + + early_exit_case = frappe.qb.terms.Case().when(Attendance.early_exit == "1", "1") + count_early_exits = Count(early_exit_case).as_("total_early_exits") + + entry_exits = ( + frappe.qb.from_(Attendance) + .select(count_late_entries, count_early_exits) + .where( + (Attendance.docstatus == 1) + & (Attendance.employee == employee) + & (Attendance.company == filters.company) + & (Extract("month", Attendance.attendance_date) == filters.month) + & (Extract("year", Attendance.attendance_date) == filters.year) + ) + ).run(as_dict=True) + + return entry_exits[0] + + +@frappe.whitelist() +def get_attendance_years() -> str: + """Returns all the years for which attendance records exist""" + Attendance = frappe.qb.DocType("Attendance") + year_list = ( + frappe.qb.from_(Attendance) + .select(Extract("year", Attendance.attendance_date).as_("year")) + .distinct() + ).run(as_dict=True) + + if year_list: + year_list.sort(key=lambda d: d.year, reverse=True) + else: + year_list = [getdate().year] + + return "\n".join(cstr(entry.year) for entry in year_list) + + +def get_chart_data(attendance_map: Dict, filters: Filters) -> Dict: + days = get_columns_for_days(filters) + labels = [] + absent = [] + present = [] + leave = [] + + for day in days: + labels.append(day["label"]) + total_absent_on_day = total_leaves_on_day = total_present_on_day = 0 + + for employee, attendance_dict in attendance_map.items(): + for shift, attendance in attendance_dict.items(): + attendance_on_day = attendance.get(day["fieldname"]) + + if attendance_on_day == "Absent": + total_absent_on_day += 1 + elif attendance_on_day in ["Present", "Work From Home"]: + total_present_on_day += 1 + elif attendance_on_day == "Half Day": + total_present_on_day += 0.5 + total_leaves_on_day += 0.5 + elif attendance_on_day == "On Leave": + total_leaves_on_day += 1 + + absent.append(total_absent_on_day) + present.append(total_present_on_day) + leave.append(total_leaves_on_day) + + return { + "data": { + "labels": labels, + "datasets": [ + {"name": "Absent", "values": absent}, + {"name": "Present", "values": present}, + {"name": "Leave", "values": leave}, + ], + }, + "type": "line", + "colors": ["red", "green", "blue"], + } diff --git a/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py b/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py new file mode 100644 index 0000000..8310a17 --- /dev/null +++ b/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py @@ -0,0 +1,251 @@ +from dateutil.relativedelta import relativedelta + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import get_year_ending, get_year_start, getdate, now_datetime + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.attendance.attendance import mark_attendance +from hrms.hr.doctype.leave_application.test_leave_application import make_allocation_record +from hrms.hr.report.monthly_attendance_sheet.monthly_attendance_sheet import execute +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_holiday_list, + make_leave_application, +) + +test_dependencies = ["Shift Type"] + + +class TestMonthlyAttendanceSheet(FrappeTestCase): + def setUp(self): + self.employee = make_employee("test_employee@example.com", company="_Test Company") + frappe.db.delete("Attendance") + + date = getdate() + from_date = get_year_start(date) + to_date = get_year_ending(date) + make_holiday_list(from_date=from_date, to_date=to_date) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_monthly_attendance_sheet_report(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value("Employee", self.employee, "company") + + # mark different attendance status on first 3 days of previous month + mark_attendance(self.employee, previous_month_first, "Absent") + mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present") + mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") + + filters = frappe._dict( + { + "month": previous_month, + "year": now.year, + "company": company, + } + ) + report = execute(filters=filters) + + record = report[1][0] + datasets = report[3]["data"]["datasets"] + absent = datasets[0]["values"] + present = datasets[1]["values"] + leaves = datasets[2]["values"] + + # ensure correct attendance is reflected on the report + self.assertEqual(self.employee, record.get("employee")) + self.assertEqual(absent[0], 1) + self.assertEqual(present[1], 1) + self.assertEqual(leaves[2], 1) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_monthly_attendance_sheet_with_detailed_view(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value("Employee", self.employee, "company") + + # attendance with shift + mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift") + mark_attendance( + self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift" + ) + + # attendance without shift + mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") + mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present") + + filters = frappe._dict( + { + "month": previous_month, + "year": now.year, + "company": company, + } + ) + report = execute(filters=filters) + + day_shift_row = report[1][0] + row_without_shift = report[1][1] + + self.assertEqual(day_shift_row["shift"], "Day Shift") + self.assertEqual(day_shift_row[1], "A") # absent on the 1st day of the month + self.assertEqual(day_shift_row[2], "P") # present on the 2nd day + + self.assertEqual(row_without_shift["shift"], None) + self.assertEqual(row_without_shift[3], "L") # on leave on the 3rd day + self.assertEqual(row_without_shift[4], "P") # present on the 4th day + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_monthly_attendance_sheet_with_summarized_view(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value("Employee", self.employee, "company") + + # attendance with shift + mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift") + mark_attendance( + self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift" + ) + mark_attendance( + self.employee, previous_month_first + relativedelta(days=2), "Half Day" + ) # half day + + mark_attendance( + self.employee, previous_month_first + relativedelta(days=3), "Present" + ) # attendance without shift + mark_attendance( + self.employee, previous_month_first + relativedelta(days=4), "Present", late_entry=1 + ) # late entry + mark_attendance( + self.employee, previous_month_first + relativedelta(days=5), "Present", early_exit=1 + ) # early exit + + leave_application = get_leave_application(self.employee) + + filters = frappe._dict( + {"month": previous_month, "year": now.year, "company": company, "summarized_view": 1} + ) + report = execute(filters=filters) + + row = report[1][0] + self.assertEqual(row["employee"], self.employee) + + # 4 present + half day absent 0.5 + self.assertEqual(row["total_present"], 4.5) + # 1 present + half day absent 0.5 + self.assertEqual(row["total_absent"], 1.5) + # leave days + half day leave 0.5 + self.assertEqual(row["total_leaves"], leave_application.total_leave_days + 0.5) + + self.assertEqual(row["_test_leave_type"], leave_application.total_leave_days) + self.assertEqual(row["total_late_entries"], 1) + self.assertEqual(row["total_early_exits"], 1) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_attendance_with_group_by_filter(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value("Employee", self.employee, "company") + + # attendance with shift + mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift") + mark_attendance( + self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift" + ) + + # attendance without shift + mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") + mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present") + + filters = frappe._dict( + {"month": previous_month, "year": now.year, "company": company, "group_by": "Department"} + ) + report = execute(filters=filters) + + department = frappe.db.get_value("Employee", self.employee, "department") + department_row = report[1][0] + self.assertIn(department, department_row["department"]) + + day_shift_row = report[1][1] + row_without_shift = report[1][2] + + self.assertEqual(day_shift_row["shift"], "Day Shift") + self.assertEqual(day_shift_row[1], "A") # absent on the 1st day of the month + self.assertEqual(day_shift_row[2], "P") # present on the 2nd day + + self.assertEqual(row_without_shift["shift"], None) + self.assertEqual(row_without_shift[3], "L") # on leave on the 3rd day + self.assertEqual(row_without_shift[4], "P") # present on the 4th day + + def test_attendance_with_employee_filter(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value("Employee", self.employee, "company") + + # mark different attendance status on first 3 days of previous month + mark_attendance(self.employee, previous_month_first, "Absent") + mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present") + mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") + + filters = frappe._dict( + {"month": previous_month, "year": now.year, "company": company, "employee": self.employee} + ) + report = execute(filters=filters) + + record = report[1][0] + datasets = report[3]["data"]["datasets"] + absent = datasets[0]["values"] + present = datasets[1]["values"] + leaves = datasets[2]["values"] + + # ensure correct attendance is reflected on the report + self.assertEqual(self.employee, record.get("employee")) + self.assertEqual(absent[0], 1) + self.assertEqual(present[1], 1) + self.assertEqual(leaves[2], 1) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_validations(self): + # validation error for filters without month and year + self.assertRaises(frappe.ValidationError, execute_report_with_invalid_filters) + + # execute report without attendance record + now = now_datetime() + previous_month = now.month - 1 + + company = frappe.db.get_value("Employee", self.employee, "company") + filters = frappe._dict( + {"month": previous_month, "year": now.year, "company": company, "group_by": "Department"} + ) + report = execute(filters=filters) + self.assertEqual(report, ([], [], None, None)) + + +def get_leave_application(employee): + now = now_datetime() + previous_month = now.month - 1 + + date = getdate() + year_start = getdate(get_year_start(date)) + year_end = getdate(get_year_ending(date)) + make_allocation_record(employee=employee, from_date=year_start, to_date=year_end) + + from_date = now.replace(day=7).replace(month=previous_month).date() + to_date = now.replace(day=8).replace(month=previous_month).date() + return make_leave_application(employee, from_date, to_date, "_Test Leave Type") + + +def execute_report_with_invalid_filters(): + filters = frappe._dict({"company": "_Test Company", "group_by": "Department"}) + execute(filters=filters) diff --git a/hrms/hr/report/project_profitability/__init__.py b/hrms/hr/report/project_profitability/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/project_profitability/project_profitability.js b/hrms/hr/report/project_profitability/project_profitability.js new file mode 100644 index 0000000..13ae19b --- /dev/null +++ b/hrms/hr/report/project_profitability/project_profitability.js @@ -0,0 +1,48 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Project Profitability"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname": "start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1) + }, + { + "fieldname": "end_date", + "label": __("End Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.now_date() + }, + { + "fieldname": "customer_name", + "label": __("Customer"), + "fieldtype": "Link", + "options": "Customer" + }, + { + "fieldname": "employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee" + }, + { + "fieldname": "project", + "label": __("Project"), + "fieldtype": "Link", + "options": "Project" + } + ] +}; diff --git a/hrms/hr/report/project_profitability/project_profitability.json b/hrms/hr/report/project_profitability/project_profitability.json new file mode 100644 index 0000000..40e5ee7 --- /dev/null +++ b/hrms/hr/report/project_profitability/project_profitability.json @@ -0,0 +1,44 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-04-16 15:50:28.914872", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2022-06-23 15:50:48.490866", + "modified_by": "Administrator", + "module": "HR", + "name": "Project Profitability", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Timesheet", + "report_name": "Project Profitability", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "Accounts User" + }, + { + "role": "Employee" + }, + { + "role": "Projects User" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Employee Self Service" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/project_profitability/project_profitability.py b/hrms/hr/report/project_profitability/project_profitability.py new file mode 100644 index 0000000..aa955bc --- /dev/null +++ b/hrms/hr/report/project_profitability/project_profitability.py @@ -0,0 +1,200 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.utils import flt + + +def execute(filters=None): + data = get_data(filters) + columns = get_columns() + charts = get_chart_data(data) + return columns, data, None, charts + + +def get_data(filters): + data = get_rows(filters) + data = calculate_cost_and_profit(data) + return data + + +def get_rows(filters): + conditions = get_conditions(filters) + standard_working_hours = frappe.db.get_single_value("HR Settings", "standard_working_hours") + if not standard_working_hours: + msg = _( + "The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}." + ).format( + frappe.bold("Standard Working Hours"), + frappe.utils.get_link_to_form("HR Settings", "HR Settings"), + ) + + frappe.msgprint(msg) + return [] + + sql = """ + SELECT + * + FROM + (SELECT + si.customer_name,si.base_grand_total, + si.name as voucher_no,`tabTimesheet`.employee, + `tabTimesheet`.title as employee_name,`tabTimesheet`.parent_project as project, + `tabTimesheet`.start_date,`tabTimesheet`.end_date, + `tabTimesheet`.total_billed_hours,`tabTimesheet`.name as timesheet, + ss.base_gross_pay,ss.total_working_days, + `tabTimesheet`.total_billed_hours/(ss.total_working_days * {0}) as utilization + FROM + `tabSalary Slip Timesheet` as sst join `tabTimesheet` on `tabTimesheet`.name = sst.time_sheet + join `tabSales Invoice Timesheet` as sit on sit.time_sheet = `tabTimesheet`.name + join `tabSales Invoice` as si on si.name = sit.parent and si.status != 'Cancelled' + join `tabSalary Slip` as ss on ss.name = sst.parent and ss.status != 'Cancelled' """.format( + standard_working_hours + ) + if conditions: + sql += """ + WHERE + {0}) as t""".format( + conditions + ) + return frappe.db.sql(sql, filters, as_dict=True) + + +def calculate_cost_and_profit(data): + for row in data: + row.fractional_cost = flt(row.base_gross_pay) * flt(row.utilization) + row.profit = flt(row.base_grand_total) - flt(row.base_gross_pay) * flt(row.utilization) + return data + + +def get_conditions(filters): + conditions = [] + + if filters.get("company"): + conditions.append("`tabTimesheet`.company={0}".format(frappe.db.escape(filters.get("company")))) + + if filters.get("start_date"): + conditions.append("`tabTimesheet`.start_date>='{0}'".format(filters.get("start_date"))) + + if filters.get("end_date"): + conditions.append("`tabTimesheet`.end_date<='{0}'".format(filters.get("end_date"))) + + if filters.get("customer_name"): + conditions.append("si.customer_name={0}".format(frappe.db.escape(filters.get("customer_name")))) + + if filters.get("employee"): + conditions.append( + "`tabTimesheet`.employee={0}".format(frappe.db.escape(filters.get("employee"))) + ) + + if filters.get("project"): + conditions.append( + "`tabTimesheet`.parent_project={0}".format(frappe.db.escape(filters.get("project"))) + ) + + conditions = " and ".join(conditions) + return conditions + + +def get_chart_data(data): + if not data: + return None + + labels = [] + utilization = [] + + for entry in data: + labels.append(entry.get("employee_name") + " - " + str(entry.get("end_date"))) + utilization.append(entry.get("utilization")) + + charts = { + "data": {"labels": labels, "datasets": [{"name": "Utilization", "values": utilization}]}, + "type": "bar", + "colors": ["#84BDD5"], + } + return charts + + +def get_columns(): + return [ + { + "fieldname": "customer_name", + "label": _("Customer"), + "fieldtype": "Link", + "options": "Customer", + "width": 150, + }, + { + "fieldname": "employee", + "label": _("Employee"), + "fieldtype": "Link", + "options": "Employee", + "width": 130, + }, + {"fieldname": "employee_name", "label": _("Employee Name"), "fieldtype": "Data", "width": 120}, + { + "fieldname": "voucher_no", + "label": _("Sales Invoice"), + "fieldtype": "Link", + "options": "Sales Invoice", + "width": 120, + }, + { + "fieldname": "timesheet", + "label": _("Timesheet"), + "fieldtype": "Link", + "options": "Timesheet", + "width": 120, + }, + { + "fieldname": "project", + "label": _("Project"), + "fieldtype": "Link", + "options": "Project", + "width": 100, + }, + { + "fieldname": "base_grand_total", + "label": _("Bill Amount"), + "fieldtype": "Currency", + "options": "currency", + "width": 100, + }, + { + "fieldname": "base_gross_pay", + "label": _("Cost"), + "fieldtype": "Currency", + "options": "currency", + "width": 100, + }, + { + "fieldname": "profit", + "label": _("Profit"), + "fieldtype": "Currency", + "options": "currency", + "width": 100, + }, + {"fieldname": "utilization", "label": _("Utilization"), "fieldtype": "Percentage", "width": 100}, + { + "fieldname": "fractional_cost", + "label": _("Fractional Cost"), + "fieldtype": "Int", + "width": 120, + }, + { + "fieldname": "total_billed_hours", + "label": _("Total Billed Hours"), + "fieldtype": "Int", + "width": 150, + }, + {"fieldname": "start_date", "label": _("Start Date"), "fieldtype": "Date", "width": 100}, + {"fieldname": "end_date", "label": _("End Date"), "fieldtype": "Date", "width": 100}, + { + "label": _("Currency"), + "fieldname": "currency", + "fieldtype": "Link", + "options": "Currency", + "width": 80, + }, + ] diff --git a/hrms/hr/report/project_profitability/test_project_profitability.py b/hrms/hr/report/project_profitability/test_project_profitability.py new file mode 100644 index 0000000..7cea22d --- /dev/null +++ b/hrms/hr/report/project_profitability/test_project_profitability.py @@ -0,0 +1,72 @@ +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, getdate + +from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet +from erpnext.projects.doctype.timesheet.timesheet import make_sales_invoice +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.report.project_profitability.project_profitability import execute +from hrms.payroll.doctype.salary_slip.salary_slip import make_salary_slip_from_timesheet +from hrms.payroll.doctype.salary_slip.test_salary_slip import make_salary_structure_for_timesheet + + +class TestProjectProfitability(FrappeTestCase): + def setUp(self): + frappe.db.sql("delete from `tabTimesheet`") + emp = make_employee("test_employee_9@salary.com", company="_Test Company") + + if not frappe.db.exists("Salary Component", "Timesheet Component"): + frappe.get_doc( + {"doctype": "Salary Component", "salary_component": "Timesheet Component"} + ).insert() + + make_salary_structure_for_timesheet(emp, company="_Test Company") + date = getdate() + + self.timesheet = make_timesheet(emp, is_billable=1) + self.salary_slip = make_salary_slip_from_timesheet(self.timesheet.name) + self.salary_slip.start_date = self.timesheet.start_date + + holidays = self.salary_slip.get_holidays_for_employee(date, date) + if holidays: + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) + + self.salary_slip.submit() + self.sales_invoice = make_sales_invoice(self.timesheet.name, "_Test Item", "_Test Customer") + self.sales_invoice.due_date = date + self.sales_invoice.submit() + + frappe.db.set_value("HR Settings", None, "standard_working_hours", 8) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) + + def test_project_profitability(self): + filters = { + "company": "_Test Company", + "start_date": add_days(self.timesheet.start_date, -3), + "end_date": self.timesheet.start_date, + } + + report = execute(filters) + + row = report[1][0] + timesheet = frappe.get_doc("Timesheet", self.timesheet.name) + + self.assertEqual(self.sales_invoice.customer, row.customer_name) + self.assertEqual(timesheet.title, row.employee_name) + self.assertEqual(self.sales_invoice.base_grand_total, row.base_grand_total) + self.assertEqual(self.salary_slip.base_gross_pay, row.base_gross_pay) + self.assertEqual(timesheet.total_billed_hours, row.total_billed_hours) + self.assertEqual(self.salary_slip.total_working_days, row.total_working_days) + + standard_working_hours = frappe.db.get_single_value("HR Settings", "standard_working_hours") + utilization = timesheet.total_billed_hours / ( + self.salary_slip.total_working_days * standard_working_hours + ) + self.assertEqual(utilization, row.utilization) + + profit = self.sales_invoice.base_grand_total - self.salary_slip.base_gross_pay * utilization + self.assertEqual(profit, row.profit) + + fractional_cost = self.salary_slip.base_gross_pay * utilization + self.assertEqual(fractional_cost, row.fractional_cost) diff --git a/hrms/hr/report/recruitment_analytics/__init__.py b/hrms/hr/report/recruitment_analytics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/recruitment_analytics/recruitment_analytics.js b/hrms/hr/report/recruitment_analytics/recruitment_analytics.js new file mode 100644 index 0000000..51dc7ff --- /dev/null +++ b/hrms/hr/report/recruitment_analytics/recruitment_analytics.js @@ -0,0 +1,23 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Recruitment Analytics"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname":"on_date", + "label": __("On Date"), + "fieldtype": "Date", + "default": frappe.datetime.now_date(), + "reqd": 1, + }, + ] +}; diff --git a/hrms/hr/report/recruitment_analytics/recruitment_analytics.json b/hrms/hr/report/recruitment_analytics/recruitment_analytics.json new file mode 100644 index 0000000..30a8e17 --- /dev/null +++ b/hrms/hr/report/recruitment_analytics/recruitment_analytics.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2020-05-14 16:28:45.743869", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-14 16:28:45.743869", + "modified_by": "Administrator", + "module": "HR", + "name": "Recruitment Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Staffing Plan", + "report_name": "Recruitment Analytics", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/recruitment_analytics/recruitment_analytics.py b/hrms/hr/report/recruitment_analytics/recruitment_analytics.py new file mode 100644 index 0000000..b6caf40 --- /dev/null +++ b/hrms/hr/report/recruitment_analytics/recruitment_analytics.py @@ -0,0 +1,198 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + + +def execute(filters=None): + + if not filters: + filters = {} + filters = frappe._dict(filters) + + columns = get_columns() + + data = get_data(filters) + + return columns, data + + +def get_columns(): + return [ + { + "label": _("Staffing Plan"), + "fieldtype": "Link", + "fieldname": "staffing_plan", + "options": "Staffing Plan", + "width": 150, + }, + { + "label": _("Job Opening"), + "fieldtype": "Link", + "fieldname": "job_opening", + "options": "Job Opening", + "width": 105, + }, + { + "label": _("Job Applicant"), + "fieldtype": "Link", + "fieldname": "job_applicant", + "options": "Job Applicant", + "width": 150, + }, + {"label": _("Applicant name"), "fieldtype": "data", "fieldname": "applicant_name", "width": 130}, + { + "label": _("Application Status"), + "fieldtype": "Data", + "fieldname": "application_status", + "width": 150, + }, + { + "label": _("Job Offer"), + "fieldtype": "Link", + "fieldname": "job_offer", + "options": "job Offer", + "width": 150, + }, + {"label": _("Designation"), "fieldtype": "Data", "fieldname": "designation", "width": 100}, + {"label": _("Offer Date"), "fieldtype": "date", "fieldname": "offer_date", "width": 100}, + { + "label": _("Job Offer status"), + "fieldtype": "Data", + "fieldname": "job_offer_status", + "width": 150, + }, + ] + + +def get_data(filters): + data = [] + staffing_plan_details = get_staffing_plan(filters) + staffing_plan_list = list(set([details["name"] for details in staffing_plan_details])) + sp_jo_map, jo_list = get_job_opening(staffing_plan_list) + jo_ja_map, ja_list = get_job_applicant(jo_list) + ja_joff_map = get_job_offer(ja_list) + + for sp in sp_jo_map.keys(): + parent_row = get_parent_row(sp_jo_map, sp, jo_ja_map, ja_joff_map) + data += parent_row + + return data + + +def get_parent_row(sp_jo_map, sp, jo_ja_map, ja_joff_map): + data = [] + if sp in sp_jo_map.keys(): + for jo in sp_jo_map[sp]: + row = { + "staffing_plan": sp, + "job_opening": jo["name"], + } + data.append(row) + child_row = get_child_row(jo["name"], jo_ja_map, ja_joff_map) + data += child_row + return data + + +def get_child_row(jo, jo_ja_map, ja_joff_map): + data = [] + if jo in jo_ja_map.keys(): + for ja in jo_ja_map[jo]: + row = { + "indent": 1, + "job_applicant": ja.name, + "applicant_name": ja.applicant_name, + "application_status": ja.status, + } + if ja.name in ja_joff_map.keys(): + jo_detail = ja_joff_map[ja.name][0] + row["job_offer"] = jo_detail.name + row["job_offer_status"] = jo_detail.status + row["offer_date"] = jo_detail.offer_date.strftime("%d-%m-%Y") + row["designation"] = jo_detail.designation + + data.append(row) + return data + + +def get_staffing_plan(filters): + + staffing_plan = frappe.db.sql( + """ + select + sp.name, sp.department, spd.designation, spd.vacancies, spd.current_count, spd.parent, sp.to_date + from + `tabStaffing Plan Detail` spd , `tabStaffing Plan` sp + where + spd.parent = sp.name + And + sp.to_date > '{0}' + """.format( + filters.on_date + ), + as_dict=1, + ) + + return staffing_plan + + +def get_job_opening(sp_list): + + job_openings = frappe.get_all( + "Job Opening", filters=[["staffing_plan", "IN", sp_list]], fields=["name", "staffing_plan"] + ) + + sp_jo_map = {} + jo_list = [] + + for openings in job_openings: + if openings.staffing_plan not in sp_jo_map.keys(): + sp_jo_map[openings.staffing_plan] = [openings] + else: + sp_jo_map[openings.staffing_plan].append(openings) + + jo_list.append(openings.name) + + return sp_jo_map, jo_list + + +def get_job_applicant(jo_list): + + jo_ja_map = {} + ja_list = [] + + applicants = frappe.get_all( + "Job Applicant", + filters=[["job_title", "IN", jo_list]], + fields=["name", "job_title", "applicant_name", "status"], + ) + + for applicant in applicants: + if applicant.job_title not in jo_ja_map.keys(): + jo_ja_map[applicant.job_title] = [applicant] + else: + jo_ja_map[applicant.job_title].append(applicant) + + ja_list.append(applicant.name) + + return jo_ja_map, ja_list + + +def get_job_offer(ja_list): + ja_joff_map = {} + + offers = frappe.get_all( + "Job Offer", + filters=[["job_applicant", "IN", ja_list]], + fields=["name", "job_applicant", "status", "offer_date", "designation"], + ) + + for offer in offers: + if offer.job_applicant not in ja_joff_map.keys(): + ja_joff_map[offer.job_applicant] = [offer] + else: + ja_joff_map[offer.job_applicant].append(offer) + + return ja_joff_map diff --git a/hrms/hr/report/unpaid_expense_claim/__init__.py b/hrms/hr/report/unpaid_expense_claim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.js b/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.js new file mode 100644 index 0000000..f0ba78c --- /dev/null +++ b/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.js @@ -0,0 +1,13 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Unpaid Expense Claim"] = { + "filters": [ + { + "fieldname": "employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee" + } + ] +} diff --git a/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.json b/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.json new file mode 100644 index 0000000..d2da882 --- /dev/null +++ b/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2017-01-04 16:26:18.309717", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2022-06-23 19:59:29.747039", + "modified_by": "Administrator", + "module": "HR", + "name": "Unpaid Expense Claim", + "owner": "Administrator", + "ref_doctype": "Expense Claim", + "report_name": "Unpaid Expense Claim", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + }, + { + "role": "Expense Approver" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.py b/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.py new file mode 100644 index 0000000..62b4f63 --- /dev/null +++ b/hrms/hr/report/unpaid_expense_claim/unpaid_expense_claim.py @@ -0,0 +1,49 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + + +def execute(filters=None): + columns, data = [], [] + columns = get_columns() + data = get_unclaimed_expese_claims(filters) + return columns, data + + +def get_columns(): + return [ + _("Employee") + ":Link/Employee:120", + _("Employee Name") + "::120", + _("Expense Claim") + ":Link/Expense Claim:120", + _("Sanctioned Amount") + ":Currency:120", + _("Paid Amount") + ":Currency:120", + _("Outstanding Amount") + ":Currency:150", + ] + + +def get_unclaimed_expese_claims(filters): + cond = "1=1" + if filters.get("employee"): + cond = "ec.employee = %(employee)s" + + return frappe.db.sql( + """ + select + ec.employee, ec.employee_name, ec.name, ec.total_sanctioned_amount, ec.total_amount_reimbursed, + sum(gle.credit_in_account_currency - gle.debit_in_account_currency) as outstanding_amt + from + `tabExpense Claim` ec, `tabGL Entry` gle + where + gle.against_voucher_type = "Expense Claim" and gle.against_voucher = ec.name + and gle.party is not null and ec.docstatus = 1 and ec.is_paid = 0 and {cond} group by ec.name + having + outstanding_amt > 0 + """.format( + cond=cond + ), + filters, + as_list=1, + ) diff --git a/hrms/hr/report/vehicle_expenses/__init__.py b/hrms/hr/report/vehicle_expenses/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py b/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py new file mode 100644 index 0000000..b60e23e --- /dev/null +++ b/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py @@ -0,0 +1,78 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import unittest + +import frappe +from frappe.utils import getdate + +from erpnext.accounts.utils import get_fiscal_year +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.vehicle_log.test_vehicle_log import get_vehicle, make_vehicle_log +from hrms.hr.doctype.vehicle_log.vehicle_log import make_expense_claim +from hrms.hr.report.vehicle_expenses.vehicle_expenses import execute + + +class TestVehicleExpenses(unittest.TestCase): + @classmethod + def setUpClass(self): + frappe.db.sql("delete from `tabVehicle Log`") + + employee_id = frappe.db.sql( + """select name from `tabEmployee` where name='testdriver@example.com'""" + ) + self.employee_id = employee_id[0][0] if employee_id else None + if not self.employee_id: + self.employee_id = make_employee("testdriver@example.com", company="_Test Company") + + self.license_plate = get_vehicle(self.employee_id) + + def test_vehicle_expenses_based_on_fiscal_year(self): + vehicle_log = make_vehicle_log(self.license_plate, self.employee_id, with_services=True) + expense_claim = make_expense_claim(vehicle_log.name) + + # Based on Fiscal Year + filters = {"filter_based_on": "Fiscal Year", "fiscal_year": get_fiscal_year(getdate())[0]} + + report = execute(filters) + + expected_data = [ + { + "vehicle": self.license_plate, + "make": "Maruti", + "model": "PCM", + "location": "Mumbai", + "log_name": vehicle_log.name, + "odometer": 5010, + "date": getdate(), + "fuel_qty": 50.0, + "fuel_price": 500.0, + "fuel_expense": 25000.0, + "service_expense": 2000.0, + "employee": self.employee_id, + } + ] + + self.assertEqual(report[1], expected_data) + + # Based on Date Range + fiscal_year = get_fiscal_year(getdate(), as_dict=True) + filters = { + "filter_based_on": "Date Range", + "from_date": fiscal_year.year_start_date, + "to_date": fiscal_year.year_end_date, + } + + report = execute(filters) + self.assertEqual(report[1], expected_data) + + # clean up + vehicle_log.cancel() + frappe.delete_doc("Expense Claim", expense_claim.name) + frappe.delete_doc("Vehicle Log", vehicle_log.name) + + def tearDown(self): + frappe.delete_doc("Vehicle", self.license_plate, force=1) + frappe.delete_doc("Employee", self.employee_id, force=1) diff --git a/hrms/hr/report/vehicle_expenses/vehicle_expenses.js b/hrms/hr/report/vehicle_expenses/vehicle_expenses.js new file mode 100644 index 0000000..2d0aa0f --- /dev/null +++ b/hrms/hr/report/vehicle_expenses/vehicle_expenses.js @@ -0,0 +1,51 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +frappe.query_reports["Vehicle Expenses"] = { + "filters": [ + { + "fieldname": "filter_based_on", + "label": __("Filter Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1 + }, + { + "fieldname": "fiscal_year", + "label": __("Fiscal Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", + "reqd": 1 + }, + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "depends_on": "eval: doc.filter_based_on == 'Date Range'", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12) + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "depends_on": "eval: doc.filter_based_on == 'Date Range'", + "default": frappe.datetime.nowdate() + }, + { + "fieldname": "vehicle", + "label": __("Vehicle"), + "fieldtype": "Link", + "options": "Vehicle" + }, + { + "fieldname": "employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee" + } + ] +}; diff --git a/hrms/hr/report/vehicle_expenses/vehicle_expenses.json b/hrms/hr/report/vehicle_expenses/vehicle_expenses.json new file mode 100644 index 0000000..1a3e5a9 --- /dev/null +++ b/hrms/hr/report/vehicle_expenses/vehicle_expenses.json @@ -0,0 +1,26 @@ +{ + "add_total_row": 1, + "columns": [], + "creation": "2016-09-09 03:33:40.605734", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 2, + "is_standard": "Yes", + "modified": "2021-05-16 22:48:22.767535", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Expenses", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Vehicle", + "report_name": "Vehicle Expenses", + "report_type": "Script Report", + "roles": [ + { + "role": "Fleet Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/hr/report/vehicle_expenses/vehicle_expenses.py b/hrms/hr/report/vehicle_expenses/vehicle_expenses.py new file mode 100644 index 0000000..fc5510d --- /dev/null +++ b/hrms/hr/report/vehicle_expenses/vehicle_expenses.py @@ -0,0 +1,177 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.utils import flt + +from erpnext.accounts.report.financial_statements import get_period_list + + +def execute(filters=None): + filters = frappe._dict(filters or {}) + + columns = get_columns() + data = get_vehicle_log_data(filters) + chart = get_chart_data(data, filters) + + return columns, data, None, chart + + +def get_columns(): + return [ + { + "fieldname": "vehicle", + "fieldtype": "Link", + "label": _("Vehicle"), + "options": "Vehicle", + "width": 150, + }, + {"fieldname": "make", "fieldtype": "Data", "label": _("Make"), "width": 100}, + {"fieldname": "model", "fieldtype": "Data", "label": _("Model"), "width": 80}, + {"fieldname": "location", "fieldtype": "Data", "label": _("Location"), "width": 100}, + { + "fieldname": "log_name", + "fieldtype": "Link", + "label": _("Vehicle Log"), + "options": "Vehicle Log", + "width": 100, + }, + {"fieldname": "odometer", "fieldtype": "Int", "label": _("Odometer Value"), "width": 120}, + {"fieldname": "date", "fieldtype": "Date", "label": _("Date"), "width": 100}, + {"fieldname": "fuel_qty", "fieldtype": "Float", "label": _("Fuel Qty"), "width": 80}, + {"fieldname": "fuel_price", "fieldtype": "Float", "label": _("Fuel Price"), "width": 100}, + {"fieldname": "fuel_expense", "fieldtype": "Currency", "label": _("Fuel Expense"), "width": 150}, + { + "fieldname": "service_expense", + "fieldtype": "Currency", + "label": _("Service Expense"), + "width": 150, + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "label": _("Employee"), + "options": "Employee", + "width": 150, + }, + ] + + +def get_vehicle_log_data(filters): + start_date, end_date = get_period_dates(filters) + conditions, values = get_conditions(filters) + + data = frappe.db.sql( + """ + SELECT + vhcl.license_plate as vehicle, vhcl.make, vhcl.model, + vhcl.location, log.name as log_name, log.odometer, + log.date, log.employee, log.fuel_qty, + log.price as fuel_price, + log.fuel_qty * log.price as fuel_expense + FROM + `tabVehicle` vhcl,`tabVehicle Log` log + WHERE + vhcl.license_plate = log.license_plate + and log.docstatus = 1 + and date between %(start_date)s and %(end_date)s + {0} + ORDER BY date""".format( + conditions + ), + values, + as_dict=1, + ) + + for row in data: + row["service_expense"] = get_service_expense(row.log_name) + + return data + + +def get_conditions(filters): + conditions = "" + + start_date, end_date = get_period_dates(filters) + values = {"start_date": start_date, "end_date": end_date} + + if filters.employee: + conditions += " and log.employee = %(employee)s" + values["employee"] = filters.employee + + if filters.vehicle: + conditions += " and vhcl.license_plate = %(vehicle)s" + values["vehicle"] = filters.vehicle + + return conditions, values + + +def get_period_dates(filters): + if filters.filter_based_on == "Fiscal Year" and filters.fiscal_year: + fy = frappe.db.get_value( + "Fiscal Year", filters.fiscal_year, ["year_start_date", "year_end_date"], as_dict=True + ) + return fy.year_start_date, fy.year_end_date + else: + return filters.from_date, filters.to_date + + +def get_service_expense(logname): + expense_amount = frappe.db.sql( + """ + SELECT sum(expense_amount) + FROM + `tabVehicle Log` log, `tabVehicle Service` service + WHERE + service.parent=log.name and log.name=%s + """, + logname, + ) + + return flt(expense_amount[0][0]) if expense_amount else 0.0 + + +def get_chart_data(data, filters): + period_list = get_period_list( + filters.fiscal_year, + filters.fiscal_year, + filters.from_date, + filters.to_date, + filters.filter_based_on, + "Monthly", + ) + + fuel_data, service_data = [], [] + + for period in period_list: + total_fuel_exp = 0 + total_service_exp = 0 + + for row in data: + if row.date <= period.to_date and row.date >= period.from_date: + total_fuel_exp += flt(row.fuel_expense) + total_service_exp += flt(row.service_expense) + + fuel_data.append([period.key, total_fuel_exp]) + service_data.append([period.key, total_service_exp]) + + labels = [period.label for period in period_list] + fuel_exp_data = [row[1] for row in fuel_data] + service_exp_data = [row[1] for row in service_data] + + datasets = [] + if fuel_exp_data: + datasets.append({"name": _("Fuel Expenses"), "values": fuel_exp_data}) + + if service_exp_data: + datasets.append({"name": _("Service Expenses"), "values": service_exp_data}) + + chart = { + "data": {"labels": labels, "datasets": datasets}, + "type": "line", + "fieldtype": "Currency", + } + + return chart diff --git a/hrms/hr/utils.py b/hrms/hr/utils.py new file mode 100644 index 0000000..c2c1233 --- /dev/null +++ b/hrms/hr/utils.py @@ -0,0 +1,676 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import ( + add_days, + cstr, + flt, + format_datetime, + formatdate, + get_datetime, + get_link_to_form, + getdate, + nowdate, + today, +) + +import erpnext +from erpnext import get_company_currency +from erpnext.setup.doctype.employee.employee import ( + InactiveEmployeeStatusError, + get_holiday_list_for_employee, +) + + +class DuplicateDeclarationError(frappe.ValidationError): + pass + + +def set_employee_name(doc): + if doc.employee and not doc.employee_name: + doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name") + + +def update_employee_work_history(employee, details, date=None, cancel=False): + if not employee.internal_work_history and not cancel: + employee.append( + "internal_work_history", + { + "branch": employee.branch, + "designation": employee.designation, + "department": employee.department, + "from_date": employee.date_of_joining, + }, + ) + + internal_work_history = {} + for item in details: + field = frappe.get_meta("Employee").get_field(item.fieldname) + if not field: + continue + fieldtype = field.fieldtype + new_data = item.new if not cancel else item.current + if fieldtype == "Date" and new_data: + new_data = getdate(new_data) + elif fieldtype == "Datetime" and new_data: + new_data = get_datetime(new_data) + setattr(employee, item.fieldname, new_data) + if item.fieldname in ["department", "designation", "branch"]: + internal_work_history[item.fieldname] = item.new + + if internal_work_history and not cancel: + internal_work_history["from_date"] = date + employee.append("internal_work_history", internal_work_history) + + if cancel: + delete_employee_work_history(details, employee, date) + + return employee + + +def delete_employee_work_history(details, employee, date): + filters = {} + for d in details: + for history in employee.internal_work_history: + if d.property == "Department" and history.department == d.new: + department = d.new + filters["department"] = department + if d.property == "Designation" and history.designation == d.new: + designation = d.new + filters["designation"] = designation + if d.property == "Branch" and history.branch == d.new: + branch = d.new + filters["branch"] = branch + if date and date == history.from_date: + filters["from_date"] = date + if filters: + frappe.db.delete("Employee Internal Work History", filters) + employee.save() + + +@frappe.whitelist() +def get_employee_field_property(employee, fieldname): + if employee and fieldname: + field = frappe.get_meta("Employee").get_field(fieldname) + value = frappe.db.get_value("Employee", employee, fieldname) + options = field.options + if field.fieldtype == "Date": + value = formatdate(value) + elif field.fieldtype == "Datetime": + value = format_datetime(value) + return {"value": value, "datatype": field.fieldtype, "label": field.label, "options": options} + else: + return False + + +def validate_dates(doc, from_date, to_date): + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", doc.employee, ["date_of_joining", "relieving_date"] + ) + if getdate(from_date) > getdate(to_date): + frappe.throw(_("To date can not be less than from date")) + elif getdate(from_date) > getdate(nowdate()): + frappe.throw(_("Future dates not allowed")) + elif date_of_joining and getdate(from_date) < getdate(date_of_joining): + frappe.throw(_("From date can not be less than employee's joining date")) + elif relieving_date and getdate(to_date) > getdate(relieving_date): + frappe.throw(_("To date can not greater than employee's relieving date")) + + +def validate_overlap(doc, from_date, to_date, company=None): + query = """ + select name + from `tab{0}` + where name != %(name)s + """ + query += get_doc_condition(doc.doctype) + + if not doc.name: + # hack! if name is null, it could cause problems with != + doc.name = "New " + doc.doctype + + overlap_doc = frappe.db.sql( + query.format(doc.doctype), + { + "employee": doc.get("employee"), + "from_date": from_date, + "to_date": to_date, + "name": doc.name, + "company": company, + }, + as_dict=1, + ) + + if overlap_doc: + if doc.get("employee"): + exists_for = doc.employee + if company: + exists_for = company + throw_overlap_error(doc, exists_for, overlap_doc[0].name, from_date, to_date) + + +def get_doc_condition(doctype): + if doctype == "Compensatory Leave Request": + return "and employee = %(employee)s and docstatus < 2 \ + and (work_from_date between %(from_date)s and %(to_date)s \ + or work_end_date between %(from_date)s and %(to_date)s \ + or (work_from_date < %(from_date)s and work_end_date > %(to_date)s))" + elif doctype == "Leave Period": + return "and company = %(company)s and (from_date between %(from_date)s and %(to_date)s \ + or to_date between %(from_date)s and %(to_date)s \ + or (from_date < %(from_date)s and to_date > %(to_date)s))" + + +def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date): + msg = ( + _("A {0} exists between {1} and {2} (").format( + doc.doctype, formatdate(from_date), formatdate(to_date) + ) + + """ {1}""".format(doc.doctype, overlap_doc) + + _(") for {0}").format(exists_for) + ) + frappe.throw(msg) + + +def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee): + existing_record = frappe.db.exists( + doctype, + { + "payroll_period": payroll_period, + "employee": employee, + "docstatus": ["<", 2], + "name": ["!=", docname], + }, + ) + if existing_record: + frappe.throw( + _("{0} already exists for employee {1} and period {2}").format( + doctype, employee, payroll_period + ), + DuplicateDeclarationError, + ) + + +def validate_tax_declaration(declarations): + subcategories = [] + for d in declarations: + if d.exemption_sub_category in subcategories: + frappe.throw(_("More than one selection for {0} not allowed").format(d.exemption_sub_category)) + subcategories.append(d.exemption_sub_category) + + +def get_total_exemption_amount(declarations): + exemptions = frappe._dict() + for d in declarations: + exemptions.setdefault(d.exemption_category, frappe._dict()) + category_max_amount = exemptions.get(d.exemption_category).max_amount + if not category_max_amount: + category_max_amount = frappe.db.get_value( + "Employee Tax Exemption Category", d.exemption_category, "max_amount" + ) + exemptions.get(d.exemption_category).max_amount = category_max_amount + sub_category_exemption_amount = ( + d.max_amount if (d.max_amount and flt(d.amount) > flt(d.max_amount)) else d.amount + ) + + exemptions.get(d.exemption_category).setdefault("total_exemption_amount", 0.0) + exemptions.get(d.exemption_category).total_exemption_amount += flt(sub_category_exemption_amount) + + if ( + category_max_amount + and exemptions.get(d.exemption_category).total_exemption_amount > category_max_amount + ): + exemptions.get(d.exemption_category).total_exemption_amount = category_max_amount + + total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()]) + return total_exemption_amount + + +@frappe.whitelist() +def get_leave_period(from_date, to_date, company): + leave_period = frappe.db.sql( + """ + select name, from_date, to_date + from `tabLeave Period` + where company=%(company)s and is_active=1 + and (from_date between %(from_date)s and %(to_date)s + or to_date between %(from_date)s and %(to_date)s + or (from_date < %(from_date)s and to_date > %(to_date)s)) + """, + {"from_date": from_date, "to_date": to_date, "company": company}, + as_dict=1, + ) + + if leave_period: + return leave_period + + +def generate_leave_encashment(): + """Generates a draft leave encashment on allocation expiry""" + from hrms.hr.doctype.leave_encashment.leave_encashment import create_leave_encashment + + if frappe.db.get_single_value("HR Settings", "auto_leave_encashment"): + leave_type = frappe.get_all("Leave Type", filters={"allow_encashment": 1}, fields=["name"]) + leave_type = [l["name"] for l in leave_type] + + leave_allocation = frappe.get_all( + "Leave Allocation", + filters={"to_date": add_days(today(), -1), "leave_type": ("in", leave_type)}, + fields=[ + "employee", + "leave_period", + "leave_type", + "to_date", + "total_leaves_allocated", + "new_leaves_allocated", + ], + ) + + create_leave_encashment(leave_allocation=leave_allocation) + + +def allocate_earned_leaves(): + """Allocate earned leaves to Employees""" + e_leave_types = get_earned_leaves() + today = getdate() + + for e_leave_type in e_leave_types: + + leave_allocations = get_leave_allocations(today, e_leave_type.name) + + for allocation in leave_allocations: + + if not allocation.leave_policy_assignment and not allocation.leave_policy: + continue + + leave_policy = ( + allocation.leave_policy + if allocation.leave_policy + else frappe.db.get_value( + "Leave Policy Assignment", allocation.leave_policy_assignment, ["leave_policy"] + ) + ) + + annual_allocation = frappe.db.get_value( + "Leave Policy Detail", + filters={"parent": leave_policy, "leave_type": e_leave_type.name}, + fieldname=["annual_allocation"], + ) + + from_date = allocation.from_date + + if e_leave_type.based_on_date_of_joining: + from_date = frappe.db.get_value("Employee", allocation.employee, "date_of_joining") + + if check_effective_date( + from_date, today, e_leave_type.earned_leave_frequency, e_leave_type.based_on_date_of_joining + ): + update_previous_leave_allocation(allocation, annual_allocation, e_leave_type) + + +def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type): + earned_leaves = get_monthly_earned_leave( + annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding + ) + + allocation = frappe.get_doc("Leave Allocation", allocation.name) + new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) + + if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: + new_allocation = e_leave_type.max_leaves_allowed + + if new_allocation != allocation.total_leaves_allocated: + today_date = today() + + allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) + create_additional_leave_ledger_entry(allocation, earned_leaves, today_date) + + if e_leave_type.based_on_date_of_joining: + text = _("allocated {0} leave(s) via scheduler on {1} based on the date of joining").format( + frappe.bold(earned_leaves), frappe.bold(formatdate(today_date)) + ) + else: + text = _("allocated {0} leave(s) via scheduler on {1}").format( + frappe.bold(earned_leaves), frappe.bold(formatdate(today_date)) + ) + + allocation.add_comment(comment_type="Info", text=text) + + +def get_monthly_earned_leave(annual_leaves, frequency, rounding): + earned_leaves = 0.0 + divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} + if annual_leaves: + earned_leaves = flt(annual_leaves) / divide_by_frequency[frequency] + if rounding: + if rounding == "0.25": + earned_leaves = round(earned_leaves * 4) / 4 + elif rounding == "0.5": + earned_leaves = round(earned_leaves * 2) / 2 + else: + earned_leaves = round(earned_leaves) + + return earned_leaves + + +def is_earned_leave_already_allocated(allocation, annual_allocation): + from hrms.hr.doctype.leave_policy_assignment.leave_policy_assignment import get_leave_type_details + + leave_type_details = get_leave_type_details() + date_of_joining = frappe.db.get_value("Employee", allocation.employee, "date_of_joining") + + assignment = frappe.get_doc("Leave Policy Assignment", allocation.leave_policy_assignment) + leaves_for_passed_months = assignment.get_leaves_for_passed_months( + allocation.leave_type, annual_allocation, leave_type_details, date_of_joining + ) + + # exclude carry-forwarded leaves while checking for leave allocation for passed months + num_allocations = allocation.total_leaves_allocated + if allocation.unused_leaves: + num_allocations -= allocation.unused_leaves + + if num_allocations >= leaves_for_passed_months: + return True + return False + + +def get_leave_allocations(date, leave_type): + return frappe.db.sql( + """select name, employee, from_date, to_date, leave_policy_assignment, leave_policy + from `tabLeave Allocation` + where + %s between from_date and to_date and docstatus=1 + and leave_type=%s""", + (date, leave_type), + as_dict=1, + ) + + +def get_earned_leaves(): + return frappe.get_all( + "Leave Type", + fields=[ + "name", + "max_leaves_allowed", + "earned_leave_frequency", + "rounding", + "based_on_date_of_joining", + ], + filters={"is_earned_leave": 1}, + ) + + +def create_additional_leave_ledger_entry(allocation, leaves, date): + """Create leave ledger entry for leave types""" + allocation.new_leaves_allocated = leaves + allocation.from_date = date + allocation.unused_leaves = 0 + allocation.create_leave_ledger_entry() + + +def check_effective_date(from_date, to_date, frequency, based_on_date_of_joining): + import calendar + + from dateutil import relativedelta + + from_date = get_datetime(from_date) + to_date = get_datetime(to_date) + rd = relativedelta.relativedelta(to_date, from_date) + # last day of month + last_day = calendar.monthrange(to_date.year, to_date.month)[1] + + if (from_date.day == to_date.day and based_on_date_of_joining) or ( + not based_on_date_of_joining and to_date.day == last_day + ): + if frequency == "Monthly": + return True + elif frequency == "Quarterly" and rd.months % 3: + return True + elif frequency == "Half-Yearly" and rd.months % 6: + return True + elif frequency == "Yearly" and rd.months % 12: + return True + + if frappe.flags.in_test: + return True + + return False + + +def get_salary_assignments(employee, payroll_period): + start_date, end_date = frappe.db.get_value( + "Payroll Period", payroll_period, ["start_date", "end_date"] + ) + assignments = frappe.db.get_all( + "Salary Structure Assignment", + filters={"employee": employee, "docstatus": 1, "from_date": ["between", (start_date, end_date)]}, + fields=["*"], + order_by="from_date", + ) + + return assignments + + +def get_sal_slip_total_benefit_given(employee, payroll_period, component=False): + total_given_benefit_amount = 0 + query = """ + select sum(sd.amount) as total_amount + from `tabSalary Slip` ss, `tabSalary Detail` sd + where ss.employee=%(employee)s + and ss.docstatus = 1 and ss.name = sd.parent + and sd.is_flexible_benefit = 1 and sd.parentfield = "earnings" + and sd.parenttype = "Salary Slip" + and (ss.start_date between %(start_date)s and %(end_date)s + or ss.end_date between %(start_date)s and %(end_date)s + or (ss.start_date < %(start_date)s and ss.end_date > %(end_date)s)) + """ + + if component: + query += "and sd.salary_component = %(component)s" + + sum_of_given_benefit = frappe.db.sql( + query, + { + "employee": employee, + "start_date": payroll_period.start_date, + "end_date": payroll_period.end_date, + "component": component, + }, + as_dict=True, + ) + + if sum_of_given_benefit and flt(sum_of_given_benefit[0].total_amount) > 0: + total_given_benefit_amount = sum_of_given_benefit[0].total_amount + return total_given_benefit_amount + + +def get_holiday_dates_for_employee(employee, start_date, end_date): + """return a list of holiday dates for the given employee between start_date and end_date""" + # return only date + holidays = get_holidays_for_employee(employee, start_date, end_date) + + return [cstr(h.holiday_date) for h in holidays] + + +def get_holidays_for_employee( + employee, start_date, end_date, raise_exception=True, only_non_weekly=False +): + """Get Holidays for a given employee + + `employee` (str) + `start_date` (str or datetime) + `end_date` (str or datetime) + `raise_exception` (bool) + `only_non_weekly` (bool) + + return: list of dicts with `holiday_date` and `description` + """ + holiday_list = get_holiday_list_for_employee(employee, raise_exception=raise_exception) + + if not holiday_list: + return [] + + filters = {"parent": holiday_list, "holiday_date": ("between", [start_date, end_date])} + + if only_non_weekly: + filters["weekly_off"] = False + + holidays = frappe.get_all( + "Holiday", fields=["description", "holiday_date"], filters=filters, order_by="holiday_date" + ) + + return holidays + + +@erpnext.allow_regional +def calculate_annual_eligible_hra_exemption(doc): + # Don't delete this method, used for localization + # Indian HRA Exemption Calculation + return {} + + +@erpnext.allow_regional +def calculate_hra_exemption_for_period(doc): + # Don't delete this method, used for localization + # Indian HRA Exemption Calculation + return {} + + +def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, component=False): + total_claimed_amount = 0 + query = """ + select sum(claimed_amount) as 'total_amount' + from `tabEmployee Benefit Claim` + where employee=%(employee)s + and docstatus = 1 + and (claim_date between %(start_date)s and %(end_date)s) + """ + if non_pro_rata: + query += "and pay_against_benefit_claim = 1" + if component: + query += "and earning_component = %(component)s" + + sum_of_claimed_amount = frappe.db.sql( + query, + { + "employee": employee, + "start_date": payroll_period.start_date, + "end_date": payroll_period.end_date, + "component": component, + }, + as_dict=True, + ) + if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0: + total_claimed_amount = sum_of_claimed_amount[0].total_amount + return total_claimed_amount + + +def share_doc_with_approver(doc, user): + # if approver does not have permissions, share + if not frappe.has_permission(doc=doc, ptype="submit", user=user): + frappe.share.add(doc.doctype, doc.name, user, submit=1, flags={"ignore_share_permission": True}) + + frappe.msgprint( + _("Shared with the user {0} with {1} access").format(user, frappe.bold("submit"), alert=True) + ) + + # remove shared doc if approver changes + doc_before_save = doc.get_doc_before_save() + if doc_before_save: + approvers = { + "Leave Application": "leave_approver", + "Expense Claim": "expense_approver", + "Shift Request": "approver", + } + + approver = approvers.get(doc.doctype) + if doc_before_save.get(approver) != doc.get(approver): + frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver)) + + +def validate_active_employee(employee, method=None): + if isinstance(employee, (dict, Document)): + employee = employee.get("employee") + + if employee and frappe.db.get_value("Employee", employee, "status") == "Inactive": + frappe.throw( + _("Transactions cannot be created for an Inactive Employee {0}.").format( + get_link_to_form("Employee", employee) + ), + InactiveEmployeeStatusError, + ) + + +def validate_loan_repay_from_salary(doc, method=None): + if doc.applicant_type == "Employee" and doc.repay_from_salary: + from hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( + get_employee_currency, + ) + + if not doc.applicant: + frappe.throw(_("Please select an Applicant")) + + if not doc.company: + frappe.throw(_("Please select a Company")) + + employee_currency = get_employee_currency(doc.applicant) + company_currency = erpnext.get_company_currency(doc.company) + if employee_currency != company_currency: + frappe.throw( + _( + "Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}" + ).format(doc.applicant, employee_currency) + ) + + if not doc.is_term_loan and doc.repay_from_salary: + frappe.throw(_("Repay From Salary can be selected only for term loans")) + + +def get_matching_queries( + bank_account, company, transaction, document_types, amount_condition, account_from_to +): + """Returns matching queries for Bank Reconciliation""" + queries = [] + if transaction.withdrawal > 0: + if "expense_claim" in document_types: + ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition) + queries.extend([ec_amount_matching]) + + return queries + + +def get_ec_matching_query(bank_account, company, amount_condition): + # get matching Expense Claim query + mode_of_payments = [ + x["parent"] + for x in frappe.db.get_all( + "Mode of Payment Account", filters={"default_account": bank_account}, fields=["parent"] + ) + ] + mode_of_payments = "('" + "', '".join(mode_of_payments) + "' )" + company_currency = get_company_currency(company) + return f""" + SELECT + ( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END + + 1 ) AS rank, + 'Expense Claim' as doctype, + name, + total_sanctioned_amount as paid_amount, + '' as reference_no, + '' as reference_date, + employee as party, + 'Employee' as party_type, + posting_date, + '{company_currency}' as currency + FROM + `tabExpense Claim` + WHERE + total_sanctioned_amount {amount_condition} %(amount)s + AND docstatus = 1 + AND is_paid = 1 + AND ifnull(clearance_date, '') = "" + AND mode_of_payment in {mode_of_payments} + """ diff --git a/hrms/hr/web_form/__init__.py b/hrms/hr/web_form/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/web_form/job_application/__init__.py b/hrms/hr/web_form/job_application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/hr/web_form/job_application/job_application.js b/hrms/hr/web_form/job_application/job_application.js new file mode 100644 index 0000000..ffc5e98 --- /dev/null +++ b/hrms/hr/web_form/job_application/job_application.js @@ -0,0 +1,3 @@ +frappe.ready(function() { + // bind events here +}) diff --git a/hrms/hr/web_form/job_application/job_application.json b/hrms/hr/web_form/job_application/job_application.json new file mode 100644 index 0000000..512ba5c --- /dev/null +++ b/hrms/hr/web_form/job_application/job_application.json @@ -0,0 +1,200 @@ +{ + "accept_payment": 0, + "allow_comments": 1, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "apply_document_permissions": 0, + "client_script": "frappe.web_form.on('resume_link', (field, value) => {\n if (!frappe.utils.is_url(value)) {\n frappe.msgprint(__('Resume link not valid'));\n }\n});\n", + "creation": "2016-09-10 02:53:16.598314", + "doc_type": "Job Applicant", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "introduction_text": "", + "is_standard": 1, + "login_required": 0, + "max_attachment_size": 0, + "modified": "2020-10-07 19:27:17.143355", + "modified_by": "Administrator", + "module": "HR", + "name": "job-application", + "owner": "Administrator", + "published": 1, + "route": "job_application", + "route_to_success_link": 0, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 1, + "sidebar_items": [], + "success_message": "Thank you for applying.", + "success_url": "/jobs", + "title": "Job Application", + "web_form_fields": [ + { + "allow_read_on_all_link_options": 0, + "fieldname": "job_title", + "fieldtype": "Data", + "hidden": 0, + "label": "Job Opening", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 1, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "applicant_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Applicant Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "email_id", + "fieldtype": "Data", + "hidden": 0, + "label": "Email Address", + "max_length": 0, + "max_value": 0, + "options": "Email", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "phone_number", + "fieldtype": "Data", + "hidden": 0, + "label": "Phone Number", + "max_length": 0, + "max_value": 0, + "options": "Phone", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "country", + "fieldtype": "Link", + "hidden": 0, + "label": "Country of Residence", + "max_length": 0, + "max_value": 0, + "options": "Country", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "cover_letter", + "fieldtype": "Text", + "hidden": 0, + "label": "Cover Letter", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "resume_link", + "fieldtype": "Data", + "hidden": 0, + "label": "Resume Link", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Section Break", + "hidden": 0, + "label": "Expected Salary Range per month", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 0, + "label": "Currency", + "max_length": 0, + "max_value": 0, + "options": "Currency", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "lower_range", + "fieldtype": "Currency", + "hidden": 0, + "label": "Lower Range", + "max_length": 0, + "max_value": 0, + "options": "currency", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "upper_range", + "fieldtype": "Currency", + "hidden": 0, + "label": "Upper Range", + "max_length": 0, + "max_value": 0, + "options": "currency", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + } + ] +} \ No newline at end of file diff --git a/hrms/hr/web_form/job_application/job_application.py b/hrms/hr/web_form/job_application/job_application.py new file mode 100644 index 0000000..02e3e93 --- /dev/null +++ b/hrms/hr/web_form/job_application/job_application.py @@ -0,0 +1,3 @@ +def get_context(context): + # do your magic here + pass diff --git a/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json b/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json new file mode 100644 index 0000000..a41d644 --- /dev/null +++ b/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json @@ -0,0 +1,353 @@ +{ + "charts": [], + "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Onboarding\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Separation\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Grievance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"quick_list\",\"data\":{\"quick_list_name\":\"New Hires (This Month)\",\"col\":4}},{\"type\":\"quick_list\",\"data\":{\"quick_list_name\":\"Exits (This Month)\",\"col\":4}},{\"type\":\"quick_list\",\"data\":{\"quick_list_name\":\"Trainings (This Week)\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Onboarding\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Journey\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Grievance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Training\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Exit\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Daily Work Summary\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "creation": "2022-08-20 14:06:34.309347", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "assign", + "idx": 0, + "label": "Employee Lifecycle", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Onboarding", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Onboarding Template", + "link_count": 0, + "link_to": "Employee Onboarding Template", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Onboarding", + "link_count": 0, + "link_to": "Employee Onboarding", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Skill Map", + "link_count": 0, + "link_to": "Employee Skill Map", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Journey", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Promotion", + "link_count": 0, + "link_to": "Employee Promotion", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Transfer", + "link_count": 0, + "link_to": "Employee Transfer", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Grievance", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Grievance Type", + "link_count": 0, + "link_to": "Grievance Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Grievance", + "link_count": 0, + "link_to": "Employee Grievance", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exit", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Separation Template", + "link_count": 0, + "link_to": "Employee Separation Template", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Separation", + "link_count": 0, + "link_to": "Employee Separation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Full and Final Settlement", + "link_count": 0, + "link_to": "Full and Final Statement", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exit Interview", + "link_count": 0, + "link_to": "Exit Interview", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Program", + "link_count": 0, + "link_to": "Training Program", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Event", + "link_count": 0, + "link_to": "Training Event", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Feedback", + "link_count": 0, + "link_to": "Training Feedback", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Result", + "link_count": 0, + "link_to": "Training Result", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Daily Work Summary", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Daily Work Summary", + "link_count": 0, + "link_to": "Daily Work Summary", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Daily Work Summary Group", + "link_count": 0, + "link_to": "Daily Work Summary Group", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Daily Work Summary Replies", + "link_count": 0, + "link_to": "Daily Work Summary Replies", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Exits", + "link_count": 0, + "link_to": "Employee Exits", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Birthday", + "link_count": 0, + "link_to": "Employee Birthday", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Information", + "link_count": 0, + "link_to": "Employee Information", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Analytics", + "link_count": 0, + "link_to": "Employee Analytics", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 11:35:33.462001", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Lifecycle", + "owner": "Administrator", + "parent_page": "HR", + "public": 1, + "quick_lists": [ + { + "document_type": "Employee", + "label": "New Hires (This Month)", + "quick_list_filter": "{\"date_of_joining\":[\"Timespan\",\"this month\"]}" + }, + { + "document_type": "Training Event", + "label": "Trainings (This Week)", + "quick_list_filter": "{\"start_time\":[\"Timespan\",\"this week\"]}" + }, + { + "document_type": "Employee", + "label": "Exits (This Month)", + "quick_list_filter": "{\"relieving_date\":[\"Timespan\",\"this month\"]}" + } + ], + "roles": [], + "sequence_id": 6.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Dashboard", + "link_to": "Employee Lifecycle", + "type": "Dashboard" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Pending", + "label": "Employee Onboarding", + "link_to": "Employee Onboarding", + "stats_filter": "{\"boarding_status\":[\"=\",\"Pending\"]}", + "type": "DocType" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Pending", + "label": "Employee Separation", + "link_to": "Employee Separation", + "stats_filter": "{\"boarding_status\":[\"=\",\"Pending\"]}", + "type": "DocType" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Open", + "label": "Employee Grievance", + "link_to": "Employee Grievance", + "stats_filter": "{\"status\":[\"=\",\"Open\"]}", + "type": "DocType" + } + ], + "title": "Employee Lifecycle" +} \ No newline at end of file diff --git a/hrms/hr/workspace/expense_claims/expense_claims.json b/hrms/hr/workspace/expense_claims/expense_claims.json new file mode 100644 index 0000000..095e89a --- /dev/null +++ b/hrms/hr/workspace/expense_claims/expense_claims.json @@ -0,0 +1,285 @@ +{ + "charts": [ + { + "chart_name": "Expense Claims", + "label": "Expense Claims" + } + ], + "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Expense Claims\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Expense Claim\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Advance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Claims\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Advances\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Fleet Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Travel\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Reports\",\"col\":4}}]", + "creation": "2022-08-20 16:28:40.701015", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 1, + "icon": "expenses", + "idx": 0, + "label": "Expense Claims", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Claims", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Expense Claim", + "link_count": 0, + "link_to": "Expense Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Expense Claim Type", + "link_count": 0, + "link_to": "Expense Claim Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Travel", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Travel Request", + "link_count": 0, + "link_to": "Travel Request", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purpose of Travel", + "link_count": 0, + "link_to": "Purpose of Travel", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Advances", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Advance", + "link_count": 0, + "link_to": "Employee Advance", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Entry", + "link_count": 0, + "link_to": "Payment Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Journal Entry", + "link_count": 0, + "link_to": "Journal Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Additional Salary", + "link_count": 0, + "link_to": "Additional Salary", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fleet Management", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Vehicle", + "link_count": 0, + "link_to": "Vehicle", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Driver", + "link_count": 0, + "link_to": "Driver", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Vehicle Log", + "link_count": 0, + "link_to": "Vehicle Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Vehicle Expenses", + "link_count": 0, + "link_to": "Vehicle Expenses", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Advance Summary", + "link_count": 0, + "link_to": "Employee Advance Summary", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Unpaid Expense Claim", + "link_count": 0, + "link_to": "Unpaid Expense Claim", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Vehicle Expenses", + "link_count": 0, + "link_to": "Vehicle Expenses", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Reports", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Receivable", + "link_count": 0, + "link_to": "Accounts Receivable", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Payable", + "link_count": 0, + "link_to": "Accounts Payable", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "General Ledger", + "link_count": 0, + "link_to": "General Ledger", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-21 12:49:49.047356", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claims", + "owner": "Administrator", + "parent_page": "HR", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 8.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Dashboard", + "link_to": "Expense Claims", + "type": "Dashboard" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Unclaimed", + "label": "Employee Advance", + "link_to": "Employee Advance", + "stats_filter": "{\"status\":[\"=\",\"Paid\"]}", + "type": "DocType" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Pending", + "label": "Expense Claim", + "link_to": "Expense Claim", + "stats_filter": "{\"status\":[\"=\",\"Draft\"]}", + "type": "DocType" + } + ], + "title": "Expense Claims" +} \ No newline at end of file diff --git a/hrms/hr/workspace/hr/hr.json b/hrms/hr/workspace/hr/hr.json new file mode 100644 index 0000000..1ed8b31 --- /dev/null +++ b/hrms/hr/workspace/hr/hr.json @@ -0,0 +1,342 @@ +{ + "charts": [ + { + "chart_name": "Hiring vs Attrition Count", + "label": "Hiring vs Attrition Count" + } + ], + "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Human Resource\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Hiring vs Attrition Count\",\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"HR Dashboard\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Recruitment Dashboard\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Lifecycle Dashboard\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Attendance Dashboard\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Expense Claims Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Setup\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", + "creation": "2020-03-02 15:48:58.322521", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "hr", + "idx": 0, + "label": "HR", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "HR Settings", + "link_count": 0, + "link_to": "HR Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Daily Work Summary Group", + "link_count": 0, + "link_to": "Daily Work Summary Group", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Team Updates", + "link_count": 0, + "link_to": "team-updates", + "link_type": "Page", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Setup", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Company", + "link_count": 0, + "link_to": "Company", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Branch", + "link_count": 0, + "link_to": "Branch", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Department", + "link_count": 0, + "link_to": "Department", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Designation", + "link_count": 0, + "link_to": "Designation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee", + "link_count": 0, + "link_to": "Employee", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Group", + "link_count": 0, + "link_to": "Employee Group", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Grade", + "link_count": 0, + "link_to": "Employee Grade", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "link_count": 7, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "Attendance", + "hidden": 0, + "is_query_report": 1, + "label": "Monthly Attendance Sheet", + "link_count": 0, + "link_to": "Monthly Attendance Sheet", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Staffing Plan", + "hidden": 0, + "is_query_report": 1, + "label": "Recruitment Analytics", + "link_count": 0, + "link_to": "Recruitment Analytics", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Employee", + "hidden": 0, + "is_query_report": 1, + "label": "Employee Analytics", + "link_count": 0, + "link_to": "Employee Analytics", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Employee", + "hidden": 0, + "is_query_report": 1, + "label": "Employee Leave Balance", + "link_count": 0, + "link_to": "Employee Leave Balance", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Employee", + "hidden": 0, + "is_query_report": 1, + "label": "Employee Leave Balance Summary", + "link_count": 0, + "link_to": "Employee Leave Balance Summary", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Employee Advance", + "hidden": 0, + "is_query_report": 1, + "label": "Employee Advance Summary", + "link_count": 0, + "link_to": "Employee Advance Summary", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Exits", + "link_count": 0, + "link_to": "Employee Exits", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Other Reports", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "Employee", + "hidden": 0, + "is_query_report": 1, + "label": "Employee Information", + "link_count": 0, + "link_to": "Employee Information", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Employee", + "hidden": 0, + "is_query_report": 1, + "label": "Employee Birthday", + "link_count": 0, + "link_to": "Employee Birthday", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Employee", + "hidden": 0, + "is_query_report": 1, + "label": "Employees Working on a Holiday", + "link_count": 0, + "link_to": "Employees working on a holiday", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Daily Work Summary", + "hidden": 0, + "is_query_report": 1, + "label": "Daily Work Summary Replies", + "link_count": 0, + "link_to": "Daily Work Summary Replies", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 12:32:12.608987", + "modified_by": "Administrator", + "module": "HR", + "name": "HR", + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 8.0, + "shortcuts": [ + { + "format": "{} Open", + "label": "HR Dashboard", + "link_to": "Human Resource", + "stats_filter": "{\n \"status\": \"Open\"\n}", + "type": "Dashboard" + }, + { + "color": "Green", + "format": "{} Active", + "label": "Employee", + "link_to": "Employee", + "stats_filter": "{\"status\":\"Active\"}", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Expense Claims Dashboard", + "link_to": "Expense Claims", + "type": "Dashboard" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Recruitment Dashboard", + "link_to": "Recruitment", + "type": "Dashboard" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Employee Lifecycle Dashboard", + "link_to": "Employee Lifecycle", + "type": "Dashboard" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Attendance Dashboard", + "link_to": "Attendance", + "type": "Dashboard" + } + ], + "title": "HR" +} \ No newline at end of file diff --git a/hrms/hr/workspace/leaves/leaves.json b/hrms/hr/workspace/leaves/leaves.json new file mode 100644 index 0000000..8386eb5 --- /dev/null +++ b/hrms/hr/workspace/leaves/leaves.json @@ -0,0 +1,205 @@ +{ + "charts": [], + "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leave Application\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leave Allocation\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Setup\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Allocation\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Application\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "creation": "2022-08-20 16:06:26.672497", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "non-profit", + "idx": 0, + "label": "Leaves", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Setup", + "link_count": 5, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Holiday List", + "link_count": 0, + "link_to": "Holiday List", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Type", + "link_count": 0, + "link_to": "Leave Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Period", + "link_count": 0, + "link_to": "Leave Period", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Policy", + "link_count": 0, + "link_to": "Leave Policy", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Block List", + "link_count": 0, + "link_to": "Leave Block List", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Allocation", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Allocation", + "link_count": 0, + "link_to": "Leave Allocation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Policy Assignment", + "link_count": 0, + "link_to": "Leave Policy Assignment", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Encashment", + "link_count": 0, + "link_to": "Leave Encashment", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Application", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Application", + "link_count": 0, + "link_to": "Leave Application", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Compensatory Leave Request", + "link_count": 0, + "link_to": "Compensatory Leave Request", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Leave Balance", + "link_count": 0, + "link_to": "Employee Leave Balance", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Leave Balance Summary", + "link_count": 0, + "link_to": "Employee Leave Balance Summary", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employees working on a holiday", + "link_count": 0, + "link_to": "Employees working on a holiday", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 11:36:11.119565", + "modified_by": "Administrator", + "module": "HR", + "name": "Leaves", + "owner": "Administrator", + "parent_page": "HR", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 12.0, + "shortcuts": [ + { + "color": "Orange", + "doc_view": "List", + "format": "{} Open", + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":[\"=\",\"Open\"]}", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Leave Allocation", + "link_to": "Leave Allocation", + "type": "DocType" + } + ], + "title": "Leaves" +} \ No newline at end of file diff --git a/hrms/hr/workspace/performance/performance.json b/hrms/hr/workspace/performance/performance.json new file mode 100644 index 0000000..155c518 --- /dev/null +++ b/hrms/hr/workspace/performance/performance.json @@ -0,0 +1,120 @@ +{ + "charts": [], + "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Appraisal\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Transactions\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Appraisal\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Energy Points\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Promotion\",\"col\":4}}]", + "creation": "2022-08-20 16:17:20.159886", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "star", + "idx": 0, + "label": "Performance", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Energy Points", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Energy Point Rule", + "link_count": 0, + "link_to": "Energy Point Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Energy Point Settings", + "link_count": 0, + "link_to": "Energy Point Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Energy Point Log", + "link_count": 0, + "link_to": "Energy Point Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Promotion", + "link_count": 1, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Promotion", + "link_count": 0, + "link_to": "Employee Promotion", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appraisal", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appraisal", + "link_count": 0, + "link_to": "Appraisal", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appraisal Template", + "link_count": 0, + "link_to": "Appraisal Template", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 11:49:27.869740", + "modified_by": "Administrator", + "module": "HR", + "name": "Performance", + "owner": "Administrator", + "parent_page": "HR", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 13.0, + "shortcuts": [ + { + "color": "Orange", + "doc_view": "List", + "format": "{} Pending", + "label": "Appraisal", + "link_to": "Appraisal", + "stats_filter": "{\"status\":[\"=\",\"Draft\"]}", + "type": "DocType" + } + ], + "title": "Performance" +} \ No newline at end of file diff --git a/hrms/hr/workspace/recruitment/recruitment.json b/hrms/hr/workspace/recruitment/recruitment.json new file mode 100644 index 0000000..f7dab16 --- /dev/null +++ b/hrms/hr/workspace/recruitment/recruitment.json @@ -0,0 +1,227 @@ +{ + "charts": [ + { + "chart_name": "Department Wise Openings", + "label": "Department Wise Openings" + } + ], + "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Department Wise Openings\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Opening\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Applicant\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Offer\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"quick_list\",\"data\":{\"quick_list_name\":\"Interviews (This Week)\",\"col\":6}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Jobs\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Interviews\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Appointment\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "creation": "2022-08-20 13:28:59.962164", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "users", + "idx": 0, + "label": "Recruitment", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Interviews", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Interview Type", + "link_count": 0, + "link_to": "Interview Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Interview Round", + "link_count": 0, + "link_to": "Interview Round", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Interview Type, Interview Round", + "hidden": 0, + "is_query_report": 0, + "label": "Interview", + "link_count": 0, + "link_to": "Interview", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Interview", + "hidden": 0, + "is_query_report": 0, + "label": "Interview Feedback", + "link_count": 0, + "link_to": "Interview Feedback", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appointment", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appointment Letter Template", + "link_count": 0, + "link_to": "Appointment Letter Template", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Job Applicant", + "hidden": 0, + "is_query_report": 0, + "label": "Appointment Letter", + "link_count": 0, + "link_to": "Appointment Letter", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 1, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Recruitment Analytics", + "link_count": 0, + "link_to": "Recruitment Analytics", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Jobs", + "link_count": 5, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Staffing Plan", + "link_count": 0, + "link_to": "Staffing Plan", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Opening", + "link_count": 0, + "link_to": "Job Opening", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Applicant", + "link_count": 0, + "link_to": "Job Applicant", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Offer", + "link_count": 0, + "link_to": "Job Offer", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Referral", + "link_count": 0, + "link_to": "Employee Referral", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 12:00:50.412412", + "modified_by": "Administrator", + "module": "HR", + "name": "Recruitment", + "owner": "Administrator", + "parent_page": "HR", + "public": 1, + "quick_lists": [ + { + "document_type": "Interview", + "label": "Interviews (This Week)", + "quick_list_filter": "{\"scheduled_on\":[\"Timespan\",\"this week\"],\"docstatus\":[\"!=\",\"2\"]}" + } + ], + "roles": [], + "sequence_id": 9.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Dashboard", + "link_to": "Recruitment", + "type": "Dashboard" + }, + { + "color": "Red", + "doc_view": "List", + "format": "{} Open", + "label": "Job Applicant", + "link_to": "Job Applicant", + "stats_filter": "{\"status\":[\"=\",\"Open\"]}", + "type": "DocType" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Open", + "label": "Job Opening", + "link_to": "Job Opening", + "stats_filter": "{\"status\":[\"=\",\"Open\"]}", + "type": "DocType" + }, + { + "color": "Green", + "doc_view": "List", + "format": "{} Accepted", + "label": "Job Offer", + "link_to": "Job Offer", + "stats_filter": "{\"status\":[\"=\",\"Accepted\"]}", + "type": "DocType" + } + ], + "title": "Recruitment" +} \ No newline at end of file diff --git a/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json b/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json new file mode 100644 index 0000000..83ac025 --- /dev/null +++ b/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json @@ -0,0 +1,235 @@ +{ + "charts": [ + { + "chart_name": "Attendance Count", + "label": "Attendance Count" + } + ], + "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Attendance Count\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Checkin\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Attendance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Shift Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Shifts\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Attendance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "creation": "2022-08-20 15:50:06.598086", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 1, + "icon": "milestone", + "idx": 0, + "label": "Shift & Attendance", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Shifts", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Type", + "link_count": 0, + "link_to": "Shift Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Assignment", + "link_count": 0, + "link_to": "Shift Assignment", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Request", + "link_count": 0, + "link_to": "Shift Request", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance", + "link_count": 5, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance", + "link_count": 0, + "link_to": "Attendance", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance Request", + "link_count": 0, + "link_to": "Attendance Request", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Checkin", + "link_count": 0, + "link_to": "Employee Checkin", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Attendance Tool", + "link_count": 0, + "link_to": "Employee Attendance Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Upload Attendance", + "link_count": 0, + "link_to": "Upload Attendance", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Time", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Timesheet", + "link_count": 0, + "link_to": "Timesheet", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Activity Type", + "link_count": 0, + "link_to": "Activity Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Monthly Attendance Sheet", + "link_count": 0, + "link_to": "Monthly Attendance Sheet", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Hours Utilization Based On Timesheet", + "link_count": 0, + "link_to": "Employee Hours Utilization Based On Timesheet", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Project Profitability", + "link_count": 0, + "link_to": "Project Profitability", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employees working on a holiday", + "link_count": 0, + "link_to": "Employees working on a holiday", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 11:35:56.729759", + "modified_by": "Administrator", + "module": "HR", + "name": "Shift & Attendance", + "owner": "Administrator", + "parent_page": "HR", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 7.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Attendance", + "link_to": "Attendance", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Dashboard", + "link_to": "Attendance", + "type": "Dashboard" + }, + { + "color": "Green", + "doc_view": "List", + "format": "{} ", + "label": "Employee Checkin", + "link_to": "Employee Checkin", + "type": "DocType" + }, + { + "color": "Orange", + "doc_view": "List", + "format": "{} Pending", + "label": "Shift Request", + "link_to": "Shift Request", + "stats_filter": "{\"status\":[\"=\",\"Draft\"]}", + "type": "DocType" + } + ], + "title": "Shift & Attendance" +} \ No newline at end of file diff --git a/hrms/hrms.png b/hrms/hrms.png new file mode 100644 index 0000000000000000000000000000000000000000..5564fe66a33ae1de9738c7cf8c7e0ea0cda21e22 GIT binary patch literal 1180791 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�ny~;|a@GZUFZ$HGov1xyM7tA)LYV9%?c5#1y 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%0AM3%@(K>2rys1@k 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

4o3h@wtR(21Jg@pI@Bfu%+nP8cL=`AVqa| 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

5J2@X?M2qbpNFW6=1Dg0ZZ4gs;q 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 z%0A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 z�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�o%C#`7i?Jy zWg5ksyMKLP$Gm{aiT|y_LA*K*GgjqiS$3=VgLQFOs@+J$VRc%>j2Nz0k9z?_+ zow4z*YT-oE-8%AV_Tz-3DEKl0Th}0sS`gkB+U@}ilkS&T=^v0fp^}=}m&Gov+sZ!e zcOMQwFnZ1CrsMuyxG?UYQjeLzotdnt4*USY4NZ&G&rwh>#;CD)k*)i2w@2q3`X{bP zT6i0&44I|=dfECofIbF#jATIJYkxJhc=t`7c2S_LO^X5NZ+EEle_=CcWu-=qHdULZ zMa5j_Fne#bbvZ9W{D~KEP>EgC6O|-g_FR{B_X#PYIT2f#vT;zi2vDFEv`WcHgQgcb z5zI)k1@S>;LE`{KFVrNX7wo8&aM@kV1!?b2S%IWI-D<0VH)`mi5deBCL+b~y>SJ_fSY5;FoqdcnZ@ zb?ML5Y`4Up8BNMFa8v5aXSUoPVe61=p9vgKZ3dtUPYV5t)h1*Pb6C}T6{Blv{bk-t0=~OEZjfNu9cnyFoJ;MG4T3||gJ6+Hdjll#A56Xbt zYkxT&*>A4N@g^~s{Z6|A@|a;G-O#5}XH8I)g*Ph*?9)geE=9GB?3#{%zWOx`1tVAC zeE@n*^V^oTMZX@m-S+wVpFyD{2nv;J#YS9-i+bufvFcWx$_PcQr*F7IYqeNyyMmrV z!!1_HHWGD@*l#QX!ctl-e7@Gbj4Belj4v_9Z$IB)6w8h5Xb2cSs^RPD0q?|o=^GVvi>7u;p@SnnX|F&&%JhJK^Xuy{bVJ8|j%}-c`~x6Ja@&01 zXP3l?dL>eR34i_mLUd`l~_FlCh)Hm;3CTxy(Sm z&9h>)J*4k%onvK_n04CICHn@nU87gM61B*0=Ohu&w>n<%xH*w~Bl-l_sR9^ecPGp)fKjGUjg3jP28CuRd z&rlk(M~%|lrHl7NhM2_QP*)7J7)RdB@C1U6>I{wUy3DV%C9-`TI$F4~YP~C4rs|Lz zqA*pj>P>9Tatm)l!EuRoO0`TULd$)&M#2rVF)|u@#E$bmdD%GvV;3+YdN-R^I0g=I z*P2eIL~2V6FDlg1_?5lPf#&J1g9C!dKt2dYq=b`9WN>}(BOuc!!F0m}-4c3=@MH*A z6G!ea-)uu~1*}K7O5%L%9QyTNFL~|r&;~=U$mo3!(b}Y^79zE;TOV@uv7JtP8w<{j zIbwD#(DI7ztF8UgT{O7?_#_%PY{CT|sol4*3#RgQ%=DBmpIJrCJh&Tt36!7O zKM(=#-(i%7T59S*>3l0=c2&K0U17PD6ugArp|sX#hDHl-8>qtX+{TWE7cKATVUiMo z({&+vB7S8RM!*JsiQR5HZZs}qxv_i0U}AD;I@lG4Xvc%Mh6Bk+>#;~>xSc)8Sc&&# z$AtY<%5oa?b@}tOM?m*e?>}CrJ?%~nXA(3*Q?wP;K2C0HtG88PJ>KS^AdntQ2Fl_x zuIDnnD$iSDo)ocQk@y|z!!C06^~Ae}5jnckbiFLtEb!X$edTQ4-9rli;)c;CfciL= z^US2XZHSMYah~S(5kGnT!RIDC-H35R?_lf|{-$xi{p%^amJVe4`f>RHR4dcKhKsk# zc570Z_Ws>{B_}#VI6s};@g&%cxoVzvM+=z6#cm}!*OW#Mz;o}&?*8oqjVDW6K>f|Vh$@D(r;9`70#{6hYHc&(&JERT^B^+me2=NyLKyTj<)EAhQ^6_xEw zDTbNJVn~))Z<#`5N+cAknP&^rr?#t=uGLEIR&@}PJi61XPZ=RK$gY-q#G)iNAn<7Q z6$V=n$=#_{Y)0RNe?G52I|Ju*-4S6q_Gj)&a|=Dk*-*N=Zaeo;Yw6J;o_@`3%J3X! zLK-uDJPh&lUVB_V-3|78@xxE?rx;>{`DA6*p2qd?fkn&u4v&Fs=-g)6@KA3<uHEt{^few!P{;18SdXxZG-MbT-? zz|K^4s_|^sQCVe>0I%!$<>+HW>Y}uuXJ1qC@kN4<{w^8>c;cK zy)o*ZXrb&TQq8Xy3L{q-Nb(MO1IPhiYDK8c=_W z&V6%vF2^^UZMq`OD4c~ymOr%HULmJlzFrfmfLS){qDqHU=sj%f*=G&&A z&<_3^#Uw0);|=(H(gH1 zn5K>?DeXA_?m)lwbdmFw>#@Ny4$gh9@i!s;X_a+1+uu-4T8aEm z;IGzBIW7^1k2ltKt#(v1QvR9@8Wn&2icUG8xxk){{!FVIX3lCXr^WtY?%QExjWK?h zmj#JxWjEyZ{isEqbjAIlH8Vg+&^wSbkp<|9J9~%))5-&gaL4NNymm5GjJ9ZoYSo1N zjEJf2{HPz1tdCDuGP2xhFKV6uoD$W_x9y&IGAj&nr!v*Xm(y6IEPrQ!hp{ccU)`y1 zpDJ*63Of$Ad@cXvb3XK5l$a51KqFL z_B=`|Nxv|=xYRP`mq+HHaX-H8fbJ0rR|4_)`_%OZlntI_qY45CZ7=?aCwz`9T2fWv zh!vd8e(rupB?V*@UgM!^`_Wmli~q+MqpyZBFZ3g`J5y0#dXIEzmA!HIk0b+629!=+ zV;pquZ58~hcKKm}sdJf5!#mqqbxb(&VJ~!DVZN z2r@x4oP)o0e5B0}?|HXK0HjVX2d6&AW+IHgg!HzqU8bK0M4bYc_CuZc;djsK(AR~u zK>Tm<;(&w^nz+!m3}#MLcc5RU1t|9 zBc+a6nRSwxtxpRdVYnKAET0F_;bdY*f+BL6#1_tzz+?^Z1_V^b&9>GiPGPwB&xKrt z^pY?4I|3w^2mp|LIwKf;1N2TSiHN)da1d)RcIUc92@)rNUbIFkIDcW)8J zDK^zF1%E%SWy?=ajM6L`YF3ZOJ)x4k=yOWTFR6MClO$(vZMgb!p{iu` zJ^I`SS?7+z)-HY3x_$+uGLm9NH0;%3^C}3fx5V)JbV2Ltfeya!%iTm-RnjH-2ymD_ z^*>naV1E|B3u304gZiQI;FM530}+?>6B5~bTHsZ;?+=R z_CwASnZ&45i`I|tXDMoZX0&}l&+Xv*C5XurrwyZG(OLHphA(?HUI+W#X>NoR2#Tl| z%{NsgE7am{Rv{Sp=4-p3viYScg_ji?m?3F}>^{&wTV%q(p3bI2{Gezp`anoYsdRjkGrf9m(!3WDdFz{)ThVG_P$3?Pc=jf%=UTM zdXr!xvhG?F3*1mlf0YhcZi?FIUNbIg_H~yonz(Xzi28jnahu&E`zOFap=sBwBr5Z7 zbF<2Afr8E}h4Ef6MPz6x8J5|n?|!l>^Y#9@8*N0;y{oH)cy^in=c#5;m$`6+;1#{d zYHgNj|ED-gsDFEkyBnEGq>GpT^DyiF5;2j_J*tAO@Ah!emrzB96fN{_wEto%J#SvYBO0d0(8tF(@CV?B?KUDr55!(Zig3I z!OH5-7!+_dj|sM0(qkoQvs&)t28J`6TgmZ3`};8nR8mp#5O_wq%SjwlOgIc2!@=*5 z8uy>ptq*x_tnH9#7x-|GRmVAVNN40UAe^Cp0%C^C` z!FNHXgVo!gGwsX1{$r6CQi#~CGQ-v!f9za;=2aBLc(^3@Ff zOr|2i=rx!FmsY@q{Jr@pmy2k`#*RO}&GXM8l^nTTF+RWFDN=QNHJT;iEQar=*SEX8 zpabpQByaN2SDlPx!6;C$)zN2R09SFHqFt9Fagx3}iI0OLwVOh%De7F$c^gnY&}z+k zlIC-zebbLc{oNibB)KkoLSD~(#SI!)X0asHZA-XQ-3`anO&NlezU$voA+|+Kk`;RI z&2DdKqugo3+m|EL;eU+LYOWn+?6or{8_@Acwmabu@qj8c*kZ`WmW@CsB?fm8x_#(c z+JDD&tJL2{Zk)o`71sE*hb&Qcf3s~S*j9hYFcHCxk;;kKa|L&$MBY#_e<9hT@6?95d2CXAHi4b z!;5l5l(B6xR3TKB5JW*^R8hRNi z)E|V+oKUUQM#n;biRi&PC3MI=ixmyr(IoCz9%4dQZOyH(pYZE@mySu)2F1YRk)xVr zcO~p6j|f5ogL1>`vOf*gtuWQmbq}}!Fi=g#lryE%vu-%;8<-ecJ{D|JppUM@Tpxqq z?WbpdQ$K@D!Gb4Fj*%h$RR@heT`CW$cx_@U_zK%M+7|lxB1wSmuT)DZPYhAwA=r<-;qw?kEh)iC@Y+WYM zHX)cA_JQ&%VCaov%-ftJc9Uss8KgzGy!&J^5RB`goy>WejvmVMLEr9EO?t{NO1N~j zk;YlakCEI!5;kGo9~PbVZC?;w0mqmtU13KwdCGQzI6(72SCBruZm;x7ybFz#iYJFv zaX$#(IHy!?_DJt(GLHMuAynoJ|3#C!dSV=^#0ITffb^6AD(2PQ8-#&LQqwdSbO}c^ z(V;$(>9yEvB&a12_48gnw*-@iC5q^#U$98MMa)HN2O@KouD__+4Sk}PGyrV(gLOenI6nB!tcp73Vm`?cw!RsPY zF5!Szqvp?|n#`0jukFN~kHrIonLaCz)}axAI@xDt%MbbQ=(H`9z|bJfL!O2<%$*QP zNs2gC>=J^NBVSmAQvh=(g$8Pj$_Va2yf+KQqRrl^vwt6bz6IiZns8c8bPj92z`Tb& z8zsX*WR3>wgKTmM5vK}%d|rDTACOf6Ao7#%QBLY_qB>CH_C!~`p$34PX}OHf#$5q% zTE~TQ78G!{76Sxvob_hU&s(Mi;je}FVvfd`l;+2Qo zWN%UIwB2P)6`Otg_V-5KBj^PQ^L0{ltk$c@Z4;^`1n~Ll-{Fo%f!)oy zgqEEiyC<7vWu^mb^0a=A4&YxRPqy#Elr`9)S5J5gLqOF(95;Ga>xEY%AnAp+rjdW4 z8+ohW?o^jsxZ(clZ0K5%cwxWf#ZBX;(F|lkr=^}2X?QzCcyiHa+_SfFo_(U$MbG(~Q3VU$$KEf#YVs!F}uCFg#R>t(KVS9*Wm-fJipa=WcE{wR}i1g$+OlFHHTk) zmMA8Gq;5UR)AS^|l@^mnf`;#QoYsHc$;9S;dD!e(-`G?{64Q!*a9K2yp&vi}jwQi? z%wUTyF=1L>FI}%}+!3SQrgKDm{iG1F%pHKl!K*v>3Mot#y1=^q3wSe|D3+1)20;2o z+`$|}PZ;;geFP{7a-`zVG-2o2LRg}UT}xY%yOwhKZXiK6Cvz|AfKroAxM#>KoZG6Zs z0i-aeX#lnywrfe265Ua`5oW8LVdgpOGj)u3q8P!`1yTL|yAAV1SE`je`{ixLUIcsy zT-tN6On!iHL?(++LjA$=mJAYpz;*t|)0W4|< zKGz3vM(w{DUS@y+a_s;j?C~bp=K^;!G8yE<9&n#5=B}3EV40r@7bo&_xY7|g64&Yc6?9lB2l?r@XZDP{rNOO?BTkEUc1NodtQ5_h){(+JQ zwU@s&N^YNG9^a@rLV}`%{gV(MkN$@u;-XUJ$ytB zJf2;u0G;TvZ$)VTmFB&@)l6&`&AW?CVsdiQ`ZNq&qCT4Q6`ywE-g?%`KMWHMyaX{F z;@y|L$2=G{kxsK#fGwD61vE0_uC7bfi_%-Q(8yUZB5)N-ia1~T>}K!|Bz__)e*+38TWC0F%%ZOaEMzfN-0RRgK9gQ!r`0)nrw^>Zw zjB_qaw=W~Ub9ET`H(?U#+8TloJb7sIYOc2P&!>;~DCy#_!Owr9;n&@+N!~5pb6Tb& z-)rl%MoK3Wb$en$=>G({sDs_rw0rk0cs^lzz?=3ZSUpE2@3Zw8A0Ohqt|$_A&;26S ze=OlYC|6b}E`Bj1+tX_)ZP!$RJ_vJ^#;n(P*tfN^?w*m1fXairxcF9XKY7^Jg`<65 zK$mboLHxjk+Q`8{;bJO=~#jOb=;v zM>7Y*qT`f=6~%RGKl0Y0I7QY6&Nr&(j}rO3e8Ack13KTUtHpPhyPhb;4|{ol+}AIK zPW#LDH)g5J*dGwD$KztDCIM!gREuI)8*1OKM5uexdC}oV#NT@S(#L%S+2t`;%NF*t zw~sx0e$Je3w1xA=5CLwqrVgd_?mMmevEVt-UUw4gEg@XTI;Lx{YDYKAVF|hXnx{Ly5_$J9Q>E*GP^N zDIiP!vI57qbZ!>hDPIKj174geku$0jCRxSx(TtAsni1}seyE>W_Rx;K5#-ELEqOSi z`)6Wg_gV8FWEMaP4kE45xvh{#!wVvTrU>Za<1fA*gd9pZ%kR&W(iHkHn%gezt@iK_ z3v8iHe4a6AT{@J$qPijSHwq63es=->x}$v)LN}+!{uaPa$!X9;7mR>$2Rl7qm<88d z)emT%a(?|D3(jxu!`md5qy%kA>I>-S_H>)Gy~%TswejBfv?{RFC>QdEYYS93glZsv z?62?Hdl-(!l&CJruJ4+!(Xiw-@hFGDWv2|qeX|YSe*%xKG5KI8=}HjFUxk!*j+qYN;D#*7FKorQG>}~rTc5lU}|l4 zZ~U|AO3RpmpYV6m#2|CC22@)%F<&^U1}Aj)Wln#J*1%Xx(f*sfbd+Uo2J2=~0Fs*f z2Q2joRqt2(tSzfyfcQYHEYCgWub@Ze(T1dc2b8BK5!!g1AmX@XJRQsEJ9_b=1j>qZ zB#+{v^~K`}mfS{yO}dC_{X3~~@1M9mR<5wp(b~S8?gQxQMOyUVA}_6-&PxXnZ-C=r z#GUZ(_um}{)6f@5`C9HVRbWxj zqJcW1(ykD*gd%|aT?5}7+Fp(!CZ1tKyy zGiV$~9agC?m}KEl!>S!%rn6g^Js$7UtNj5h3v&JFCd)QUST$W;(i`PFpKzlY0e|}j zvlmauAW>7YMmA)|pVlc_FUzHa{H17?{7oLE`l3jz$pvzTvXEMXL0DGnN;k$uzG?o{ zcjOwEg^+~pIy7u>Gj)N~O6gYU8C|Vo1+{V#nsr1Ktdf37-NX2SNfZV*AFbIbl;7{K z9MhD`e_wA`H?~QU$bph!PsHJjzyPyq3qZ= zJgGUAazjj#fGINMWj0tbALs`eCJ(_T^k7COyi^Cgp_1+k*!eG;wf&8rym z;8DZ{v-$HHV&r2&F)h!n%1P5q_WprBHjeVD)$Pm6ne8zH0kho!ivNJpvfF~Neu3IS z2YiP@u6e8QM04IY%Pywh<_Evyr~)O@!q*eL{`Tnq*`HZ;s4{|0-wnr=b$6E4iT!;R zwGTWX;@7DqU*7PsTNCAzxXR`gd{$ZXe|7!4 z(QFcciGLXj(x<1<2)bdT)+OCA{$TL71*)Pcly5_;{|{Nf|fP<0S^f(<1f(6f06=WU_wqW(llpu0SuJNvq{>451V7 zA!%8=-J3L+|6*dJIOxKZ%XYPP4JHxpb6)YqaBbdXwM;pjh0c|BQ~OdqWax;u3L@LQ zG{2pW3R{75!rW!LP=)UT(iEkh=MvtKp}UjldB+b<3Mez@zQ*KxOWywQuf6caBED#T@c_XKll{_Mu>3vm!u@kF z_~lMEUs?W-Uu`)do1$or3Z~zW=e=C|mz&>*Crf0V%9GT5p0>60NlZy=Nbm(!@$Y72 zMKjqKVp#l*SlzY%ybp}6n8-^fXSZp5_{5ol}?;6RD9-#*~74^+xRe+T2

nUF+-K$)FJ&jxRaIm2Cqr9VN9>tI~k$DR*A(pwTGf7-3Wa3 z2TZ9nz}PfTfOeCdXXI^_$ylq~~BSmty^y;p7n z4t*uwe`nyhkeDqC(T#Xhe~d2~@$vWr{htrTq;Z4fBEeV5;SPx!s(B?J-?8uUwaN)U zVs65L8sffs@UbU-^&Pj~th_8Ir&VtT6gBYqM=~*EXD+tofs#}jeag`c=*XuPR}lA(EMFLu&6hUI z82B0EI=pXI_cK8?vQIKui1|2ey8oLHS2kTj&Lf9wJ%VWH<%snr~uy?V^6MLSU|Bs(%h3fyy7v}%J0iRF1w5*&m ziv{1aZ#<>%vJkMj(=;az%LXczLpCqi}nF%PgR<1gJuJZI7;J1~jfI>e77&K!P!EY|)chch^G zx2VgLX^hBzl_Rt7$A_&;)}#P$z$a4Y;|^q$_6}DUkRS-`;=50DQu8+QRxG`xI}Yn- z1q@iKvUEr!4WtAaJ3ZIF7p7Mz8dq=*fS=3`U8r;mXmrraxxRIvYyn$;S=!D1MS-Ix zZKi6RKUCA8oiWKCDCYR@IOC=G7en4h<#d!^?R2IZ2czQ}RC z2FnK#kGO}r%0$4qtWlFsch=8Ac0~QB8YMcq`LYRC8mkIVAH3`a2lt^ZN~4lC-L#uF z(6FJZJTJ?9Aq98yo|^*JKW<7Be-pENl+6e8xF@kM(`Ez`7by7nY@6th#->{1n&(X*tKO2|~ zq&N;U_^J|+xRZZGm5+V9k&pDvL_LWi)_u?zwljCZP$P^=#5n8Ot`#1yo4}e#%foRN`AkD#wG>{vk zu;f|wIGpg}0YLxfKd=TFHDglP+>H$6789v2jxhYo&;HtZK}bBGI-#(rm=e6y&UMG; zZ5m&m;Zp;o#|N3OArtyrX4(&|DcDsE8U0TzzhHsq@5O6PWi~lB$oOffGN6@=MowCI))jb{yJvq^XNiYZ#{13>+j2zK-47{HVQm-X(Xrq9yg zMqM0oIb^4pR*DF^&o#f6;9ng~fV&HRaz;Wm(K;o|3q!{_06qTxzO7Nk80*b3$#FU| zMrnZAZIkVi^>W;tyKibwrO{n<|>Tu2LH4XSi^vkmtqXPZos?NPnLOVzyF+OK<^I| zj>5&S&^2kpN1ra(gFKc9YZ`esl;M(Aj|?p1S|YTpt3z1CW0+Dx+RtV2R0{?BG=C&k zopcJvY3pI{MN;g;v+hCDe?{)UE?3h_Hi>aNrhs$L$P*b{y*<;%oACjk>QYBshax)6 zPF;qXRQ>r5$KrV>hm+cH?-NJ+M7nfH6g=w!UnxWYoTUAZm~l7+ngG6CiNDBE(HO1XlExnnt*0GWsJCGmXa{=%a?HjveyTEA?tqhx$g9z_&k8|P!aKM8>yrC`tm0oH@Mpo5>{Ool>U$rvNYeej}=U67SP_R){et{97j z1dSIP>7jwB$3Y{|Ohe5bu)~Afq>hY0Nk)R(^iRRMN`hINJ(3%$A5#;AfFtW=@IKXR zK}zESK2(!t>7yXNnt8+qvGwpyBQwX;jQD%zb2l}dv2$ilO+zg|4}7C1(E2vW`3mEL zdi&Te4CxotdN<;~YV4(8{s)CW(6-hAe%;glQ`J;fRu55va*mD_e?`9inH3_X`Laz2 zi^98Vos}l{Zd*Z&^?r_^X2|iZJ7Q%bs}I*%T&d=QDGAd9??xN*v~^lP60ObAz``#C zFe<#wbY24OzFycMp^I%;H|JX2jTgAz@aN(~1UMz40o0Q4JiU0Y~P$hkd$u z`?+?ng~qpDy;mB<)cO=Yzq{OTjp-Ht^LFJ_DiR@8Ip9_@9M@F0vuLx814tKbm4E+AnuQd8j7r$MIg?&5dQ2S4H5#Bkcn6^n2 z! zF*r~r!_clppgw~pm*c$=)N}n@^K(hRas!4lZbfZ%a3Ow%caAv|m|vZp?}Ez$E+dcx zr(;c5Z2IVKNc*Csl4#3IBzSB26s1ze>Ha&lY2YzY6DP%>>f0SV32Njt>F~c?Ya2%W zuk2IHTt0VWBTd5h6|ytw|A-iPND?=)sJNeHCQMYF>o8a#ido1f2AEt$%Ph79Y5Cqg zLYzqDxKS$0r8W)gRT)nY67bs0>Y$=IYvJ5(wNW zAVX*CtR-U2e_lfxDpNvJew&Zgl0G`i!!BC>*`j1&E!+IHQ_q%oi&!&R^JoQ&0|Lc3 z|EC(9CI7buWu2$zRdR)4c?4frBln*%!U%64|7T|oqKjDI{V~aLd^q-n>5G4g%DU*s zo~(aJ83JsJko(LvMA@4H|M#f>y=MRHZ~u2;n~kA4(36a#EA3$rlQEt&gOIiFi&usZ zy9zGQa?~H49MA#0OsjlF2aPrb;#8@Y1SfxB5K{y6yMXjN=rulCRJqpkd$vf!j~6HD z4f-=emGM6dU(kdM(EX#FA>)2VgZI>SAmsx8%r*xTi_fm6@d@iIf!fL>HBIY#_(U*G zh{(;_J5@%r`WP;=Wb+tN^O$F&@w)>SF?5;=mYRu5KAD&nI1^`lJd5r{q7@bGS<)Rs zG&6a!*E8~~+>Sr$^f+o=g?tCl2r%z6EKcZA0bL{ZTk}rE`0;wl_h1|x?%0TUgt8sC z>UsgecMDp|e-taZ_cIWgOn%W%H8i~A+%L9(4|rNE5wc!1w*R6U<`5=kKuMK%(wQ?P z+jL`jJo;aS6QkwsVC#{g^5WzVZqF705P z!g{I*GauM#!#v7dBfQ-&S9u`UcHc;0jpqW!F06X5=V=C)!1u@Fp?{W$KEA9U0ZVn; zXFz$M1eLN_{+qUxDcC_q&@g}N@0pp>)sM1-1r*}ka%EncW7VGtR2+iP&a>=+&y4~~ zSx>2uG8FV2I27j~DZd)0f9xl=%06^GUTH5FYb#&=pGO zJPNRk~HU2mtMRbMkpRdAkRk0|M!1;>tb@C{AzShZa-XYE$kMBa?@y)7WXBqlKU`=lJl(=uBvpH`+mGK+=!(Cpbx zrW=8wtUl&XKd3i9pzm;=e^<$4pkKJX~cfKui@FJi`qa>K`opAgx|m-%+?Py`x~ zgFS9To%%#SL9+d|b0LmDO{s9z}E<2lmIxQm=UmiD!LBPeBg zph_6TpWxI8%J8ccE`aGkSZ66jz`syZ5Wi>Xf;`I%An3*(a9}?92{$~q3xVxDLKgK% zJ`{F3-5Pc()U`|lwXF<0)hzP&HwwMDIjw;efm=vvh!}C_4(omWiP&uNR)(mQ$v^De zr;IR5dA`>p8}o@s&1qmT_B#Qg)yXIAaoS#(=@*}!QJf!Kdzusv2n1M_TUh>wT~j6yI*?&04J7?Pw8XS)TQ z4&C|nN4lExOf7CI!SFe>2hsz2_y8?e@SjQJ2BoW~gbyl+%bT$2z=r(SH z?Y}cl0UqUh?UZT7OgaDO2+kYY70RpbuOG918mr&seBQpxQqQ90UlN;s*T4_HN{%Sx z&x=sM_q-K1H*P1^kf2%C=e}DIik6fS%1O-0Oeyi{q~-r|2qQH3eNQ@JK;K6xAn{Xcw53HhVs(<0}74!sz2AwUpBZu*5DY&YD z93vNt4|I^5`a{l%i!`yslJ>bxe9vckEF$wqx+hg2xhC| zD4k8coOF-8z_yw-><%~5*c^J00o2sT@{qt2svj$z#jUcn%fiPXKZvhn%^43PSfwV= z=81_;?Ei?A%Ix`d${6;>KDrD$W5h2z;xy={>n5v`XahY=4c!8RD-{aCr2QooR`seE zI?P622ktkHgAa*^ob(Tk9>xCx zLM5Z+Jd<(+r$9KR-2j++CEu_$Es|YRBTZ_|ja`>h?!qWmy~n^pp`6xNE%jxb+nY>cW^-qWB@sAbbG!rUJdDqd;v&| z$9TWXAq+&u0KTW*u42iNdE&3VvdM;W2t6s@TcLikt31qJI+jB_wO2yMfFi>`fHc1x z(~$+fb}HXhWIi7&H!4==Kc8Uh-Sb%VnII{_cv#jiHM`Q0M(m-^Ey~>1b)migaTfY4 z#Q#O==EFO6^bRAw5@ddg{sjLf?nEWi#k%kYuqsh_zSl^O;|PVwwel%fLcT&X0E)c5 zZ9{`;n#q~H2`C?d@tVIWyHoPEC1R^1qcc*B{Bg#-&n;PP_ecF7bI&QZ%Qc z%V8F5FJF^9Ou-=8Aml1c_p=X$)xD-ig7OSrdvA)1nlOF^AO+*~BA8AwDP5Q( z2MFTH0V|w1p2Ny#8=PhV`05f_9UE^pqowNHz$aI|Mg=RG=#x*#|DRU*hH@wj zO>YqXSU|=ex59Nljp96FUc81tM60`ApMg0wJj0lBgVRCBc7t<<<_XZUDlWZ<`2$4U z@h)Ye4)5wLjIUp&0KoLijvPc@hvzulPlmED)LbC#AtO`lCLKQ+LFY`cP#8AV?Aniq zoO-|8gz0&hUCoUHEzGE~>1vlE1&}-pT7OlD>CsN>sdBbj6XL|FwSd8!Ex{eEqJONhz>JwKp?Kd9{- zt=AionF^(=O)G%v$uaiDst=QQ0#;-UhCd%8za)h{d(1&Np6g~DKas|Q_MT}e-=O>v z1@ZL*=zEUqgzQRotD^$uKP3@Y@^QX8Zi@6EOCEsWQU07LgrRrO%S`3D3db z02KOCAZGI5Cq21LF3tSAkmh9dOOiI@fWQ(X%T72}wr@Cb=;yZz8!X|PEhs!n8z!r8 zDtbb6zvbUFE# z9EWb66_v(h5sFATc%PTlJ#Ezz&;i~GBO*Eb8m%ItZqLbfe}vw>aFY@X7`Yh!Xu4sL>8iL2pEjq zT)I2%ZD|h*wq6PLmHlQWLkB}>##4IX6Q{cYB@x@Qhq9l%kNDAKtPmsBKG7o9OLPx5 zQs}M@7IR(Bv^i)fDp5P@rEV!Br~LLd6oFDs=y!)9W2nF!)AC&mo;r#Pz!z9YQxLbl z1lxfL*e-hS$Kt$zNQ;aXkxNZQq$Ck{by_ex<3XP3<0V-+Y~6I08?epnE}1unzx&XJ z_3};6_Ys-2SbRrTPPCCQ^HBFIGt0&%N5Zm2p0lB2M!~;8Dc?n_N8)d0V6Ccu&VPL=vniA8D#bL^$oUC z#U>p#nRZ4#)HuYH1w7#oxpt0Y7=DMrf9A-357u;=B4h9yD29l&vSG#X2&Hf zI_6C^M^vr35xz7NKCL%(KE*oS;UIx7IteQ7RM1-M`a)J0 zi68Qa`I>6T5KuQtAtJgm!_iVytUTX#expa@$AnF_%ZHtj=dz`V-ckoVM@JM+Wp1{O||L^ zNT5U7Nx=_GTqRM@zS06Y&8p7#p>Jj-G zBqrK!e2wWdkt=M|=S_9ExixMMvonLZ2JuVElsRo@Z>hph82qjA|#foc}MaCXzR{!JK zp2&MC+EV1BY0dXIl`{(9Xsch#)!&TjO^ZcS@)dc#y*#X{EVs4e+!?FExC%r-iF4N4uPU>8?NZ_3jT3FR(BX1>QBIDgSSr8zvL_Ju_hZEu)vBR15ks^)_NuJqw04@; zyog*!OTEtDSe zb=aQ91X~;^DyvI+Y4J4tt5x@pG19S9D0BX}6<(-BBsaqJqXWAiYRY(G;rp|L_Ro+L z-CBHpHb12CLiN&KDY|v7d8x-hHB37pI?D$~%Asrc4n_(IOprsV$!at)o)97wx#Fc! z^^GYa4+R7T56+n^ax7pvOq?3%+F)JWcKp3>p%7DQvCgq=Z7Nw4lx8cat9m~A=J02~ z*jji-n47Fg%eHsnyx+aM9`))O{5KIUGivf4=w+FAi~sY3rjO6GbZnqOg2mN+6xL6> zgC8cQ{La+L77edp{*zYu*HnA0?B_uSYLt5v9Rvq{+xKmQbkc1S8f{WYS|6T$ab+i+ zhlhjurQCpP{y_+DlkA% z5$ytA_n9h-(jT{8i-~>Yv(ojO){zuZUBE!0RX%MdwHdQy%YKQ;KrE1G&B%j3itHJ9 z4fh#5#jc<0u&tu!U6)maKCBXy4JT)?BG8evWRf3vLiMSo1f08QWkX-k7rVBNN?h;# zh+90ebQiCNTyZgc?~(P$Urtmpv^U}#a9Fs5$^Xc8{I(IC?7 zTrL1U60C$myitK12j^soI!jR4oGLe%ek5>^gM@Nr5R};>Bgme>D@*V~`PZoBU??g{ z%0GQ_;oQ;Rcu<_fH?rP({tZ7#zC1ogt0lNzBFPxp59@Mkr8{;)d=d%gM19$Je_t_f z)5Nx<#XwkpAq_mGL9$BH^A$SBB!zb$JvvYUh^hVGLA=G1Xb7@~Do+wFS^)MB#`$PD zn%~1}Nnxe~btQaPbj+_omrm;ihH2Mbef;cL@kR`se?Xvzc_J^M`8Ot@oNzPoS?BJF zxghgRxUM&v{1el83vb0KkB^?omW`y>y4;z)izO?;+>*Mb#>c$i0AU9)5B})2M{>4r zW!E=vDl_Cs%mHVvt(G8=Szx~p;X{{|!i}`cMs|KRd`96gEE<+%QvSVl0_)lFraQx}$^&HCcluKKyaAZ5CTiURO-{dRh*dp0 z4|{P4AeISO#4*|>V#)}F%Eh=Bk~lCe>_93I3Y(PXg-FH!vJ6i|DL_HUBV~u_Fzg{PFu6UiVxbDIttO6 zypb1c&>y*XqB~zOv6^2-pmi7_@gs#^``*Cn1H;sW*Li>HOj~~1H~yGEi)+s}d}^Qi z1ovM>fIuq^b(+**X-ss83lMdT;D^EreM>5P&Y_sZS+jOHm6c zX|tdcQ~$x2U~6Io1h$Qp?w$sTuvaSapA{P}e6Oo3I)!6!q^Kyxo$@W-%6d69U_pM} z)QI|aL_Blz9>Eo{rMo2UCw}|n70c2+Izt-nANQPA6~sK;gin;r!{kEnv`moG^&ICA zEHqvBx9!e|w+zanNP^lBn<(nq6ty(~`Di*OqTI-My*|t=gq>k9K8CSBK89hC)R|E^ zzGSsDK$^7qh0hIl_dc4~GTF=H_^o-!uwYEh%qRKnVf}mA^BZ=N=dEUl&qIm6-DLmZ z6GyN9VN(vP(ZE>aMWN?BUm}N6c$E+8mMvo*IiT6Oyx>Wexx~7lvM0*DlkxJorwckw z6BD3g#_in`q^GnD+QIG~lhRZV$WVq#>uO1s9RXXG^aF;IWsWnSk4C@Ff?rd{ir!2z zT;@gDpGQgi_NcK9Gp8mNP-(wu}>IoP6%Lg5ZX1 z90S#GJZqVib?NB#PH0qudi{*E`_t{`!#b0?42;w>Lk0eEeBh>uI>+X^IlG+AAFg@p zsN}zk=Bmu@r-+>kJ(Zklo}OK|x@6MNplHI@FZd(wW7?pxa=uV1ZdcxUV)K8q`$D(| z+MK#7^Tjwp0m8MXc(I9TRj{I=^SU!?G;DDZWEXm6c7JmdhWqG1z53Cuo4Cf|fHQqJ`ETQ%d~(276+7*9ymZcHIV>>jHRhUX zS-)Di_A`D)Ccl&CzqgDf`e@z_wK+Lg<&$s%`Kgx+u4Jy-$2-8^lzZ_us{U|qZvU)% z@9R4yDxO7czOMqFm>vnUV(9+CB3ve;SG+GgaT_EEu@TDiXEk{89#{X<9jWpg&9K-W z9q0s_wk+ciXH!G5G%a0!TD$GZnF@Zi+D4LzC6=`K$KR+wuWqsCd5p|+ zPNMgBrh5?qsSAc$q#K5zAV&a;HaB>m05OBSh5q%=S@@n}LOO4-TjEJ|`y+D<445^~ zY)B$foQOXsq~E1KS4JDwtEV=v^ydbTpmkMWPVj4Cst|vvGWtRAj2tcHx9kh3SFt|` zLpQvl7X?xED1@tCUCJS+plxCn^4DwpM;u$rB{GKNgMq)P>f#|;+Q<$>z7s}9c1{SD zZM6FC+2_C_^6Es;8s8rsQTG`J;$h~g7_?R}VQ}f=X4~&VO}9($Z_xG@++$0FFe%pK z`{Uc~{%4eia94sax<(Oof_t~Yv++Np{!>JPv3@~h{MP#h1Ifx<3b+ltz|hz4qxZ(+ zh))iJfL5KIlaXpI%kCL>9~JGaSgHZDiU3m<$lu-C~;%n%F9n=L;25||cx@qv<8U-4(EQXUy zdVxud)X!-t4A;wG`C;qe2ng|9xt}imt;XBPbuakAD;Bk`K!c~A$H`xU!qXl7Xu=OC&rli*D{#Ln7n}r-r<5l$P_? z$GK3~hrdR{*y>W7xlCf6J3gzzuMb$EfS6{QtgfMZkx&25iV|ui0~RY^+APD-x=!(Y z*3SIsd_0KAe5??|Jn8w8IN3!3Gm2UCggvzP;1o(3r2whymqMD>)o{;Ce8)eJ*l*Bbs7| z(l8*{Pdv!$4k^u`W4RZiuHGjf%{FQu~GiUP1Nl1tq~PZ&kda}HO&ib<@<}#QXYMQ z2M5gyaYH}~yP)<}vnWLh>)(2PLD7>(L<`m}4Tm*%r30>i;j>?m-!>1*ToZy`cz)<< z5qvT9m9eGVa=qN}1s$1zkGb0h=1qR+Xf*aYnBNI73og0jiC*l+G@_2m-!0hG&ENAUx?%1Mce&vF7`! z$mcRhDhn&T*e~kLp``VG`*XB85Kg0IMRY`S&P4uxT9S3Lj`@d*-OZu(rP7Lk;S-W4 z^kAOX`>P1Y8vh>1iZ62K^E{$G*nJ@#8cM`(Wv*?j>5xjgs$GG}O)V?^Faok;nnprG zP-i11#`@d$F|_vRt_Jw!vkq+M9E?LqHWWanX{LO~M*kwSlp{2=jGPw(!MkXLpLYXF zTg&7e%X`Gt2nYkSrQv&U=oxe!!esU^6kt8AEvgKz^xA+XKMU17dW@f}RR{5UNRp!0 ziDc}N3?*ufIDl?~F<>5YBWjb-OGS$uvRI%03(p0OHyh(wm1wyh!@t}ZP}?a!ohiIX zU8gC36Abjl(gBgLNazw6h&bNj2+N8EqLIdvP3w7l2aZAqo_t&=em zf5r(~C4z@@RhJ9dgKSmKCFeVo%wl&4-o4q1mQy?F_3>FUnt_F9gfTe*bf0nR(U zLXg?YFx>rB?Sy5Q(`kD`_SC!e;BHe8|Mky)OUaK>y9ZgxiS*CUlm7xd#6udL)Y^2Y zI&;*huZ?~_<&fJ>?7>JD94;7yqezHP_>@UN&m0U5 za$J4m&-0l2>)JJR-cyvgsHu{v^vfdRVXfqNS3M6Nn?<2-7s)Rl{VT{+SBajq9$1ZP zmVMA}^ixgfhjlrJ)hJH}2MZZoCg9%!M8oI+IaD;N{S)0C(Q`$M7^LRHmtx$rr?-!X zWwxIEHShj6>I{OeD=Huz8(Z&}7Z*zmxEqqqNHk-O4vVXDWw=k+J-mk0v%a%QgxK?N z`+d*I!6!{C;k&@clBsV|Pm7dmF7NJS=_oHl1_pZf3r&+_{0g~O%&29;XBJphGDgD> zf0V>+8fe`*H-CY=%IS*!zfr3Tr@ z!QR=Kro9w%?%dOzN`UXLgP=PQwfQB%(jzzGDU4;WOJ zjQA1?s=}n`*MUo`!!X!)Dfw@Uz%9pR*|Uu?3_tva$nkdgh4*g8@Jn|klpsuIJSEyU zX=Jdm6vW-PH?xnQQCNpn@}$O6iZa`Us*C>TagfAXYIHo0@Xp%l>N523p6uvW!ZzPWeb!r- zONu?WRYgP?q(rHm-d?-9BfFEl5-t|j)SqcKrPs=U+(sgMb1ikKzy8OxWc(Z(k6Z!PDVH}|t;{7uD zd2Q>X^+=3sEA?~O!SdZ!j%r!oyj3Z)RY^_YUk&*2?Dghv*Oi!&8?1xVUMl*fig6P; z17P4fnhmr>St1QkmbFDE40w9(t*C^u$+QSNq3iuQYW}}OLns`<^qRkuZj`=f-v`dkqph-SqSfP3Kr8^%P#cx^J0D_${YZrr3cL zo77y+(mTw&jKGq+|EF1Pi7LXb5oz;qW~CqGcz&=#Qt<=pVbZp4kLF-eR^`4rYcQnA zrJc&1?*n+>F84!|)^(1xYcBDlh#9S8zxd;J)EuV0(z!B1`7Cc=r6|0y((GPFW>2Md zV0EI|X;78|90!{o?N{JDuHW8FQjlkhx$h6B82t2n!sOXg2daDT&+>-sN9a+0)DEd+ zQaKccJb?{t70`b_r@M8!G82`So`7u0^dz%wDBxTE&x=PD{5ioU@balB4h8Ddu)g{T zw~Iv(buFvLM%vZPfKEHZy|Fhmz!L$bWT)!Az}~(53oF`vF4u-x-RoefMmwR{=lT4i zfgkRw9vC=L6t9kwN7XvQ(MirGFYa&2#Z@Clb3R2S+4?ghHnynFKnUk?^II(jF9hC; z{E?WPMwd8oKkR{J$THFSk;%b(zwRJf`B}Jnu4ec@4}f#+EP}>f0-Mytlo|r*7>#6$ zbU!ESa~xbJqRV9}WH03WwDy>x+U?oL!1)OYZJc-eTn-#xr;9r_zh?*+-a{r;?MSu$ znv4e3AN!KDiuQx*ED8dPgIj9;A1gtlNGFE;q(Nn)SceFk;xC*AP0j;YY|f`$?gs@k z=NZB3{R@#+?TBNoqcv5rGlh1qAGF_amF1Ib66>}a)P!sbrl~aRe`R|Z93)eJl2Rl zc(gIz?35H|ZYn^<BKs_8(1Iy=qQV{P(TQ zfM_H$`E$X?@8!eZTMA>8pr>HK7~#G5;q>!h2$<*dOVB7*`@QZR4|CD#)GwP3qwo|? zgv)n!I*iR}0E8@jZPcg{@B-2K#oDYJx z?~5yS+zXQ8W`zF&rHB|Je@|fghPF0}Q;84jxl0#Zb}`oN19-$S<+ms8WbRq^814uJ_E^3L2$4 z;L`01o7T&@(ALjc!TC#K>|gv!e|)cVzCGTyYkPl1mMJY)wDmK7p;8sA$xT%~O($+0 zdI%>%Fxiu_1s?$hr>;g8fo(cApKUh!Lz2&8Z=jo;dxjGb@(1KE1PQ;A4++uTVYHeF z2;Tx+uQy~E7<3t)y&RMw5l@Rv(i>JkgY=_=*gtAWLWsCpxN>A{kaFb70A*PxQ}VrJ zjQmQ5q(;DX25PIE9M;-;-%nL=`F+NVsFgxt|BFkQ)B1+hS{@)2hEjL-9v2?v=z0fFXduKeS;sd9^{muH;;~QR|n)=5gG>MOd zl0yWi1VG&CJzd6iOaB5LlDJH3%;oChjWfOuwc_9pssaz}^rju$DU6x5q;r@keFfPM z>P5rKiHkDTV#C@};=g@NlhFy*RAnBJSydc-!=e0|t-COv*!=ZLt~qr=-qUy!O^x8n zr*3~L!w3I-4{&&&Q5bcKGoHwO{XpBg`HPk5DCiu6C#ZAsf{<_|O51%p9gDafvo;7VdPTHaqgaN2G(T49 zKlXTyDuG}};s-f*HaZyq>ks%P_0P%~2tAap^1^0UycDOr&8{G2b*W2*!&diqQ9XBV zS<{}cS*on#`wP#K?`_wMO1lmUETKZlZwtMC4wH#JnV2+;|N2N?CL;qCQ7;qfRf1-; zYkdF(gcU!Lb3d?qFD^B84}_fMsPdQ(d>#VXLHgI z(cxrDE+9x#Oo&${$-GKID{cm}Txz9$*ULWbhiNxUY^r;ApvrOBFYr0^Od&p`-t60m zyfP7pfkT>r?AJDOxIss&I|EgtZ#h>+l1Yd&98j(taEKtn-HXR$+oTc};))73P$vFj zK_Limv=&Gld<2ibqBhlCHl<2F3=UZ&l|O5xz^=_d1GY40`ZGdFlDScmMr(mIKg*)_zMI2A{2Ltq`Or=Qo*qi-ZzQ>)(8GJnB@n!k;?g9eTn65sJ!o)O>LPnbPc?oGaholvx7 zg6DI@JBCC^!g4&*^;qUMOK-Ok57XqZOBk7fV-mrVwN=AKa1pV(DRLA^D8?o!up+bU zWfgh`6N7{I5k21lt08@}Fc~aB^k~#^le>4#clg9(BVuORP5m)AT;X_l%J>D8{tcmj z?f&Hsgu2>4rnGGIo zlHJkJT`yn1n}+3HmW7Fl40kew)27%l?ijj7t>ydqsG?HM{_H3dptG?GrQe>3y^Hy# z(J;iiG&Y}lVfY+5ZxRNfFaeTz0KhtntzTy+37rIyf_`*fPKsn!-j9Sm@S2|D{RNKv zq5~VSoYG?9G&7J~{|vH%LDY-|cqP31m?cFvC0h#+!!?u^1eM91qxwwD9^$~sXAcnb>d$|@3t50cnviu!ITd$F%W$Bvt{|+VYlA}XXEAv19_yg z?Le-~|8N0>;cE?_%1Uk<(s<{{H4d$w+`{-Av%}X4j5~Eg0L2YJa+lR0%|6mc^2p9< z1T^o$7xhBvtrjHRK-VN?+#jJ$zSg~!`&{{Irh!ymK^1%?LB_5BphO-qYQ}-~{vb2i zWvm$%&@@D6K7PJb`KYo=t{Z8uf~wibw|j`L9qirp^g+Eaz=R=NMQ<c_*dNNamIe7a1VNQ=5cpM?;A+J2~|Oj9;GSzb+l zC%-{+2y=hP+K8N4sJ8x$bb@(Iri15iOU3bAq8AD_064TD+WXh>b5 zP9t&_)Z_;}4oYIi0gG9CVU)l6=9xT4Y)S3ns}P8mp||F3J4X|m=ua}vkAru`72(_7 zCyMx5r?z4yF3kHvFW89~L|p&A-rNO+m0FX8)dg0}n8aACekml2>dG|psi7RV4b2Zd zO}7Xcp|fCzC$WYOgUUaU7X=uLAMB8+}baXSuu(Hpup z6tZq@_vcQd2?=VQV^I1Pl&4j=*X*_pb6bxnM~QTZ9DV5}-1Y^2}vc zV(waNe#6IaX?En1@e8i~Fal8($KHN@x})}dmM!up<8I)^c*7bYkLJ|z~Z-q*>b znWdppT89L@`ma~udrau!tJT3ds~>>OaT-ghaN8|KO;UxqKi?d;z0E{c4KS(vOea=k zBFej^aT0VOyo5-VQs0b0@-%6X(iJd6g}aHLju|(PXdh7s0b_ODT@1luZa1yI&V{=_ zk5l91umnu)6aRj;Q7Q)#)5E4lx|XO8ASPY!Z`TJ4&UX z@yFPg;;;8lgU*LcIvY2j`nE7hi11lki)(){D@TNtb;^cB*ZVu_3)Y5z5QH&9YBtsj z-4`)dp&&1+$twyn(5yuPB#rgXTc1S)2qxj`iQn=X6ny=TPz6KIc1Xo-h@x49v)D=OZ_C49Ake3gjOF>o9Mv9^W~Q z-M?$+{?2$(6H52@rq`hH{>EnrvckHtKXM2hn{NO$`>lS0IP=MfV~V0O1~Nl8+f@oZ%I0GxHp`T<6Hn=E57b337-WF+py}33YRDIt6=Z-Vkn+#efBz zsO&L6ub;5klvR?OLctOt4UWgr$Fm@yeHtY9B=@JVfDVYaaPRi@_5KFz!@ZL^c+7Yo zhUe0$UFRmWKGui4BuP-DDs0lE#qtDE_34m?E5j)~>03tjFNeXej``>)awR8M6`rs; zTEm(>mYoeeSDvJEXP%>}>9(+X-4tqF3F}8O+*zHzKZEQ`OgbkU^s&ou5w{yvw;mXN zbr(w^2KHZZ(q2JlAkC=h%bos>2O~6@@9t!m%Ftnk)gqu`t2a=!L|h^KIwFPf=n0+H zDT{ImHN)s6;ZjnEfL6L-!v>CBdHN6~bx&oR3~|u~TI{G_;HbFe8L|t|w6?w&2wBe7x^=>Bpyj9G&M1es<%<)3PPYq!Yyfe***bc8S6~vKi)~ z?&sS?0pTUoFEX{;Wusrpo&Af~W^c+1LIL21LL#4>%c9G}$@P1x0F%*$gFM;unxHqI znl0}`o4z~-@7g2@-c+COz7MP_hMtdAi%}Qxoo<=a|hU=rL%PfdQYCUhBPOj{>NRhc%=%+%Qolnht zN=Ptt*VO8ul$dROCD71yinB87@EUGczT8+j;$b!T^C{<`RT+#!o_$KuW!6VT*L?N+ z*Ykt{H0JAsY?B?4d3{jU8tsd`+Y5|(@zz7?B2NE}AQUE`=JR14x;6^olnf$hpVeBW z_B`?RJ}nXHlKdEgL*uT|nkpstHPh) zVHQ#x7y?$J$YevKIa_ZeJTCW{MUKn;2&ni}ZjUbv-M{foudxenYU2!0+J#5$yo<22 z5O#xkZ>`C`h#AdQiZP_FhQTv+Pz;J`Ov<9C)z&=Drf9y~nySl)JwbZZAJiMHlRPD4 zPtKbk-`CMuMdgXeO0WxWz&OIjZDviKt#idL z>d~~JvX=h0NYSv-_sv)HDUKIUg9h?dD)=2%0Bb86^>SSMh29KVwUaH^ufRcnU&4`;I&%WL9tgSe0nTH$28Ipn%b?*!C z9$k<;RWbz5`As4dp8ADG`C?!jrHb@j>=_QkC_j#Qdp$4dalLPl2)S!O(#sCnjlbZH zaC&%9iVP-c~);5zQ@CQ<2<5n8lW z(Jbk&Jgaxw*APG`2w3)nvb9XAomD{*I@tu3K{un;7!5YELtyf*(*p;DG16w_RJWob zT^6NmoO#Rn#ybQO>T-0xv;QPl^#ZSq=ku(M()}P(;_r-h17}*|t(ok}u<4+$xE8zn zB#l3IqyETWw4)ENg4sj`tf*6p8SYq9X1-ZEU?46VV4+UZba09}R92z*{Js+Q7j!z0 zKD0Z7ByDjy?RT{E-O})g3qOOFyt*|cBHxf?khZmr0r4_72rQqYg0jXTy8?i6`nBs@ z?A}+TWneOh;w%H+LuzHF{nQJ{08h5jx-vWRK zA59T7bKXIfX*HC4*nRxnlMc=zZAz^1K?RL&d5t&sS}Vh)J?9m>C|G5 zQgEx`{s!ufCR#aHK~TCmDk-0BfkK$0>+8>$kP>7wz_Kx^6Y5~h;M%bIf z_qU|b%iZOcCa;j;SBS0wG1#oWlE`f~8!ErcA{b`ilVeL)zYnK0pByZEUuV0-)oQmn zP*j$1J|e8{(k@8>1rz?{4VFcS$}CetUW`5*;7Tw*nU1t{>?fJ&Q=Bniwpyz>YM^@+ z;&)!girVw9b8EiZro3?;Y(ANe5k;*@maR~GD#$?VErt?lgJ`~mhEj_tL3ICkC;dI^ zp5)x(30>>4D(jGZ5>5-5mE9s_KF>p}IlJFf^lH4gV_T8U^|~tf=(08C*aKDW<@CJ9 z3?1L`%Q~=Y)b(I%3Ps!wRh@cVI?kDduy3v+uVduY#i9l|V0r>NuGDk(S(m=75#V3c zj<@5WP2C{uZ=)oR>I&i@6m&kUEtJZ2QeE)%dJ9+9c@ZK-ll>f+$IS;;bM6)`LSYYl z6`bp_*+)=VpDYCGUZ7adTO!DWRQ0=xb5#iBe{Vz4bqdZn$cij$3;2D!D>rhvw~6s=i<6W6k!7% z6uW6}zR0{FCqb>6sG{KF*^RE)5X~&`9c~D}J+Nz*#x7|LrULI`vgH#>XQ@s~=4a8D zq^xZNih{X}N&9Y4u^{22OTdMG67tS!$cb#3p!<$6hQXUn+t4-3*|3pP;I+@|nPvMJ z#4}0^Wg%4ES5qN&_zGFM?!}j`mvJ|-??*p{>b6b~C*T~yB0hR`lgy4aWL$i*ZRkMs zgB?hmmsa^ma05(ZzJ{En$HpbdD|g{RH>x zw6Xcknoum4y9k>;>=LvpZLly#0UcBza#ixDIicOC3wOiVfRaNy#zweK)$&ZGav@Z@04^{Mj5mT z#A|>>&%XDcKZ}|PZbo%>^Y~=<(NtBY=}DBecaC6gWf(~8&p=Cz!TZNH`B@i+-u65` ztjVb-luwgq`WL$J$iyTEthVK2)uLQBH z^Phy5z!=}yr?axSdBYm(HL1G@$x_IHp z6Pm-;q6u*AMN|KwaMjkYJZQl-AePJ}?gLB|z(Yowj+-^pyw{&TzeN10oEyt-HOS+n-uv*&s4 z=f1D&cLiqJWg7E>?P1M0_d3iGT>Hk1x5u9>AgPokP?x-uTf!j*S`vW%!gP_nZX+SB z*>rd`J9u0QNwH(`S0tC&uvk^d-Uox8h|`=vhFZ_6{EG#aik*}<6&6H8(aE;7XiS*O$YBmo*m!pqbtFF``l zzgGxwvpC5hcdX_XPuYDL0s>CQuQta?Ku$+8bBz)4eWolK|8G^$UC+BY7t?c@r?XFR z*JWjSGho)pzl(ulWI>Q1j1a-*sY+AO`AM(M@bqlriyB{!d?D)r&yh%<7!0>iv2iGd zkCOQVd4Dv|Y%v(Oir6)Nd`^m~eg=x~yun$z#hCy_N0Y)5`sO>lou9K>%Ko58ykG2h zMi1rD@vb+^*9whu^HGAiw>F3qRQctb_^}$NrJ57^6?GLasj=l-;D;9D1Dy@PSBt#9 zUL#o^nYWBxJgdI)y4wVny8vKJ zqvcP`>7d6it=WN?&b(w(mmk{Lk;jcVg{9C0FEl=CB?H{4vu8mRGet6NBY=b)1~8J&B@X0bc$geC4seF$q5M zY?G=DJz3@n*gMqhx(}UmHfodM(2=>!myS7c{@(uMU#?OA6`r}khNQ|8w`6<$1=^P% zOtg_F^*Ej3!%K&{0)-8{eaC>Vg9Zcnk>~j*++)NT&|{JLJ4AH~Y{M&E@1tZyM|Vnp z3-VW?=m8m>o}XZAPSBp(sk9Yo?D{v;s@b?YJNDKOn@0{^egUNY?!8Pm%w^*){LNgx zZ>eYD;{!2&!l3UTi`uSjwgh4kxiT7Juz>S%#U|XIhK+N}&)CD;jW;ny!a>3lCc@|; z6PpU4@(`ylonZ~+GTK%L?20(Aps8kRULsU&W5^bAjEa$b{csJ1WaxI-!&^FdX_Dba z%a=dE&D6#e*M6L}G_P>P^u20)G~MOw4UT!(f1m2d*;e%VrX1kGYQooBZVD%#MS57J znHV|g25r4qPQb=hL+!zH6LPDqX`AmMQ|>fXKwXmO$n+7EfGl1Kfpc;}Z~7H=0XFY7 zT%9D^kxg=RLgqyyvq3NCm&V#iopSGN-#Wf@;;z80HatM8_!NGfbt{U6DG)5^UNf+a zE#J7m&}k})KPBVEU1}hHs7ZBc6eo4)&p8YYloKCf^iy+PTL>q{Ms*5}j%c16ys@-H zlS|r!TPda5xmMaK>#c2g$-JO@I@#p-n+*+ZrIBPvLUnw^!pZX#Iu}*p&mvKJe;afB zHvl1HZ7V&@5fSn}`?7Tb{Dh$OvDmUEfJL?eEE7fnp45N9qaWMm0ZUeSW^R|pNGBzL znG`Vgy&Y7ET47J%!N&qI{e1;;U>-HCu=oN-6@gy={JL5##sW$U++N)`8g7hM7?qICQ?wtfKv*_ZC(+KAI zlgVR@5NYEOF7ufn@K2P?N!NNYj_2vtm!+MdGGAX%`7{3r)`{i;1l9?i0S=1nj~iam`*YEiZQzzO>29O8*}U0 zW3X!!%(Y1eUpqz7#!39SQla?tt0$x4ugGQHfcd*{tuJd=b8=3vT0XqacK+5;-U;ti zj#amZjTpu7QArRmDZ^a}jPFwvh%UV$8fwy9uWse5h$5r+@6Ym?>lBN0`rc0b-VU}_ zl}vPr3O~3|2s+CRj`uJg+q_3wz8XN<~T$p`r!KT}`c zNGCPC!laRBFagtR2u;vyJUEi%?4V6i=i>!UNn9Kh*0XI${dk<$CFM!tEX>${{j(A5 z*5Rj7Wz1^BF4NUmoS1_jMx$2Rp#hpxM;`?OiZ>vo zjTrfKz7@J`IwJiyz-i4#!w}~Df}k;_`KU>SW<}qbHs@XXot;_O)#;&0Y0mr1P1ddl zJB|tuN0!$q&Jbbk+=DKyFK}mL>}|odXjonS6>e=C&6_ws_ug&uVs`M^6?hG}dKidC zh!~k_LCd|DZqWAY>&UYEAJjCXmo)?sx7V3C>~c}uyrJxzU`99Nlc0BtVBgXe(}Tt( zzeo(~9C~e3zUeFvczxExBmw5s+1!mz$fT^ z`(?}6B{s^|npF*t$dYlaG7BZ6^FL^`WTp5wE{M2PrLz^*Pb=IDDE;0C<)i9I)O74^+9E2^&BKbm-V^5TmL&(D0_ z+T=>kqkfZ@j*X#ARnEN;NA_(Q7^vanZYcFrO!xD2ykG>^_zIFs}Dn`WbH8Co0j<=NP45i4rl1D#pJDlQ)4wH2iM|e(aDrCi)k$ zLdDU-JYsP>rUDeQ|7oH4c#qCnPcV~*Nk4q@*Urv0pQ)3cn*y@;bC!|TS<+LaFH05* zpZfU2=^eH44Y3d4L&k?XYPftzV)Vc1(dT)maJ*oo>g|CW&C~O7B@&18FLDzL4W;KlgMhD)ycj-o1A@Tz}9$L8FzdNAk;!?a#wQF5(% zc*&+TcY`IM=xTBlnzHGCX1bva<_aCHvEt-@X+Haii#+<^T6=PL@iSnaF|~_`bNx^q z5{V-sA#by|IgDCQSw?RC9n*^J`)b*#S2}HpyF4}xeFO(G6D}#1Z*lX&y~VwfdfRO$HZ$8k(rYSE5^(xO6SHHL_0+u(?_ojL+fO()ZKhQXFB^9B z(vL#pf$ytUy@!oyy>LI1ybe-;Qy6<9^cjCJpt;Goi@=a5)flzmw*dE4>%!y&mgbA;3nWni^ZJ|3J3)HF@R=MLI28)COS~TQ9bwgXy~KB9u3>|TuJ-L7$#yHtj*N(A zePw))PvQn;{4W)UYJ^Oq46)Ea&M$#zlmWg%b!8Q(Z=4Z z-f}J^1acs5r>k=HQp8$LafV5;&uWHU3-N{)fe1Bc^?7Ixl}&p6z@a z7w0iL3_Eo{uR)}^AH`*qSGOw8ys~?2b(=_nB_9R>_kV!YUwIx5{>nPzW_@hjzEf** zgZtd+0ak%RrQ+@n!8k0myvdI`Ou?L`(&7YdT7mDOo$d80Pd4Hta|MYRqjDu!rPdZC zx2xa#9zDPO&XJUF}W9Wq~MCrrk1UZotk{x^sL6{_Edy0D%yf1D=@;ce|)uI7S zi%j1XhUtO3Tk|F0cLBWFs62IQk$g1*5&1}gOIQsafhPuKV(Lo1;Qj_jLgcp} zVliFAjSG&oD8XPvy;)TD0bZe>ImZnh!nI1_gLDvwqMuX?Z-9vYvA7z-PzpjMOoh{G z*@5uDN3!jTo~$a=U`e&Y$zC5z$Y24eK*I#%-MkorBs#l-iDMjPX~w?{G1& z0GF)sg!P1Hm|}jn7VKs7H$ozwCfrZI^9Pc})dX#m1qvji5#PdB#+jNnze#P}a!U09 zLn+=8UH>5Pn|b-iY{M#n1P{c>=e&zBqK4#V=Pfh~cd*vt&g|Db;}U)=o5XRK54(KbMWnaEPH$JCr@{~Cmyd`ORE@zoUVC62u@ zH+C{dr88(~>JkAv<}aU;h|UC=Or6h+Xi##UA0K|rbt4WVk1t|%=sX^yJqL zicz%FKoW2q9APT0`amhn!i~0|c*|V@tRbQ`Ixj{)@%2MQ8k;T>S~%@cz1OKN_;?Go zF2E8nqUs#|OA+zYD@@=~19%{WTIcmR!}o?g1zLGhYJ97i1LWvMUA@~(X@&$Q@z4ai zss$i^x^I`6qSZHMCi1N|Lcf>%(*BN!Ml3LAxCfS7&;J?BH|Knk4fJXs|B~y8quc@) z`u(kX`{Ng>^S|+z>ZCHdwiHMYlhLUws1OqDLG5cryI{e%)ku~{(4`pHu(f+!ondV| ze@B|Z7uQrE{sE#81zQ=PJVDE3njX)(0ZfxeCq91p0{KKa2`1~2e7ssKPC<;`fl;u}2@AghdW`)P&tQ1lV6l}i$*P^1phvej37n*9^AhMw(UE<; ze%I81-@<8}+*NKjf@2$>m;Sz;g|D*yHPOas$=eZM+5ycL!lJ7Q4KNZ+vgU1?=~w z&6-zhso>DZCbP>V<8RRO$WBaAlZXv&L*o>Dru>!1R;|yv3d*5^a@L#`Eg$nXRj9ww z!;Pbz9z^Oj;_&)=eCH1D%-btU!D``pb9pB=0DVl-nt*Z^FV28}=faMDuDwr~>Er?u z7Ct-7kjP%c_k9AHVnaIrp>bZb$ z6N`-#A1dgYHngggQ2hMZ`HqE~j=EaRfLWznjVfGw(6L3u3IlskI+F0zS5Oa(%o!o# z{=M0s+8*XRun3MXN8}wio{tj^J)?T~aSMP1+^w!s{tJLtP}ShRoVc}h9#k6+?=X$c z8HW*5$h6}27@wZJomT>(o0FqnVLMDcVYBw{j;auU?vRO+oUh~ao$nx$$?mWkUx1nt ztgpOgYC@DQSK5>4?({lli_W>6!5A3twW2PwV_VxDR9g6dv)DeWJD(toT)pAPN!tsV z-YM!$LZB*RgRolIiyhq6As_bYZ|C2+t3l>>OL39sVsneJsX^)N5XaAo@+OiHj)rdN zrq4+f+{&m~B;RT8PGCSZY-E;ioAS90A>aaQC4%2&~3SnKB=5_u6 z5*+dC*`_a=iBR+4^HaX6BuAI;dNZbW&Jve9>^66osD%kUGoRYmG6i*wzdeIwdL0nj zHR2t+Acv^X9Ily1ThPdfL{vG(^EXjTbcmMfzEecR zVENlTcgF{dy6Ehc;Rho9h=h!%sH}UY`Z0Pha1!iyZ@n&KAFFv9((3(Oeur9;v9UAD zS#~@p1fSR3ko|4Gf1u!7y-=H8aY$HL`1tMmTEhe`1 zQ!uoi{eps*@3&fTJ(F+xd;_pr&BScTquE_sMn&1H*JTG_d8hSEVu0o?8mrCE=d;KD-n3W!Z8+9nFeCBkWaT`J{*H$H7%0y9JJZLLOI|aVD z{HESZD9wv%3_br=5T2KJelV@S-|TKhbd1B;LDMS5mYLQG$T*2r*;KONRO~0(x<`_lv=EYajO?eS2{EN5is?dkx`Q6I{oIBblHRQ*j_!-Z}YYm5`jG zEu$6Wk5TjJ9TroK)Xw$7X+uqc2MyWw_rTx}Zl82=iqGCj-Y(P~3SXG$Auuq-r1e5X zCbzSfgwfx`znOLUSF$c!}NoY%ZzI8pb3z)SPRkj46NSEqJDWS$gpc zR>c|^%pip(fL4UyhMAIPx4)ntcz@z~@Fjwcq%wVQJXtnCt6{{j;GA|Icm1@jC`GAb z*^UjruQgq2=M;4msBM5_n(lvZIfy0$`B7WTZs65cw$=IB{YVgfS7I#@mQ>3eOoKQJ z#A^@k}F48$4}p{7hd9OgxV*+7jB4La>o1Qw z(rx?!me{3MgL~7nT^QcUx8~w|S#+PERuQXkd{_PlDr7kwN^j+c{&cZ{K=k#{HC{A>zoH>9k-b^R!D9x&XsJcs$LDg`9J{%D47B)y1x zS%b}vkMoHLy>&O6hcWJbx3t(T#=u@?)dxcR6+R*0tYv}xEIIZKP+!%B+Y*n(N$yuY zrjWsW4$`imkS*=$#DZs;DzDB)cZw3m8x5S{@ywo4JJoeGvX#MB5>B;qLCwyI>zHwU zlDOA$^5{S#6E(5V=`T_Ty?;@C^GS4mkn|fcrmC5x>oO9Mu*Y}Kc3M_Y<*@wrb#~BNGQ$W0A$yzJtSpe-ne<-M%Drr`eT=l z(F?qle|uDxOx#Igt|)7VC~bV3evSyShSVpNmzhNfov4g}0UrHeNS0TQ^}8N02l{sN z`<-v*^{oA139`t*L{suPCW%*_H*|`G1FM1eiY@>9$6xpqe$1+2o+~aHSE}vLF5QFL3OZ1Ksz!8T^oPtDgF%XIYFQ9I3GbJznWl2UVbR3O~I5Jj!kM zN*>WXnY{)0l%3t!_RogTmgBTjz1bALV)wABVZek{6m z4tE&2R)5Fb_Ql*gL(rhRI#7bPU)uO)K1|M(FfaW%NxQ=+E4`jD3XxkCM4Loy>6po+ zzf%75^C+lkBbm>dUsmcl1GpGFntS&o9a%xL%cmm*-j@FAj;_c)xiC6C@DwF(ZE5k@ zI7baMoYr~_g@Okw%4r~G+fpE0J>@5Qt$O&x8MXA&uOIOTkb|wa_Et%0Nc8k5^p-W{ z1hwF_VOSbD>eeujGpc@P-_<$zHf8BH=5K8^q9fxN{`T#7!nW&I@mY32t59ddZ>NkV zB1h{H5v%6=BE1#8OvH<6pUWy$Puz9p8FE5_ zL!rTwQ6TB@w|PC;n578i05iJ@a5v#H84jM8&V5IP1^rRHQaYEY%xs<9M0&+GoWlHT z9S=zuJK<~cHK?6mCU_o!m>W*AMi3b02FsoDEs%bq-dL?`cj9i5oXRDyx_8mvG?jN; zL%4JPbA^s2Xu6ug)~4{qX_cX^7}=w9Wl9er3j#}xH(F>rY5eZrug@jcleIRlL}H%| zwbPnFn9+osKe*NOQa9F;7k3SE4R6>J$x^)(t1Hb~`tJKdAOBmLZ8KN^*MJ6{%2^-= zMDI6$f>j|Ryj<2gvZNNz+sdJH5m%Q3UPPA!KJs)hK7?f^I0#opd1>9z{g?PqzD6p zwRUiq@4NGz`Cm8Z%t^2w0j{}JE*0Rb_b1Y)$n{#9pkeWRkieoXKm&7>|BT2cZrUhC zg(b*LTN>B+Jbsvjgyk#ZPOsRqz>jlb)NxUQ^*@p^UllZMzoyBuh)gWiNI4-QVRZIF z4pVrFr*tPG2ysQXAOnV`PQ3aA01=C>Q~fyc>0Wt}5U!0h@P>~xP!-%qykEt4Ka(34 zioGuj)b@{&3H@mhe_cyVvYqhc;U#K*>GX84Xmf3_D)Hn9cwp+<5U!acuJ(Q2`p@FL z$8cK4+O(A~#*&&1?T#s+eov=}zC7gn7s)4egv#(T{gGYKJR+G1UjL`fKGrQ2-UjGM zk{$4Ui%Eafl1E=Pc{<}sdeTpz262F0hpR3J;UnI7^*1D(_UI(`UK~4m(*GFmk)&kq zYNh@eBn9ne|&4PGp7As(q_KM zq+XkYml%fh$Uz9|%MZ%C(`-P%`?@QZ=7HhHMdGqUn^8#EXhObeT^AtwFq?ulGxDKN z%|pK6st^%ctYF|Q^f7-raur_;D@lK>0@+_ zOEPr@Nn{;sDimm+qoE5zJpe=XgW+BF?GqA&jr^H`6b?taGsTfV45WE2545olNIuB2iyRE4 zkYQ^+Y^kgGJ`pXcB9o*iE{&7e+*t1SKZ;8On~~4yTYC18^|Z3P!2n(c51+b^nPne> zompZvP}R=wjsA881((cReffx!2)+xVo5RDmz4TbERVt{PshXrp^@_c>gf8%uY|-!2H}`7{`J2taDHgr{sJ3A(PHVt4-aQ zi6D{ViUikN5$v5sxGZOHqsx7d5OZ^7Fd+TRKj+8I99D?>`&)Nnz4kt@hZ)zK@CKo`#w!@y^Q<6h+Oda1))`uD{ZCHkSJ^Es^^n7%))zy+MrV;~r|s7cR- zPLIDptl(VRluzMu=T^S@1+<=uq<6X5j)Fia5{PDGW>^8>fjD2()_%23!tZxvicws# zp(5=dXwmq4qi~QvWg8A*{pH*!V$S#5oL}p!B`oU|Lf19HjNQB5goq08Xbb+$e1#*(SMU6s2z zK`M|4U2T}gyAF9pA0BXmSEUV>G-{D5vf!n~v4kC{#NTpE=}U}Q#LOZfx_)?teK3Zw z8ewh~H(jqX0?=5YBWfNN9$o%jDCbXG&UGs@^F~eYjB5{^eBI0^`kO}1EVa9J_Y_W~ z9Ge*zD#OVkt{XtH`m)7C;4O0~W-$cx1X3+_=&x4J-!QhW_ z{-kAil#rQw9C=KO>`oi@lNDX4@T0NPot<{~0=KmzokZih?LHas2L)WV$%wCU!9zph z;?r{fk+$T0WD{MzC`;jKGqtPe+@Q$DL}6#DQ!peqcx>FG%iHHui*dVT@XQppdC2 zR&iIX5zFoeBY`-mK2|&LCyB<}h^}+?!=6dCjBOx0FLdsSG-6qwcN#l@IXRLaH9@j( zqo;#|Z;i~kxUs8VsQo6|GT)aDUy3)*^|ce5!3)={OVrcDD()u~d*GY00Ld~w`g_)| zuT0kfdj>KsIzIJ3*!kzNfG*X+_etobn96{KlPL4agvRNgxxdyc?+K$wv=eKN;z;^_ zqG_V8VJEI2IZ$%!L1=vU=UHoTw$~K#{nmN}Vwue@FEFq5cNzCuJ)Yx6j>GUE_in$f zW4fpve8fI$79=FfZdSsmXd7_R&>HM?Q`7)2Fyftfr{t9Q9xMR2xn*$POA;%8QKxuF z^?*!N@k7Z}yPDb0M)&;PId`f%FsQ8$G2oH3#7XMp#$RrDN!dKUapZB}8lOz`_bPm( zKeo9KpB(TAA$39#^|-1Bxe#nFC4nU9qRG9tY!*72zH5nrK^S!fT8{&oc4tgChY)$( zXIrBgE_2j=1a5iAt937DVG!cyhw1b=DI^_Dfgb_+Ic@`xjX%F!(>Gal-k!p7MmO4a zuJ)urwZh#?T5;d5Q==cai!p%#cpQH=w3m!h7I<9fbP8c$l!pth>KgjA)79NPd-rK0 z)Y99u+ubp(l$_zYhCGBrveX(RT|5ano0{c^fdolwP(ikSNO8Lj=Pv9n-hpW*!o%`R z<7sx<$3Nh;JZr$iJfzd%gV`VHw}#GNqAuy1*x9`j8NXWi*9Pp@~uyL{<5bLM>M1| z2NJIc*EkG#8qe`xl}3`Q+t9{452tYkrtj<8l+&zT481^_J~*R&lxXf_^JG{$V_ewy zgUIT!WQnxbIAOw5YqDo@mU7~~*i`u$N;{@9E;L>gOT1U|YgsJ?nzqWW9Gr6_ElVSo zSnlA*rZx$s^7@&465?isEvQ#_VVm7I5NISQ8_;_SYjO+L%n>v;H238USh(i8_Bk@h zW>bE(R2_Z!N)M39)T@oh1bSon=s4R)MwTuYE=x-GZ!C_OqGjDo!s5rn6+iO7tz^Lb+~KASqR zA1Jh4hn_It&2xE~TzYSSC%eIci*K5sU6e)S4F0%#$^10+?sP)Mizmjs*Us?|nKE$G z%LLS_B6}|yq--)FA!VLTj1#;9*-2NE$hb3^W9^MN)=Dph^*5#N;zIs2l zm@7zH&CtB2I`A^aJ3kM(zh8|=b48$NkoN>=$UFc1g_Qszzt^$}v3Hj$Y4#SI=#y(U8C**eqHKbSzpfCVj01Nu=PP2Lp0D3TcfNH1Z-i1=MN{hpmfm z4(w;1-t0(1!fWJ>uZ@V8rfU8F4=@*5ouhr4@j*Trs-IDK>C-={T3qNfQKy>|= z4}P~tlNhUBa_fg%14F{pm5~|JBAFAO|2{w+?8~gr(vN$@dug{ICx_xp#iQB zxwj!FaC-$^Jd~nJTf%EQj#7qmHyR;cx)IChNb~O=Mv@y&_~ID8K~ZLNJNq-Mc^(lv6p_`ez@|)<5n|_H`5BAfpAfdM#3fgedg6hGNNC>6-?c3q<_(DVp zH0+58eSxm5U}p^~Eo%(i6?^OD)}#(0IB)2)!)0`bfJ!&wgWKja@HY`aforS?lwkRB z-Z)H6mJf^Hv2SY<hL<3riH}+kXd-rt4@$VO~?bb#)ijfw(}UJ-%GYw+=H%t9cv0 zz7>(#Hh~w-QyQ?%YQhs8|1RmAUnY*#oWD=X=Q_|ma(WOJcPii0!x~SE=m~#SWt54gy7RfXzjo*<)#JI7V z;CsKm#JFp(CR|)VDqDj{?1ycpZH;Xm{<-g2Mb7=c-3%;RHGq<|#E9au&&L}2?k-?} zIe`OE`gzODULIrV^Z8v&C+_KQoIYsOvCTzH#_(Q8Kh?6^bm;<|vWE@W1Cm79j8J`W z`?A`>vRE2>P&d*1*Q0jeJ>id6>+ID3mj!^5$2vV(0#=mAaoDR{(An;^@_ight~<0v ztt{k(%z*n6T8x9ccCOgL1|AM*eP&nzn+1NRRz&%XZ|h)e5i$FSgW4!pe4xXRm0?V=NmS9>-kkH0ZN*x|7`Y%j3S#o%-yQuVl*LjGut>B2 zcZEkEs^{tUi}yoSU>_{fY5X!{EB_cj5j{fCR0FgZ=+P=@`@GV3+@zRA*I;iN8--K9 zlO8@LV2XHnm%&sfRA2MXbU*8A-sF->*8-mNtDV66LebqO0#|QF#8; zOexbglF0ggcdXdHz$TN5Y~rGk#Oc^iQIx;?#+xj`U8}RK_rn;7n`hNG@zY zH%o%+?;`~s8HW_GX8=a)#Lvnvb9*3h+Rrh4;g^F`oaskwxihX?EYMp2)<>2BvK(MZ zsu6-WLsQ!ox(?`>rDgt}J{_s;0xhK4?k*%AxDngj@764kz~2g+8yRP$6CVa1E=F6t z4}rbVS4pp3;qR0C0-}zZ*EgbyfFj06$PckEPQKLt?MXBKt?LI?q3{5 z>zr(w%VgVDW}&xWaK1B~`UCv6nC$w6D$MkNijbVbyVD-xVEw-f~Afrrs=U+*gde(R``~_w7iBjadh~@7ov=leqNe%cLFd z|1EIXlMZxj1x>TD#riL!pty3gw$Vg`J#JM$JY8^)Jp}u%4pO@H*aP1FO+FV5@r#dO zk>DR*-N2PncMi>e*CWLY7XVb;I!P2=rH;T#Fm%cXbFzSpkvHl>Ff`PfU5G<#e0 zNzZlt$d|khGhmjF>X#GtXTD=44~}FSB#8w53!KAPE8gn)99E0dxMIFAYO$f|fAbVs z4cS_fh5zV&1FBZa@x_+wX6z{VXy#q>-&2eAzdpgF%@2sYeRM2IK)pu^{*uQ3#vj8a zI(kTg?VV2`4WSC7Wc9lmnc7?+y;5-z^u`iV~5;ynia zRl)l2Z*Sf%cuZ*P?%rQ9@S8Sld1hSo$`O9vKV@Gt0P{RXm}X$&I1I=-=>@s3mbVTO z=0MrW)6UEpcoj!KLUQU8s0K3bf(9;&337207m;|TuwwxK+`^ddXZ%V29M3eX5Wd(5 zT_#T5a#P0-EFk9o87%sxI=$Q@HH1X2fsgnE6ow*!?_?cf_Zp|Kz;H6UJ08xaH@2A0 zd-l(5Z`?_c&yN#*!)jjKX8_?I7fLcdFcP1mdT&vHQ)!-9`R&1SuVr#zuOTMx!%L)J zvLa-WXp|C6!EpXN&vP!oWH~OO2S3}0V}0k|>lyDe3QjHcD#LxzHyE(eo$>`6v+}n; zw)4I0|1#2ZC!InT&zCH*i}i-*U<6(!uTku%ob^Ib#IdS6cmR~1%mpRDxwL?R$&L%HRr*BM{E=yH;#*OCnN;p7T4F!dZ>b{1y zK8BQ={&=pEQjH?-xDFbA02`|+{FwU2C^zn&)AbBIC~5+_z=RPI#qnH5&%JugXgOMI ztX#1EycVzD<;26;;0s}Mf>Sm|Wewf;?mhbA2ANT&t8Jk%*S z1x)VlAxx43dJ0S&Up%=MD^J~iEtWg39vZA$2%%ebQv6ugw0hcW-lHK!mAiHNI)BAY zx$mQA^z{La54hK7h~V)Lpwb7v^nWnVCsHM%ykeTvDaoCOCN9TMGiZrgIty+FsDXXy zY*uH_C-A^BHSi6)0k=nY{zNL1i;+iYQBw=u3NyLVO5GOQT1)DAHC|BWiZ;kg>ED2Y ziS~n|fHP5qPEN|^VyK15z-+(1tb0SeK~@NrfP-;Tprh=$&|A6SsDm)s0p-X0N4{gq zhMp=hEFWZzW7zRy3CO1hJI^Pedy2JC=YCunm7d17Y>4AQ!yloFkrpBuST_7Xf3CYwz07!|cG-KKdmeVWD)kki&Uw5@DUaeKR zLs%iCDQv~mAsxDI;CTh_0=s}dxtp+Luz(Rsj?wu6-hF+L4gno3^3l&U#s#l_E&PG* zGP#%W;nZ-YtNS`Z3<8x2X{?Hd{jU>2`4@OZSZq_#N~*9eCk|gxCEBRra!9#UcG3S~ zHNQ}Ke}5Zi_WTq&@X-tFT)Rqnt>ld;3O__;^u{WPINZnQ7x%mKtNn$3jR{$Z4WGI% zzvQYcPTNL_3+mz@zHcC*Sl4Ib|0R4?Zr1HV5u!?0gD`?p04MjU?Dib>T|xCuG#A>M z8TF~aof7!X?(yn;nuPPpu|fj}y{R7!(AZ(@++igXNlQPj%@%@XYn9wMul*MpSiRsgdpqKa zbX55Z&x(VlolAjnBhiP99?vu}V!ScdkFB$Rfm|rkQ% zH{{~=x%k;1-%*#47Rq|tjT#<~TyJ;+WBlDJ<88Y`q^QJzPS%u5-{I4VWww6(zm!V6 z>5TZkXnXvO4KOTFPf9@}JWIH25%~+0zZEY2l`Nn2*5?+3u8lYMiEblxZoeIOh07pS zaRMkv^!;!i)N=PO@N4}l$or`__@%tEA9s=+aWN0sRQPHd%=i%SmHqmVW7rv_#B+g9 zVBHTgnvO>B5BCYGNJ|r!F_DOIjfUd2h3lRol->@X!AaMf3aR)!fYM7=CiTl%T1gb# zj3$RWE(Q4cP9CMw_@fHA?-&iFl1YrFU^ZI>o6bD(46$S=&x?P~+kA06KS`wDS=~+i zA+vR&aYl>G{BjPhi|=DCe7zs(YVcLOcQfj4KUhx#cDg@b$jI3iSnVaO_rv6df04{p zlsZtn)fmg#J}-LPtm|4jz^dn7U7PU1)}KdsmqgiMLCj2*aBKl)9GlGijql-q-EWmR z)qkR*Uv&tU2jjD!uf|fjfw`5-gts{e=Dd&2V|6w}l+=qSVSY!Jt-Pi8^+zmQq*!*+ z=YNm>TZb0%a1BGq@`{QIH8jjHKB%l~3QUIp-+HC@>!8bxc<$BjngA${`viId7t-^uL>OxjTQfbjo(%r!8Zw=!vXI4S+6s4b_zPX{xyU&Z7pA|MS3QDT=4Ck?8 zQ{4NFpU+>g(R&>Wa10LvJqptQ1uXz(U;l0OP4`cxtQAK2xfa6YWiES#UNXR6OlNyx zt&~Hy|4EZ>TC18ZX^Kw0*j$tE4QG#ZKe4I5fA?#ytTu&CrPe5Ui53qBO3d&^t9UOZ z$jjJNK@VH=w8%bv{EwuQFLyV_8d!|>acCK@t5eI?ePea`PR$QjsFNdZsVdE%=v>pp z8X_Sbp+~w~l0+=;8eMPt+0iBY1wkn6FYjlru-h`7;bp%csOS{3^X3(@|M$r8b^h+_w7n1&#T2X$S z@U0*G@6fjDVtisGA^Q9~PDB|*fc^9bI1Vg#CM+fAG#^QZm7twe-V=&dwxS@(2#>T+*^!z&+~g= zC{$Jv_AjtNJ2Y@N<7HDfe$6+W}ha0auL-AQIe<8@pDsSx7S9=nIB%O78-2xtW!Q|az7g#d(+r* z#5Rv`x}Qi$*twUcF2v|o=qfGD#fj4w>BQ+aH96F zcM8pJSS{ZE@=NtE#3USajK6|1KYUpDlJ&d(?a+tm_OzoS^KftECmmlBCno|qjd*ZC zMEfRXe3$K}r&qrUePp`N^9P|DO(iI?8QfjyEe`a+hqkCLu#HSB8Iu*yfi1#gh1bpS z@_>Z#j|^aPMnCx7FqpQ?ydV5*7N&m+En)8mbsr>y?)>$(pw+Cccf-V19x#21DgNDp zIxC}*51Mw~8-4gab#<(kJ}CP8R&6Yi#3Mlhf`qC&!ux~53*=4wc9& z`agJ(>E8EY7Dmb=<2Y?)=s#%`k37GDY6=+aoe0s)S4jdl86Qc$-T5qOQtl!m2YSA8 z&O#eSz?QxDqI~x_0!#2;L)bHCBTe*vKzC|`hfOfFyXoW4nD_PYcjWd$AELs7$W{R5 zc0GS3d{V$8%1aX?W|eXAs~B^89Q}UTSu)05n;TdDm3n?`Z37s|k$B!8W5v|!;j3JJ zrvC${F^l*LI1Ax;I!g(qIwH$_N2yt91BcUQW_9zPP@oO^BgUdW6`O(Sj?z;FU6JnZ zx28Ix>IyQ3TKxiNSmhl4OD%9{BYdxj;4vTRn7F}HSzYtrz2niEEZfe}(3JnIFBIa5 zSrL!cmDxTOUx=jt%TC~6TN5Wt+|bEd%^9R+J3+Js$3EkiQZ4_T;Fh0AlY*D}u-G}B zf*PsVEJ^1d1}h|A1BiHzvY!VoH1wKMScX0s?-S%u`pjV*_>Sd%4y_riHc)ulW$71_!cP_ch<$9d;3}zQE1r&03 zdRe;Xl9BKak#@Ad;g50TB?%S&{_N zCP&FhB#eM$2?CN~kenoElp#pYd4QSjZ@i!HJ>Pr(J?pHq&ROeS^9T0Yv%9;dx^`9V zs_OzCm|u*{I++yV#83h8v;`q9JxLIkI>R22^v#CW1=9nWv4^}cRP zkWaL_^`I(%m?$XXhGJI$J~bw$EgWuqOUIooXo-^INmT@{no`@f+g!ma^?0$b?Y*J( z;;pSsU*zxk-{)ubEiIp#vdYWL_p;v?JRa6Sh(0haTY#|e!VIAeL9TDM9u?Y~V}^?; z^6pox+iK`?bUNMo1uzT=FL&p$K4W>&iHtxP)87^*yhKiKBY#m z$}y-!l@^!zo!+98wdhX^C z|EMUVsr*YAkFfLC{_EY4B1GLE2&*K*WZXMjZ*|6hPjP!1X_K^eSRaR)S3~Es4+~*o zFd-W!*JH)oDJEL_(6Tw>p0My;a@IZgdHVS|TNa=} zU2#cfNMxrPeGU|v8}Kaw?NEJBap4j`tU%uhddt5B;T4xY3F`17N)HFV4T;v2puL|_ zPOV)ctGYA9a)@4y@i2mgNX0&!vy7Ep*7 z+Wbh+;Z-A^f^WI2FeL266i3XHw|oiXc-2UCj&LYhzFxrNjd=?SyRN|=Xpsf=;U1uy zoG(AnKj5Qtd#E(%kt~sTVdb8KZ{53?BC7{dAN5WeJ#(_?o))`3`_BJJ2d(N;HHn^xS0lYV#^5O;mx^{^a)XF^U8BX(ly=Gir@IvVOLc-Z^D&wETO)WnblWz!}w4i$1ioO_M>W927idFZn?&s)XE4vFRqSy zAy2bhmavdosL1E_EP=20akKc?{85cv{t&M_fpJ8glAlR7P1%I8V)-4C!zmTlf$`iF z>C#?mE4BrOO=sWdHM+Lc4MF^!CLApS+R@Lp{^w%-wtjH{+-%1;l-lt*ptSb_vj&4KN^t!#y3hJIP&H-j4w%Rxqc=`E~ z(t8c~v}WM1mW=!uF4JxiU6q#NySvDo24QVv*N?zUD-lX8W@0j@EI|2I74l8RN+ zu4>okH)QMQ-EI71KdO=ynrT>0%h^in*U>=eJgWZaXFm3&=5@~ZqH#BCFuR4}6C@+6 z9I(18Qo773+%q=b24lN*3^l6VS?|X#d!_e+DO0Kr7VQ1Q7mQwSHsXA`INfI`S*ZL; z$<3Dl)X=?|ylfWgDAMQ|8AX#H?(o9rM<~5hj>y_sO~2gy>2)0z%)Om>e6YYvpFHm| z)DE^oqy&(<`dLh^v&nBLbZ z7}2JZntMCL--w{`mqN4O9#DYO2@$B3XL?FI5ikm1Q^85}rdu?R&r5?jz0oBg@@b&q z-7F$#TLmja{$Al6u24uA*6|L-nyX{4+>QTL?Jch~+7-`?Uiw};Bz=1c;3IL@Xor;0 zktlUm2a`_oWgTC_H7d$V-u?gXi3bR3ZVI5vmcYVu5aIyG9w$oJy{ehj{Oe8@uuuRb4~19g{0_ZwOn{N0@05L~ zn>?!n{0FDhE8pbcL36XUwssPc8~m+oL0k+z5nU5h=`&~69yO+0Pu^&_}G-+qbpjjYJ$2_wOxtr24rHVgfcg{c?VZg*Ox{_Pb=vFkus#uO+S3J1SG}D zzrx-^D5N>V&A9}S{G>bi$3ZJ)rO9b*{B&fsiF?E6K{P=h4z_#B6g7KdZljRB6y#dh z%SdaaGxTPTOkIGag!1=RK?_$u!?W0_IFS0O35#kx9-w5ZM;VZ?Fo?T*14DxK&+-=9 zf0bNnGhB}$h+gsBH~zDh0R61ZuncANpaz_-$xM}K_?`e}z4|>iq${-fX3svDbG7ts zAOi;OaUPssXye{J5!x9x_%U_>TLRQ$4(RaAI$3ck4<$>(x0MEA`a)GUo#+95&NX%dwN?_-MJrl*rRb#VM0x;H5SFER}n&&3QJ9F!j>XI;2)nmO9(O<2v zZ)%R^6Nd!6un%=*K>k1&`u6d$cA&s=MfHy|q*f;t>He1sk#oE(%qG}PyE}$|D%uf!#?5Q)W zr~khGkV=O_z>;Dz6ew4Gd10um{tOcpEp zaUT>(7O3X{HMH1*?IHmSz(MF2J@2oMFszl9g))Yzv=c6*9@(Zy&o^|mzDmcK7;L@o zu=&Yd%_~|Uc&2AsDQzHiGzx?oGk=V#&|fqcV84keEhawY*wHQvX(G-zfOT@9D{|wV zzl@OwSxVidc4|!;7v(hdd1>pY7H+YY9kkFbcy+$zg?y1)lR(ty6hs z-A#I>ek~M35%Cxne*@C9(4{8}EJmqyp@o*?_*6Psl^<&cH$}kcqlh!4;t(Wb$NO)z z^iKq6qZpCx0oIhL=3qsBxg7t!9c%im=Pvc$@|onJ&MQ2N^CIz=%fu2xgd6@kzf`4a zHEG^RD2#Dl61osuF0%f|f)QOJJxBAtJvRK!li%c!BD@^boy151Bnzp5cE_yoc6CnU@4zQ16-QC6{UybmN$$ zPs*0+v(bX#2de_;xNW6psUbIyo_CrK0uezRiUF^{Jnq+N662IMRkZ!Q26t$|9-MJ8 z?8GphfuIv~1va8d_gN;nHg`xE*Y2fthT#jaD8-%lG&eCXCq|!E9{x%S6%s=tTAv^> z9MW8N?|uPwwb(;pKBXA!W;=ZT?O!Kl zWc=NA+rbI;DxL|_kn~ws@uX7gPut?u>slS4C{>8S!y+3}Mk#R($8@E>13>e@YD57N zq0-M7LD;Np2I`F$KQCx_tmk(9(TR|P{LEwMQ%WJBmS&+t`Wmrj7yZcyf|S651mpND zo8^IcR*Cf|J~3+Bf0hUlIH@r9^;?|qEJ?L5AJNa0C&vuv+^WScy!wB8;q@}G<^Ba1 zy{vx-sVnQ(3T zWbJ4xzmu>|9qAv~NVRyKJz98x(EX1++tq>llw$q{YcD>iA2))igq;#@X-uqyKslvZ zNQ@B1e~xZVfW}DU+XpvKVmaOFO)MKiiP`s8F~$pXdMRuVrzPp9G#~MBVEr4-`Nz=2 zdn!@|&K>dyD@fjxDyN%MipDk^dfIzipZY!4oyA$aeb|!{yheT4)9l>II!R2>aVrvq z_hce{%;Q#;mE`G@-%;v1N~N6XCGT(J!O(HR*w+E=+R1ucO94#qanpri-{d>FKRWZ& zk1|d-NV4NGBXT90hY7dBK6NIvjikcLg*?KblbG_d0q=$>>hGi(JHO{3gHQ z9Pm9CPz=O+vN?bl)y$I_a}@ky6xw=4AxQVb+*UblaOWdewN1X{%aKvHy#492J&3P0 z%|xbunYw8>`82*W55Qx`W5Cu3W2I|TT{5ff;Z~9P98-xEHTx~_-mE4Hd4QNab+x8d zeH1@|w(Z&zH7I2Tr(csmNhg2bQ}_YQfk0f@xM7-QCPhw2zf4y2S35*U#?#27B>q`f zIPw1s;{qLQ2Zq)<%*8xvL4>O0J8GY4OP;~$U2=dhg$+1w@Ro|^)7vsCelHDiTXeo( z4!$PAh%!Bf^y&4L9ikE!<;U=vZf}H^OxlK-Y`~XpT^9DsefK%+;t_H0d~MGv@d6nT zNNc{>g6}UZ&uFH7?Z%(*xl6p(KMKzBz@rbD^MpKO{><+g&F_&vHy4PTkZ7_yCax`F zr){{k5VQ2?z=C(~P7$bQy~}?&j1_3PmQEs8Dga+{TplX(PRQJdN|j(|t1(F`2uZ*IO5o zTN{17;X$xh?=#;wxuwGVksxK13NE0R`yVmX5L|}+k)#x#!MGaU^MABL2zq>b>an%$Hx73`@Ua^&DnKUTG)MTj8vNOA%8w3SY!0iX(okm zf;;5Rh0>>35cV4`?lJ zKx$rkKh;09cEcqHpySFDpOs4_F8Q@@sH7qG#Cp*1W>_p9Q~Rr?C$IJb8pdAxv(__v z0<}ww@dIC3_SU8h^{G#1%U8>>m8PzYfZaLJWa~r2eZf&JNyxmL>h-^{iiNeEji( z%0G)5{atj-di(3&uL@@B{T*bC(yQebP)RGR)*VXEZhWm())xK9nOkc%x8{y%?U0(3 zt>0F=)OYMfYH}V}M~?-0&i?04N!BNbn`g$umXih4NdDhklq*{H?OlJ#7K=_~11oUz zsvf#Ovk&{wwj7ec;hg>F>{T&+Vn$M-aX~k?NMvTt*_5!|9^e*hb^OCK;C*lQTA-8# zf!R)R+SEM*MSJ;1r>qqBp_7SF4+re1y@FdmO+LY9)jxvMqa7-y_W4$Lvx~4b_&ovKeV~%US_HN@hgeAzenbWaL!SM>s{Gr*DfHy$oeq|^8hdB=%Jk0* zcbB`GTt2-vET1Sknvy-TQiN6=Qt)m6cVrPxHYRbLJwvVw4JayjTD{;N7bXFieF6f^ zT?J=R*GW}ojfmMrXOzw}B2V~dUX1k?AEmqF@_79{U+~#hym)_&c*Z6d`+c-{ffBk* z>sh|_chTO#mstR5M}-V6gOsipKeMM+6*#%3-%#&cgX%gtp%*=!9=~fX5T8B0;@ZOq zqiI*5%3A$$VM*T6kGHdrA`wcO{ptmZz8dBI$XVd<~ zkH$&R{*V5JhSZY1M_sxl`y&#GG%@!Q|ANeeznA&fn$~q6VM~`XKNXB#W<|Qw1EsE1>#Sr38KG#ho7e zp3JyrhG)6|=iVnAy6z`r6J-3?!#wwzev`>TK5Nm-*~uErPJA8Uz9S}DWH zl-Uxe&xQXr@a(tv(|+ z;~QHG5n>vlG`ncuLcbLo?OpvoY5u&xPBhJL<=r+sT1fh!FW~^u30od+j(|-d>_rRi z-R$<|*7#A=uzzVT&{`=SY!7y6UunRlI${{?g-6^+2$~Qp6%5{Vqd~QXC}MAlQ6S$V z5C~P=fLq`&wSz>8NK6Gs_wxd!cgL_3NSZuy+m(>a{udB&B~-&b!}`IVhDLQ8ofBce z(r@otYmhlRi#*vh^87uUu=V;(d-I1dw0dvF$FyC67#h$MJIA;#c{*4vvZBJlP>eBu zj=<~V7jByN1T*IXV3E%Y;O+$f+4Jk31g@Phy<}3=Zk5OpME@LVdL@FGLX_iA3b$vM zVmri{&n6c-&Z82{y9`%&72sPw!Xt@j?S{#$y%@=cR^DaHrigvtTApmd?M|-v(iWD% z@A)Za;jTtY}>xisGIITvTm4UMITqB z8*%4Kept`J@YRe0W0j;KYbK)z@Dka^N@O)7N`b-)Bv6AE+cwEf=^wmd1_oghG)}a~ zagvsx{MU6t%!*7Q<@C@me{g&n)p2ED<60!sTY-=o+WOJx+m*crykka8OCES?Q)P`z z_?3K%j|3wlR@VL2?HA!S1##2J5>w+{k(`|oSZIy~?U}M%JDAAAnS?z$vAO;CJkH^9 zC;akMbb{SJ?Dih)CSP^dN2m+kAs%Qym6j+qatu6{#a^ytY1|>AKl3*erZ-0Ksgz>(ImR9G<gtVhNXP6`?@H z7Fi9)3AqH${R7xe91A;o7`+E<9&e(=xw0g(O_6lK*65E_Jpn}H39(8`D-3=x@kB@H zh8@XFeyTf(8N5O?7NzVTL0@Ug&qyZz;SZ>BWABX&SZQSRSP2Dujva9RX z=xC6T5EMP6DUU!1LpnV;>$i@D{CXGG&9kJDI1c#=`E{BX*=Ql;=Nv}fA@XC$Sems) zVQnFNd*y6`(~lJ~UFhG*s)*A>OdUO4J6!sfJ5?gh^Vg&A%WcRUef)3gD$@RDUoS>r z?Q$V6N|`D`p1**KvXdVw_wNqrCE>WJ%29L*vZZGnTv~;IJj62wMRaC2zK_6g_2-#* zrXOl+64i%-JX9s}GhCYebkgswLNNc=tjhn)s{79@%zvLXsCU)D1-mkj&xUQ_lENsR zeL_RCNOvx(ugU!ge_zkv1ur|9yqZ?tG{U)nfq zv5i9-ukwGLRfM2jMVzbMuzc#VDU89vY1A^6XlFeB(}tob8{dj`9utkjaDJ;Ya-oVb zt>%U4{z`HP?x$ymwMD@||D?L=^{q1axp-fx>iatj7ZwxN#QvGn=YWEazOpHupW#!{hZ74du(J zVYE9`%po8aiLzYR8Fq=Ko%9tiIX^a^j6hk|2EOaAZo`-BLKhyfSO7Ls7J%4?{^*I~ z0~lHCbFw!n1d|E5nJE_VLw=tSsd9vf-4Z|=bpR9l@RjB!zCb_mWNqbmjeLJqXxZu+ zF^dWD)j)43reR7)ScL!P6XfY|xtB4s7ft-SQ0+AiV@^#?H!BvPmVrQB=%uGknCTc9 z4$ZN6M2%wq_WF~OEA(PAqhkp^Uq0?vH;ae%IN=fziP%iC;m!XXl8Itd(u%Y(I1cM9 zWbXAUVB7_j3ICiG4mtHp{loCRT<)R}(be5&%x|Uv!jJvr5G81P>jJLks4l*53O)2{ z6^8~9RT!?fwQi+ew5dB*`zzk6xi-!QD!c~PNks!}1IHD=HW2OKfJ?(kj~9Gxc)(}9 z9|@n`GqI=ECNFo%_m`@UE2M8tBi*3D_knKQdx*h3P_J;IM!mcywcfmp$%P)6SfKjN zsa$0LHh2Quq2v^X4zJ>5OdVJ>nts~=U@5O4VQlu>RJ(2CFZ~2yAVPBqn|>bToR6k( zG5mdnRGV4-`Y5TKKi1tqa7PIHiIcbu_Q-z@-*E9jVE-C@%E|lS_PpC;8a3;#l%9N@ z6cTK$fcHy`E3II?{(6zs1PlzM>z{^duXPX({czvuyDzX?)y3^Yad-5oPUT82vB-`G zM-8>Qe^d0X*$&BUr?^z9LTcD$u88ACu3T#V76lZc zk3#zH0uRR=US{9?tq;>oBcVRi{Zi1@y5RLb9caK@$T>N=W@D<6w}&_25%^%=C!#Rt zcQr6tTZhKZmhV%h=;A*rzFBQRUrU6IT*S4cmI5y4ef!W|a6_ z>;9W(7l;<$saKIPrA$2b#~-=8->e8ZChyWSAKC_Dj}+XN(HYjA7ebHl?Fd5Veq3Gt zc3uJ)o(gA@il%mRc${dr$Dbt4V)Jwlyceaf6<<2xazqh6GH zMZkBW?S-vf@az!B|IBS)tG^Z9o^rp`E>B}%1eDP#17&LU7jvrT11f#M)8W5AGeMf>~3lLV%NmPTTo z3Jv!&jGo|$c1{$^l^D;If%F(Yb%IQO4}Jo;W`4g6gs|phAM4Qj`(Ot4w$wq9L8Xm99;l0hfuq>6h(AhP0+ zc2r>6qg@=a!M6<0WjaKRV4ceLKiKr!{g-?PTzy%d`#=X|rS4w09q=Q~#5lG0WhlgR zAIFToF#?!f5YwSZuY^U<248~J4gLN@$wXJ=kg_7TzbSioHdF|IUYU(}i4z9@`6uVJ zo19h+rP?EeEn3%plSAmw{KeU7St)W&?~aDMCuXqRXJ ze>s429i&WK*oTEEHt03!q ztnHFvy;%50q$%(KG=GQj+6C^M?DrMI6O>urzQ_hFq6XV$8V>LK6vbqm4u9ji3cqGv zDC01pV(cE-Wu1~@n3!@`>zd@Ukoni|cZOiyG4x#t=jxDou@_sh(gi30U8 zx<;XXeVyaVat4_A82QzD{zCH!3|sESdYKmFWig;cbCv7FJqKY4G}+MnLFT&Syvvsv z39vO7$rie>;u7{|$1X!`gC#bX{1BR)F5oiB0Ll?Pv8LQHNtXDVL+lOOd^__f%;mi` zR$(aOLU|DO%WLZ|P~(QM|&ZkUuiYW4ToB---VlA@QNgh8hV)L4hn6=m*4 z5*jC7HA8E?W~sxm2Jn!jfYfrKP?qcR@Nv?;f!-w!E7ZY~kx0N>mKTz8EiR6%Rp&5I zoUju99@Tf6^sTjx(ZSl8jc?}jKb8Mzdnk4k)(_G}I%)Ac9mxj?&C8WYrha{2uFQ`0 z)pJ-TsCX6rqvIFj(3`dhdqft>$CD8IoAnPGGuK`VzFw|9B2_!<#A}$EMjFlfTQaHN zNZhdR$ujjtZuGQyPcZq<;dH2B^Q>w0@LqPwGThvv&~lmA&di~)ZhSHPMVAF+)D{#O zKJc|_`AK@s>|H0EjLA3@2`5ebMbC_I^U|KgxFiPH{Vu6X?Qq&Qh|rSS)tE%!k3pN#Y*7o7eEgtydJ_WElBX-i}~%;+;kzdu6iM?Rh}5>vNG)W z@q9dP1g?{qF7+KTeqVdJ2KtN#ONxE>a?RY&=Rv=)QIWJh1|IwEfwk;f|Ew}P4h8eX z@`S*!ReKe`Op_jmKqL??D6|H9O{#EkS=aYlT(}*&sR*67|KSnAjTQ>K*}+b_FzxDE zcS?I9J(JS1zKytW#^6*CPljQF#@8Ov-BD{?TryHqF4Vo&&ZeV5bQ?$vNLtwEs+Pj)XJnv|7+wWsLqu4<)(AY*wJwf8kaXr!w<=Z7&pSpY)7lJMf+-;{JIh46ztItm4y`ORGD8rb1 z7HEgleVC(0WMp$m-5N5rJGO6Fjcr;uIfj;_lUCO1X#g`Ww@Qx~!0XFkPgxyO5KGC%|I@0x8+l<2JwL?C z+;>RR@7m5j*V($(I$b0+p%j0)ezks7zD%*+LpL^g#t`~x9tOPW~{UdPT&pPW! zP>wrBJ1hJ5DeJ1HmGS$ z%?qlvpZpTTs!OQcm5^wY_aAfO#I+-Yj`LOWic{!^wgyX2AXyz|UXch=q8l`<@mxJY zF|_naIeE!r#0I}#AmoRp9-oXsOmoO_ZCGiv5IVpE-Emke9)l1DmwM^Rg{7%ixjoR% z|u8xB76u zH7$$e1lXW}_}~~c?P?g#rf3ynAhuYLj55-YrX_oolQmLv-m{S(inSF{I=B z{uAK#@z;Xq)jh2#uamziqL$f1Y{C07tR?PpXQ~2Wo^|=D%d`7T;MTe%Qp@r^Jyg-W ze15!hdG$q4yT{+sB>m`!Dr=rJ|B77Hb|^6~PReYdWu4HfboWi7>U@jB8({cqb!H_t z&G=GEI`fw&wDM(KYx-jPPnucwE+(xB1j#Q3ao}mgz+vgh;B!o;-cY<|9e!BRz?D$- zh|utUT)CE8*EnD>f-G zzr*mF_=-b|0@8#z#NJzj>EC=#_OkCxLF)y?Cvt-!j}aY(^UNac(D9#Q9h-e%4t)_G zNiz&Pai^IKU$_ijYJNR^s%pyABJlbCIlh5N3%f~7k}t=*;ZNwElE~cqbg4$_Q)F%H zNn9BCvnSukQX8K@Z9E5t`NBVQ2|ks7bO{C16L@&<+~lhMRS6qUz~0)8;65mo^1+Z? zA|xqL5V@VM=REbWiNVh9%3fiCGcz!`1X($Iaj!bwC4Rj8?JjaR+W8a-p|8I67(K0< z?Lyp87vOO$`}PUTCBH1{QySncd39uNvAZIOeP z_va-R@GH(4hHN}DWUQ-RxNlNdEnVLp=til_n{>yrM!PdYX8XStx`kxSrcBPuh8U8; zLdrOb0!;u9-omCMOO@M0I_h`4#>$F1XcckzqjE8;JvcQPPnJDCIQ-uGC%tjJme z5mo?Nd`7{03PLPK3ol>}T5ws-zf6aJi7cJ`LM4ub&h%M(s@*xT8B2)r zaL0(>pGI^zM?C0#!&pWZD~Wf4m$9ki=KLv&Iv$_IuF1~1&~iHQP4kmrfeis1g(SMg zVJgnRKnGJh1aq`dqSfA=%XMEOY&QNgUL{`(!EJ1{aL=!5KVz?=68hkA=m@oOMvwB$ zgYG^3gE?ln+a@A}|FfT`^u{)ASA*8=uThN+79T?lx(P2pBbyBW>yS#dz?H<=wn`^5oYnsc?MtWD+4(;nguCX^i!*`aWFWXe>nebFzdKtrP6|@_+1KWR&KmC?U1%=E|$4IyCi$Zyz~iF&~%OC zfRHE$NP+mB`u!K2(AsQ_XY1WomlLHB{pam{uPHP2)|1d-gKrV{UI}(0ML=PZkxe}YOTi5YIJ@A&)OGtaEjIO8e0)c{Etia3D zy!UIX7oegT*7WQ(_)1S~Lf-p>-xJvc+zXCu-8FLUrN^xKiFD~y`E>R!D%8L)|6KID zb6CJiiy;=8oy3W4dIqA5g9+9l&kS3@S6&jfR}GX9wx^fha9 z+d^gG6gT7pGS^O~L$DbBklIblnn`Y|GInkHc|T@AR<4)!jzh~4szqK?^Zuo`4leZA zi-}580lF5+Rfcw*#I5>04TGA%#_SD9$lyw?7p1;K7Z>x+cRcL%I zUqPvm8+ur`q}Eb;Q&RQX^`+j?EbHEbY0)7YDf8s={c4rd@#7q{PKe!{kjv}Q2;wZy zpt<`^*qHi12O1#!Lnp@Q~FoA5H>FDvyE=hm&bUi)rnPtW{S4ImGzx<5X0ya7hOcqNLqV%^*t-na4vkQm!TU8zaanQy5Khs=|WT^ z(jaCNzA_$o`(-xc&UI|gZ4Iz{zc2@2-6Fl&Z$Fbzl$Uj%w^>(0E@!0b!DoObRNt_X0+;xy6Fb z_k3tSdG(&)aF{s1%;|}!`{{l~f+_M;%h>K0-312?y9_u(S%tH9Bk9(?p2M=|)==+# z(8!sJ`WQ=+^j-}~clZl{_^?cp=}t9E*G1g_5K$^Jdb?}6pChg7OuZ9s4Dgp>s<{?` z4QyTthPefe%*ZBR-@+|GR6ofcWRJy{bf=(Og`e{3 zxCCUBT&iOr=c>U@YpbJUq27wau;`mpriR0a^I^*+G!}~KqO8Wkt(}BAt#+jDVd_@TMPuh);7ot+gfI#|n-u2|}| zpBcFk%-Rg7R*|lmtX>#iX=A3>5KN9jsH+h#bf}E93jd>MyD*~X9{hAgAO=jU&Dzmk zhlsg{uw9*5Nf&81ujgsv)qX=w#-x_WN}099Am{SH992T$Wq9C{52ukw?n`6AgF@ag zFx^{~VVO)xZ_h6A_Z3{94!dh%TY0bM|MufNpm%$xbI%wX$k;f&0x6E;*n}aqY1PyN zT!)Wa^+VYjeW#6Kmb-hG^A!jq;A|Sf-7Rj>$j@}Jjc7e{CAq)Yi;C8F+1nN~G`&&C zC(1|_x^Yz!^Y`SFf2Pv1m2X9P#c`V!Y@$lB<^LA+Z_T1*;y3{ z$}?r>Ljg3oxOL@~`NPMLbRM}epcA7?mx66l8Fvwyr?7REFSs1l_p6=_J4z<=HN~sE zYB@!^UUeLnAlqY2tr!)<)(e=EZh&jYvcFzTE9jWGblg)hOExrnOAY5Ve$3L1OX@PG zg?;fvwqFi7n`q5%?_aD-PM@fZ(z!MlW)>_!nsvXhK-5=yzDTpTz1%V~8y`p6df=W> z{yTYBz1mtbiY(pj5$V-7%cm2f*4wHky%dl~(a!niaDQ}Cq20Xy%}Za@#rFRx*I-O-VS zn-x@P<~GI`n-_@qggm9*;o(OnQ)Z5uCwj9G2{!o>4r_$Voc}uZu@lub12A4vryr_U zKVXr%Pp3?T&WBTlT2ZzZ76_e&NF257?kz=Sc863C{V1#%ir@D?=WaNhGWq6Xk=cw3rmv&*hon~Y z`MJ9?C~qZa`y8oCEq&3mKJ21R8-Kd)D>K6?>C&7T-gOJjL<_@W)YE9C9u`VKDp)3r z)(htbEP3AH2*Q+x1*SFhU|q)8XY}x>x_UU#z|d6cx4wPSik7URjn|uATNp-l7jBzv z*?gF4vRY56mC%6zsqgphCSI}nsmNYz;(0D@-&W_pi`M9kEX-toyB|994mC|}5{Jl4 z^G7FL4d9VK;>UU>lPeD}nTA%B{7et_>hr9>9cHH4QP=F6h(mNFzs!7nOJoif6{^-4 z9ObU9lO4&_3g`9ovk|furTAD$q*+?}c+h6$^xs(caOOI@FG|>7HJ}mSa=t*~?_YxN zI;-@Jj!%y>s1Dv_tUw`Lsfn6yTIH>m&o8#p5ZuxH&97Z< zf3MuI2}AE44Oo|!)s577$z1z=@K$06Do0>UTTfCd2CN28yD9iiyk$LYW9q&N!X9ml z(Zk+b?84V08xJGn_OZbJ@3MdSZ|_!3+*a@P7@>HBN)cjVXN6F*@nvD1AO?N9ewj9T z)V<^}J|RL-{L_K{`pP=8v}O)5)lt3^p1!bCfxsB9==NW#zgXj&9iFMzN=;|nZu&WcIDt+yKlI5mA$TM2pCnWu!ivEH2;o4J>nyI3ltG`AyJ z&j>$8gm#cf%VX`3(M#YBLQeGhP=*iGkg~0vt(GnGsB8Do;>&Rv0zqvfTUc_OlmF*#K~SvT9$JHL(zW5r;++Ao?)J z7VWf%vzxwav$m_A{2%HM!yNylZdatRLL1@k!!1xEEA#P+4CJ#08}8SRg(=p~`|&m0 zcxURisd&^=DwvdF_~I#9M+dfM+kZQhS?0V4MB&e_2eZa+mp8m9?{LEi(S&GpDWq`3 zfo^+nqTlX4%tyajS-6_^(1I7^-wk`=<&7`O5of&bs(a4g`+Si(+pwe>-@RBjiywZs z1ob`P2T{Mfun~@7pEJ;4c6K$Re7HS|?B_x>>94-|?Pbd}zgpGL%QrCR*s2KnoUXbH zfLH{KWfGN>5Wsv5W*<@hc@Dnkxgi1aYL{${7D;VGKR-d6bgy>`!c)iPuWoG;wwq`&P(iVKSm{Z&4|I_T= za_7Jh+oX)ULuy1|%4VG(n_n1A1W9Ep()mwP08Yv0yk4@(+j1wzRM zy)t?9vjY*>l{4J;cHxSRK3dWQ1CFWx5t=sTP{#5Q`!G(3NnK2gkG{rS!rji8eU3Yw zfs3SHuXj`2e+UgF-SDZNplwBa4dP|@5lNm6quiJK3j3^${M`<4;PSsq{|p1@mQ3#< z`bN3q&%xvgw5SX5%+eT1BlA>q7pZ37rB4N5AET^){SMZx^_&=f1T{1H_okifK1+LX z!q<@f?W%kGu#NTGRc`cZ9ioZUc@9H9q~3})7*szXIk8e(6wLw}J%gJu; zelpr)50wa#S$CjdsKA!fUBfHZ*Of0K&KitDbI;aOKCPP<#IwhtuP81iY{?U?51!Rv zE5xl66e}YFj|KH7ed&5SlD-I^xT$hJ!P99azAw8^`5-RiLSOsaouXMnu+~?I_jT5 zScSyf5lN36EP0?h`0a#hGA46;8&=!CaQPkzp04|~umc`mCuQPvW#_yl-LQpE*s&r^ zW4;r46QQ#d{KdiU+0a`&R5}rFGRI<@FGc^noWI;Ff$w=1SJZd6IR6)OZypcTAHRPq zNrfb`WEo3R$zCMOD5MgKWC>%JvhRdb*|SW@mSrjxWtV*$vW$I_WM2oPtTSVo^_<^) zzTf-4|GfYHJ^VRl=FEHMyqD|sx}F!|f820UG1&X`5yA@knFM+d<9)TE9h_XOI%J+-)kLt+HZV9oQ3Q@X_N3eFn}(@_4j@0k%IOtzQr zI1YvUV#v&_HL~?&Y#X4~IzWjMiJ+}61e>bE?6pY8lGbYIT(1d9yutb;Wu_zK>fSHE`G|vOu;%SH*}3-)C=si(bTbp}8~kV0 z?@q;WkK%d%?zoiI{&|DMCG*ZqKyxAfmRdqZrS!nx^fbdy2THXC%7CijP@2j!1*R+_4(EiNR#`v8glyvZtnZnGq zlUoFd>&Yg0ON_mW;7h@12KO&Nt&{)ivC#ddzdgczH$vk7+=W!-*R3>u3{#7H?zQzv z#9W~DJwBhmhGK5axI(V~c`>Ra^T(JS?gp_|rBF>0GqJz_HY)E+C$OcWvQW z8Vz}0k!xp}{VGLB#oCzOS7l#B5x5XJx6wS$;Gc6GKh z>fx$f?~e`3X#>lp{=*qsgoG=xm5-Ga8vimg;iH{W((XLoQGv}&ITz&zy)CO~vzBjN zx;toi^6l|#;4N6u0#$W{t@kTn;ReR-xixJxM&UBe0KBc*o?A~oo<2(m&H9{aJvHUR z+Vrv=*x$RrkKPDgz&qb*5nUP_HOcHI^DDlcpp2`mT&)3I8$#+@Y`c9g!1l-$MKpyJ z=;(Td?6XB9QFPl{J#hpXF6jNLy=Ca+9r>=JJLerg{Fes*)&Ii2vnR(~tg3Zn&{huh zQRP>&cbD5H9K*yiJ7v7uXq!;#^ZhoH z;cmQnU8mdQvv|}P`K!!z-m$`kRG(ri$OiR`T=qL96Vp-VW#0c|co*xt{tS{q1)bN9 z@vj$41WkX@Ooryb@n%IA3_TcVBb*a`=m2a4k0z`2X#O1)6i87_*sX>9=-S$wO`i!C zjK|H5Jf5bZ_Ms@}>!Jjd8#C-K-B^zv93EDO{t2&#D$z%u`yrTiCfdvn`ai4wROzKv z7#H}fNHo;=GzpmvWSb6hlN9Wrou&kk3n>!dRb~;P8^AcEZ8e|5F2(soMEU4U-|r{a z>*ZTyYPbYa!flH$R{!`A_)x4mNNGEB`{H`F=yC?z{gL;z9=1>1nRLb0GnwgJb4_39n+3F8*Pw%aZhC!t=swOB|WLbVK>7PnQ!+N?M4-l2AI-^;n+&>{aVO-SuOLFp3MkpD3L} z|HYR>{AF0Z`RPvSMdSh(pU4(}`dqB{F!G1YZR0Tu7gG9p%|hNxYg2x^kd}f6Kz6(K zeCxXWy#RlD{ajdDz*37g67iEbIQXMN5!L`n6vd9#C@giLXp#i-;92t=Z z-OrYLELT+WF!5}Vc4Wyl8Ajn^EXgE)vFB44TJvKjRp!ELPBX7B+hs}f|B6`0k|y)g z)(o0|9NSoR>u1`{8yzqa3l(RNv??0;l_^Ev-TbB~`_)u(7yHw!_5{uB>fcF75v&bk zY!c!^n0~`0UVd^C+uS3?lBg@0zXP)&SsXWR?jsH}-M1*lP(V`pmJ;P8OUi1LwLWM1 zqKBEm#IhAFhfmUV2|J;QLsdQ!T1L#g8C8CVQbpg;#sdWRCx5SG(!_-UOTX<1OYsEU zjFUi*_vmRDbY9p~T=ufYgRNG4q?{AW*aaJ$vr=#8i++ad{DfELZS@1lM{{qq>UP1w;RS3J=Wf7Yh9kiqta0e-!C+8fZDds z9~}i9)j^CHV51$QpPZ=B5}y}Az0)N48-rz5A`{VHr9J<&yu}r12`0Lo;Iclh$2&xV z7P(tYCZNq=`qg=;k=t9BqOq#=9PQ10*}8k8DFL#HuckgNxd3{%fU!)7%r=<;13z)d z6?Eqg(CM=F6LQJjG9{(e%GWWmtcz7i7`LZ&lTSGsw>#K(>A`S_t!H)@>w1adPw$hf zPeY{4Q{6Z;v&!ZH9gY}KrZTWM85cabw|0X5`0u>Iio5#Kb=$iEa4;J>rSzS%)!DFS z@z|_kcdT>T_nWeY0S9*$5R0d&Zd0s!It}Mz7!Pz$pM59j8OHlxapkuZ-P=LNRfGpP zeNsz?2axrU1Xp_x|9E0E))d8dUkNI#bAJ0aC}fTg(h$cYt{qLfX>vZrnp>m^(G1o; z$UJ~sx(!#rxjwh;!&{nyYF;;pVNz>xVYzDjyKfpsxiH8oAMsNi8hp0C6ybp&8wvjx z*DwFUnM-%*Ni8@3Ejbdh?2`zdIl94#Y)9SwfA}zM`)JOS4S%sA@3rTS;_P5s!%{l4 zP|gee#AO~^ogy~7L{Dq_NPN`Oje)1mrdBPr|7vZ1T$1_wJMy3B$tBd)=sF?x$&+4Z zncWXbC|*`3!6vowD*qv7jA%L$3uqOOdEVOr1nsX&KI4DhpkIb@;L@YJ(8fM}iX-@6 zj!vweS!9`nb{wQ~Bm_EZ$Vkpe&Zk5dI({kcO3u%-2Xg?7|MIXJkzSM@rfO~P&J4hPMlwLj^2F`K1EadI?R zLfoZGaX5P#w-(WxJ{26Bm%KfIO0KuCWIH!uIm}=R`-U5;n%9~TcDdAV9^Xfs@4u?r zf#a|I&8r?OLPZ{)FGg?$TE#z_xE~7kVQ$r-fK}R6XoMOR9q|j|r^3nav1^29^uj%pS=lpPVR;Ab1?RrqDQU+54m;2WhlKuPe zl8Y@dft9gR-qe)Od>m@i0Zy#n7O6mnxy6s0J+$3xS3vj2f8a>m98K)!{v$-hncgc% zpw`?X6>yw;PvU-<-7s^adL&l3*L<9Qqiu&0wFh=)pwUM;1o%5X_LeG+JPWw&XwO5;BXJAMfubwTBty(;2M97Q0-bm>aLV9 zzx+8V`1Sp3m(OYGXIeTcO|EP0_gr@DBfxxi1XgOt5m_!-IWN>AVG6$(x+)Rh%sZ+^f%Fa>xh%D8DUe;=|v6Z?QKXB#JWF^IOz;qODc5FgTjd{ zErCZv^89=Htjj<0;r~Q2+#kSbGG3b9Gae1vYlFAhT9F9hJNVADc<9W#u^g3xmH(|p zkwg{&dyYO1S;;NUD_qcH{pU+vTmf^B7P~l#OTwg^R&G-gBBP z7G_#vcH%?_wpY#qZaHT+iw7uAQWtZTEHj_3&mI!K+t#k95uL6(x+pP_D@Z*QCRZ|* z2oBxjOJ)B4nC+)bO=lN<_Soe#$xD{u_P>1~lXtm2XL|(Zb@Y<&;Ff(X!wb!ec1DOx)6}^Xc`i zI(c;8u#-qJIk9ICI3QqdSo8+0>b|IShaOpA3&e`gT#W3cop@LPW3rQ%AhHt9MI_Rl z#_)!v-mPI1tvIN^F4jwT}1^f?X;U4%y_X)Tc3=e9G-2w|RHR*g$=lox&u?bTZzqC#a zi4c?}qA$1ZM%%11R91Q?KdI*S%oFKF|88Kit!wK>?etC2taHF9rJf)g7SWEV`o}z6 z=;jIRd%N|gh*RUL+xkGY9Seojd)Ial_xDT$E(|Wxb!AR6lHf^?=%eHeD%OGR+hjOn zo2U3S8VeqvAE70{HX>2BdhG{CAt#puSjhOK)x11i+R*wcCS^7m__tp1L7T~woa&fX z-m$9ojyQ-TVxMoNnp@c20sU}BA$FrT%Q>E0AchTz990O2W>cirC%0$!*5Tv-}$0& zS#{QjvB&HsGJXT2uNe@_I=Q=*9vvVA?alyb_Ck_^*fE0_PS0MW7lu~pkFhhsi(F2H zE^6UoD?ga2PWXc7cDInC$xbcSbB(xZ7lJgu8}3fNIO-&&5f912X1CLo1OGj14>xI{ zctlwF#;mxEnLo;8)eb(kTALo)9u}af4Kfa_{vEv_FE*n5Y{R7`M~{H$T6MXcJ4!Q| zavFg*)jQ!|UdWb?(x1fwk6R&^2%k3X7bH0RKy&wss&u1SbVhN%wIJ~H&=78B$A;2l zwt8iB4JCd-a%E2N%R$P69=R&FBET9Y#}2YcNEO`|P(Q5qmUGL|+j+tJh0sYMwKxw| z(1g$_zsqeO12$gIX2O^*9jnz-XFmVyTd?LXzf(SMY<(7cyz;QAJYm3_PcbZAHsjOvWLtz#-lxx<-9s*$IczT`gp zwkM_DxV^{qdHj0MLxF7-gV4G5_MxE8Qff+r@6(P7 zh`dfdBeIhsw~+eSeP&HE$Gz~w} z3V}UvQ8`(atw3RDB1AZH@tXBq72^-FzocRS1%`8M?Md}4y|>J0ot{4P@Zt1`@|C$5 z#Cc-J%jB8;M{%{*31|<#GpV~GKnwi!BP|Wk1$xxK-y{tGXU4*NI5KScmuz`jvg*_Y zt+$I3KJ-7<*FR0sS+`GRdDhKY1GCf>U2TOpF@jvWwWVf?m=D%c@hn{e2#e|u`I>2vM(KhgMs zhjJtJH25AR5A&Yu0808b*vv4?%>8pCY~3@s_Xd0T7gcCcxKgYOe=%sn>lu?}qTwid z>g!A9S){y<)E>p%|Cq=7YEV^ng}F|C9LP}Qckn7we$;9-x0fQi5@#SPYq6+u?!R7u z!8|3iIntJ`5ZMn}|5(P{DBrB>lpOwDR@V`=5MHv+7_E z#MG$G{j9est?KzlTs%Od|ALtK2U5-n-2xaHBfDQ!>pw@y=s>cX^GG*;Iy)@vexyHP z+j%zdPX`{pKZu`yzjRE5LVaSYqN<7l23J82HWl#6YYeS~*(xQ#*f+Hj^yUe5nz|AS z*EU+BX06<)#OHdoncIc?R*L`|BTYJ)jOTQG(XtH`(qt7yg3t^SjCgd8*ue(d$kp$H zI^tlN2?GmeYKijwQOeqw7bz{!EynjkMXUYxx(T#r>9KDW1lUI{=Kb6Zea7J(&1R{t z0HYBMmb)!F;iJ{5@*73aZSlZ+ftSy;B(voULZ=7_*je}VuXV55>(We5Rj$FF907m-$De`!dpXx- zI{#*k*_KiPjq$zSk6f(S|&oAw;)bDrE?djl0owTq0RuyYS zcJDoFH-L$YI6)|Cxm)V*(-FPq%U7O$+NfbXq8?9z+X3l=@2(os1~D8u@2KM%16{8K zAF^wnB4iC!joINU?wn!Zf~v~DjdYqAbUS_TSSM%5W4sN}gj>RQHQHn&sX1;A_lixs zAYNl_b7sOhKp5!%dgOk2!@sAKcwST*9`!uM;48!brmDl=&pSpm;~ZrnHQJ=!)U?sY z93GbI)s*_5l1nY6a{?fV(G;LkqL^S{KqSoXP zyDP$NLk8#I7`W@MVCYu2fX*2_f^F3vEOa_3Fnhfk@g2-japeX5r{Kdexb29&b^1Xe z;Ei(15n%3?n;)?;r$aBU8Ys3*04i*|(D(bFvb>|2UF-WL*;@O52s66Otr*DsN$)>7 z@U$Bpr7=D~i-FJ(2^BwvouLwlKJKRVl8YaVN7o|=%z3T$BDb&v5sqjOb5e&5%CCr)V-<0DC5z_Jh9WCH?ox8g9pIL z^uWsOWp)EmDTs`LUj_fpRC41{g(U6gNJvjNuDsct@K+!O`jH68qN!*O!WZP2GjI4( z3}`|#7!ZRz=|P~uj=z+L%qEmlpQ_1Bnyw{nQ`P)kpUHAJOCS%1T?ThJ%DK^i%fFUZpQ9bA80?f`XCfUDAF z-h-2I7Hwg~mFcNZ_rJ0c?oxbP@b`be>Z9QC_y0i#eO0~?|FIca#)9fPAO2$u-Z51u zDyiN9P9!sFg-qb6{w2nLe(RV!|M3s`!c&azXET|DQT^f%-Zqr_qaJ1c?tASLLvnA9Wpx;HHJ@|7%{7SOUfx zN72DQ87fCV$(j6V zU+UZ1gGH@`7$kKWmi)?I)dat=cHet-3RhoX{~HJNq%awCw+`K;w$n5ZnX@TF-N*jmUbta{I@tKyOZb<^7g#SQ56tgrV>JEwE0zTKuT z+*V&6i0h~APw8e_``u~84QniCh1{;3di*GZ+0@v1lE;=zFPsKPMWQEsQ~bId=HEoX z3GS4IHq;xFNDo9PhuAgE-5seA7T>CS1u(1Q_!8<{fQtMFXUri47-{YJkoD3TjfUl&eheO{RK!X8Df zw}?G(1D(i)5}OHMYrDVyXlpdTqJ9Qk#=1kQ%=oA8MO$IlA%9f}SLHX#^GRZ|q5t&V zUDZg>sG%qN8#7y4E>aVhXxJYMx1iTAZ&qkTDM=JI9FiQp>-bq@BWd{gX{OJ5Aq;;} zqLZJBC#cDC+?Pm3v)za2qb~jdjIy=bK32GZL)a}`7P`;GnRPd6a&5qb3@*oxb8ArJD!1zB&rZ|7}^qs3&|(dWf-- z-e{XH&&$si#z<5{T0zrn9E#-`ctpqza9)BPciTs=n~sZ$Zw;pJ$y53*xYLo5%eurm zzUGP+Dz~4o(?ONNDvkSDiwhTJDvLW+3Q4J=A5ieiwi>Ov_!za>u8VF2ivu)xtHf(sWKhr=HNaO=yeHMAMaX`>5to= zRZ8S4__isX;0OTxHn>-!V2tSS;~4rn3MFHBxmK`J8oaJCU|vEt*qAIBg5OqFYi8zA zS3WyNWn^(L$q6Dfb`{IQ$!cw}{AafzOXy)?*?FThhwMbLz`-iw?@?GD=bKkoa_1>+!@lEo{&J1$Tg}T$h6?E}mD&40E9me|ljp^MQR_}o zAYr082bk(j)OsoIGt)S~_twKh8{Aq&%gsYKo1111M^Naf_Y>-@3i;4!W>FN2yY;*V z4l7G}kQ$tFLhyMI4m%DPD*b()JNwgKhnCu1UnW39e*y92NX?HLsqH((OZNC6{Kbr8 zj|Txq(CGkCU9BT~8)q^!;Bjy4LWcNph&AK~l8~aa75?Fq+ex>2Ub3c*+a}6HBYQa8(Ah*aj9NU)Ta+%@NJuvlb99 za}M9XClEV3yXOYb#*5U?7KWsLhI~iLxMkbfkjAmDJ$ftI-|~WDF1=w#s@)2ay46id zHd#%OfVDjNCOs9$@WIHd?FFv$F{MD^Zk3(5TRMpU+B$xFMne*u^eDA_IFrfN)}68p zB_@icyv!}IHX`+2j(vOhl5?7TMTUO_&N_n+=%@0@PubTsW*=Twj;$4gm=e$N&{a5j zxb|cof!nisk`L-Z3d6LFO2)(WyNCAplJ`;dFtwi zpthf4G#998?*pVyc()mmEVe4g5ak~4=p3fI;Uo~R;iVkSenuIPhzrPt4 zebONVJtw{AAE8w$Fqu7AsYkU7jV>ly|2emMGHw+O`M8Bn%Wi!I*mS3tPfFi`EaP1K zl_5v(F^b9lVLSCmt>CrdS_*8ZvSWfJFl6Pn`myP|UcopM7U}o*-d!D7%?UO-{~2G& z7Z0A1=uDtJ<)A0Td^kmQv%GNjGs8KaQ^qwn(dw4h1dM^Cn0)ey-hvYD-#UKZsx&7V zis9@OhyNw8Xg^E6fh7m`#V@iima&JI9Cs)&Qs=i9A_ zNS|dT+Y6eX+}o zXWgyeZ$K`QEonQeBAjrF!>PDKcaPw$?rz8^#W;uWFU>Y@Id)!N0yOMg!j`#tnvq(@ z<@y`o*Pg8)D{j;cjit(pK8>J>L&-dDo_wSKr#PGa3X;A^ItE=Ia=Gam9PWri zrPE9_ty65(w1V!tb0$n1RsDr=@L{#r(CY{sWxV_}nBRp;KDj_o)HTa@>|Y(7i}k>N z%{}C*{zuWOqVKJo`@n>h#R-X%4PuOA!09iGGR^;LuG!C(y6vhN z&zk@3wTfm}3j5%(H8^2+tUxuhT{2lxb@I64^oYFl<_T_A1C18th#7T#xfQzA#EXKkmrvZk$^8w)A*90v3e-gA=nJZ~rV18YPZIt?5DGzwI?Q zK48v5KLu=oedyAIKd$~`36)X2f7r$u_taX{QDHjKB;{*_;4jMzA zsgEfR+=T}X-h7gErqMTG=F)5(;cOOdG8t}oqgY|zo9M#*sZk&DdMtfiuZNg0ZF&2U z*8)4}^2zu8KY~ou8LIdF;-FNU%8l*X@uEFV@WyqhmGWnrDV;{%hz#H5UJ=#Ue4+Mo z=xq^1N-C}Sae^9@2=<`W=0a@Gn-oa$%E(*5Auil@`{4xJplP#5wk~JXH$UP0*X)ms zxF5M!mITKxjEdVF(>R&P7S;^y^vhQC3deu2@p!{P=t8c2eFd|noAE0yEtI9dMW~LY zM1AKUe!Q_845Zu6WS#q{TW`N?`GxQS;ri21&d->QrAzHBEQ(`y^lm&%KJO_F>kw8w z*?pMHPDqomJX$?yYS`Z!b1SzVcP@LJOVdhg5c}q1ltAY6k-1i_= z!Bmqal^~Ix#v+vIDEwQzviL+UnDfg$<~&1KrKhKFzbO~`{%9^t2#z(cwMxt!FJ!yp z@$?SAS`c@Pe9Tfjq_vJ%*na)v%HOo=+x{F3f6Kc8@kuMZ^;K^U&$KgdAKLmRXz_@D zD%b1b1tzb_0l0ege%ZpXjj^BR#l9Hh7cgdbI+HT|t0IjVGWglN<;6OKM=G|ja-Sze zs!mH_1Xe)w6x?zQYXnI@Kd+tb@?W7Q#&r&z9N<2g6!4lh1^J*uM#mLDh@MTj>b3f@ zVuz(LU!Z5|Qbv#JrtIEZje9Pa(!Z^};gG*HFNx52O;xFZ)2GF?fuR>cfO!VDV_AG{ z@P@xiN%fXRMxgiVb@NckAiccSZ7Ufun&ws`)qb&eXT{RoT(DLGb?e1Rj%3p;k2|Is za$65Fh2*2p-;@wn(~Vi4N72c_L2%k%WH3OnfTV~)Cr{p_7rJo{QL_*U zi9tz64+VS!dJkrj6A!nGrhlq`9?63)=##{{AH_3dCB{uWWj z;Pf7Yfu+VJ4#N`@>DJnlAp0tu@z=x;`~? zF#a5c*oMcE z6uO$RsnAPYAqHGX?5G~~@s z(!ZNmUO4oHl3ul=vb zp#(Q7_>?x$zpf)wAb@THg&xd?`E>yz61?${YNrVR*&8&WL&t`E&#u$g z=`>4<0Oe9vk_CP0D0Ws6N>2Zp18HOT+)iS+Ul39S9EbeJu`1e;p9^HZoY3wP$KBUR z;pWvau_|qUiA7q}RXPNUr@Q5kD7EdK#@E<1h&n$}nr7>6ORaQzQe2dbkx_2Pkf$MK zuT!uTxcwr%bMOs?PyJ>e-{o>@47He8@${HB?0aO|Oya`dg`Z=l6qFnv;_?k=)jGqW zrr2uG87U`&c+7s$D}YmYDk70e!Vunfp_t#>45vRs~|x--K7@-pG_5rU3;W@g|7=DCWAkhix@Tzci56he?()j%pj^a^0#tr<;D zgxY9$XaltccA}wR9_g2S9VhmAdJZTCuZ$2bzrx@knjkzsy_jrY1I>&BJ0t(RBimj} zL_GRQ?WOFJxGCnMCsv1CnlZW*EZho;Ac7$lf zblu$}rSAjiY^PJuyUBhj;(qBjZ_QPkGVjrfa2@b4HYYsPYU z7rxDkT{&Z#==FQdW2&b==#07H%?Cor5+X!YR~ay5Z-@% zV;xXf?mDXasIH{g`tF(vNVZw2NQ%LxGC9qS!H@bNO5&={&*Pw;m;G$e0=dWb3Irpg*^DE+q zHO#Nbm#Ll;rJ*kkxb9yJ)IM?T_W^R7KD~ov<$>}6q%5=80Na3gScD#(`1Vmo3F;3q z=REimbpTg80s8|QDoz_@0F~cKg@n#9Xg&Mtm2mo@Ti5#kiLhRv)coFuJns+HResY8 z)guU?Qsc-C|FG^@_<93Mk3k~9e?wGh_y(&q_ZaPoY{B>$sf z_@e$Wx*V=@CHv7^U1dxIcsLHL3SFxuRghaqbIvSg!$c)4ysP5ZxZamE?zX^m4ux8l zEfx{(d$&b&W95$asPOUm+h2Y23&(A6XL>FD0q^v`KF*1bW79b9QjEaQL+mV9z$B~V-MW6#6dcK16sMdQ}ov?KcbXOqDvt=e8&(dKm?+U z-==q8XuSAQjRIq03Yzo#=`fh()69L$SS<>RzJUwo%ka^D?&$b>aI&p`FdS&RfO!fzF z_|#aYCSHRc{n63RQUCmX82w_6S1 zB)H;B(gYN**T5E~@S}&271~L`Um+?y)pNa?$@zt5`5>=pcc+xqU7aEXxo1SUYy5PV zHC`2q$JI5nOeRFe}Hn6BUhjwo6A@@Nv*}k_L z=8qDS0(r~TT`|9L+-{f7GcE}S)8;X&2psp34Pcb}JU{j#I6QnmxN5r`y&I!o14MwK zXYuoHA#R(!D5ctpbraY-2vh&2C#Xmf!sGyvO9xm;tO*J{-K8xPDJX?G6k$wJWAfW* z%#F_(j+Fn>mxBU8qle+(xW7wie-s{8j(#mAuIBHB&jk%&WSzc{a7j!ufP(XwM)1(~VM;p>+^elf-Cu49SQ%`%D*UNhlF zd#U0uqGtXh_bf03n_Rh|5#=tG>M4ulR*XjZ2r!GMFm5gAixXG9FlYA|FQu^>I>bRk zP;N9P;S{90D9m$9!A|=+%00KO(4V_D4~Wq#Du?TI?qT-gB6mF373$zGNeUa!!n9Io z_03R!#YEEbS%Hw9%SX`)rQVBrwAd^_a!~R6;>P*tiDSSqIEw&XUFiO8iO#e0ZO$AM zrm4TCL|orE5y(BLHeNADNLObEjpG*jLBanfDU|4x-cH?qCPaZxh4a7RH+#2Vn=xEo z>wDko9~k}QU00AxyO^W7R3qyBm`Fl9Uy5E5~IePq;B0PE%2tJ5PEakAE~6z>nM!DWis6?192&0M zMh|}>{Im+T?)Wrje)bVCN}m`e^PZ3tEV=0?et|At3Gu{O8a+H+adT5@82ZUOQU@JB z{=)&15^=h!HAKMES?hJ8?^XDKJm+7y=4zcc{YLvZ@!Ik_bmI7rDt(_Y>*C2u$f!cn z`L97hkzClQX{5~EpF9IaOMverD+W@Z1=`P0tMa;KFB2`h&sJ0&yWsOH(YtF&Bb)q% zAvZrBAC$e5aKd^~UZ0J7s`gD^F3X#CC30LD4mg^dq`OQ=knLbn!UUOja)$6>!ZF(l?k+_-Ok1v`vQCMFFa0Ti9LU(tLnb7(egS ziw<>?&>}g};og_?hUw1|&^%bA0JIzI8r?pHv+%kC7M>TEs{1}|Vhd~lL*oU$blaY& z_XR-vAx-F7ECus553pel^u=#}(l_>poquSGl?>uX*NDpX8cZK=*53PsbrUgNvDpU- ztG-5CofyDtk(yq`v;NYp^V|y}-OZI7gSeG~<9@01y|-T-Iwo?^T}(uvbjb+D76 zfDNn)^&s8bLNBulo=H7+btyiGzA}2j*X+_O`Ocl7V6mh3aR<^oiIsS4!r(ex;x9+t zCpOhf`kJ^`k^c^z3=%xc+~)t4_}_6I9Y9;zTCxh_FR>TeJOp){pBg^Ob$D;e^U4Z% zBmd6vEVnUZ_|Ucpye%+{BIy-*4LS#E9xaCuexN^PiH+&XbfI1shV294TLQjjOMI&c z1;OB}CSBmj!RtkE^`Fx|;I-dQM&2-p-66+x>xFBbh3&&}3P+YLI-5^VPhQrY)E@;8 zJQ&DO+UL2R6)H`7U7`F=Ncgh!@0Yk%khC-=o$~hdR_vvU;b_D1cj_juLlQSH-c;*F zlXWW$0vPM&nt3r;lGQL!1L+`XcAU561l6;2zIa^7Jt1_38c8quuyNBgB0cBmsqrO3 zGW_FvP3P;eBA*^m%-O=9^bWaB_RGK9M|TzLi|D*l)Br`8=N~)_k67)>5iH3J_}HY( zk+N>Gpt5+OGTQ8lEr7CRv?lY)Hyv?2jzMYr*f7^kDkUzQMOQ=Y`A|efdoSW-<51iK z^6SLI(i^@HU_5W(YBxXKI;B+oljnJ-s%El_aD_le7u$Vg(O1pL>vr!Q>78mDK$0{D zX72+ocD^t<2UV*+lrARfAjWNyT%yt*XkAk4ek z#uOoI;@AftvFg8uq7{Bug$7-H+_R>293YNU!XY>76E=WdaSVbQt9edGBDd-{BgW^9 zYr2X2rl0U_5#W#`tI3*bDh^qWBOA`Yi92$^StYXxeX9J22eI$7tru(GgC0b$ zE$HwTib~fQcJ#eM`f!XGCV2D0k#l4_N4xf-i##_ib>1n|_G<4e`W=t;u76*28Q zXEQeK>D30;pr&{fBXm!Bj+ok^x)h*19|=v48*~AYeA1_okS7C625cBr(4j*Z7!mGu zq8ZeVWQbb4{HQDh;kqGBvAgZ`LzJ6BxStd_wWIU>2EMl(Si~>;X$v-6r+nX!lK zuLze%-Ky1sQnB&p6`vk2I%H{|)8wFZlQ4>brHqoAAR%shzDmkD{VTpYyY_4W1g<=B z){P*Mo$Dz!;z=B5JjN4zCKq>RyI@|GI5)##EUqs}Fp4qK#;Fx~!~H)U0)3CYmunbq zA_ckHUMu0qEez1}?EQT161#La0ad4a+to&D=+9Jo6PM^SqnaEmn4-2Ff%b2ug!-tB zvF%Pqh(OFO%);Z89eC?g>SG!}uNht+uLvTSk(}NEt?}dT=*YdW4YwGjz8O3Q352sC zv7I{S&hw{9Gi)dVtyrZ9x(t60T59JvgPQ{Fm4qB)g&TaFG|C+((;v^V+z)3gHrfIm zSl^b&&!K7Ze2+Gt_!5}|joaKa9Ht@v(s>Ec(~Y9=f~*l&DLSHgFzmxEOU(di(#liq zS<3o$*T)Xg4YLY(W4$)04VKK8xIhA@-w&fNsh7h8;ub~9(ThR^SfuZXalm)MbvT%j zYsr~)9!H-i_<+zp1s3|sO0s;6_3+^crB6r(7Q@CT&?vZCZ4yV<+I6P1%{_XuXGIr=_)VKC;o~FlbkMUc1 z{^_OlV2o(N7F7!4+e<|a!;Xk06-Ui+beHJD_9Az?M6`U!IMlufQcPI7)E?$xrp0&e zABMGvfBE~VuP$&@y2s%_2a1CuY;Cv8D=IosxaBkesRCU?V&fw^>zfFQ#}0VyPSetv z7?35H!Fvf5y}ljB@D$iaBhd_)?dmz!&o4J8FKG1AKSoyUWz@=M(5h1W@ar;u0`th+WA#M*qUNsIE^BqT%e7isg64D;$k zs)v5J-z8?uWdQ#j^Y}dLQ{3Q@|i=PQ8J?|0-S)Mm`E|uyf5aWYoAK5 z(xT-sd>EaG5T9&UvjN1c0T4TmAQhccz@5ETeg>x*?|TLz9{vpr+d0JfM|$JgO4`CJ zjDEo><>t34T<1upLfbbMi>}pn?O%J)?e?>EM!aZ3lUET)_{O^yE5WYyAPCIY;-+IDaTHGD(G3hN7fe%kYJgPBIwwUjO zM-%{|Ad_9?%z;JU>c7`RsxZq3BKGza$ZTDSi2}`rnmVW83JC_Mh>2mez3q#dz>6YD z!Oo_|oMs_ok$2O*K0hT#MlSul-$I(?d=Xu7q^t96Wk((5B$o%rg7_Vl5dltdgNijJ zjR9?~NPDsOhm}f`;o;q2+?vjs;$0CSM>*TfafR-GouYP9fPWdfOShkTH5fN_pJQE* zz~`syKFTfr(aS4WNH?Omez4?CSor%&$Kwfa1RNEGG;;!l>ncCW%T=j@>Zn5>L*uds z+kb&sA@;5Eqj4ANAuiON9>O@Inu3_dd3#TrToyvVcU(j66mvQ5E66XWLA?0VsVjLs~ zZGe}(uZ134@_b6jU$0o--r*ZZd^tTnKU$0~PVRLk*I-w`gURV&Z$e%7g!_h8j-U0p z;2M=YUcuh@pEW!JYaLhK8%9;hO;xejwy0*r=&6pp&Mg*$6$=71M3;W7ux<~5UGwyt ze!*clYkWXzmG(z^;&0$NFMOH@wne3$2g{daZZB~OqA9I!7DE0QmpaGewPQM>kN}g^ zSK@uu|Mv0mm(pXHchk{5INp@!N~bM5-yaFClfK+^8U}QzD001IU_pUnoa2H?d7suE zMY=uy@ln=2su-)tJg;y{sBuRV5T^uL`hs7{`&-qxJPeTDhj&QXW7GqpT%W0UZ2x%N z|9-|;Sits<@TWQwc^DXGCW+{$kbw3PQrPF$S~f@R`=uw0e_SQpZkjiEvH={~u}ew< zBo$dM>1=rzS_Vj$1Fzi3508ledQLhjf-iTqfoH1!^NI-!lbvI#Shy&2u1t#MIUDuh z4c~14_WwL-82-xO=XFT)DMBNY$YPQ{=+RUr>KaHM( zDn~jzmQGy4pnQD6IJnOwIf3N;f7-k9N2tE{Z(p-#30a1$NtQBYn6hLoOA(q3DI}t- z33syZ!z2+ItrCe+CcN!C8B0<2j7%Ean8b`R_j|oRpFiOH2Yl}H<9Y5q_w29d^*raC z(s^dX$rK84pX4}xcVeC%IX0I5XzB3fIR=GpwarO+LsnGLM}RVp7%J0Fi7o54FcHai z=0$eT!J-1{rZgV>m~MvtPRq@yXJsqdW>(L1;Ap{K^R(Fwk#R+iTZx;I$w46}!MvlE zDg=BRjd%M=Zde~}%C5036{b);(MfolQn%}4bEDEpo|cSeIt$XK-Q9kiY4iCt_7WOCkj8@VJdw*#RUc8Db5pi8$FX_lNu%;a&M zkdjKh>LRYurPX%1DBm)7faiQXaN_J_ytYu;Aa%U-23#_ZLn~7rIAx2$ z1jTUXj`$k|wI72LS$m1!!Cxx3^VoW+ybfYYd!EomL7g;5*_D?;cMd^1^t#jIA-7pC zusu#3s!)7sI@>L1;tXmU!tuQrzB!S9aU|q-F(4Nvq)tXNQsobws@DZg>vq8Uw~AXk zoCn!COXHF4mkx+Vmp`@7?QxWw>>#E>SA@qnvQ7H*?zDe8*z7G{J!(!$_71#z-eIog z3rW2v%zn=AU*FGF9x~ousO&M2J2a17!dq=-_j+iQTd~@4o$=jB?xM}8r*$7Q-h9y1 zIVPj&s({X!%RNZt{3`0GCYlA9sUUb9JHLTEuqC_f=1!OWUeDbiqgQ;-prV^O!16Rx zQGmGY4?T=l*QTEmu4#eso{-LwxnFx+@$@`{#85&1vPsnE&ZRi~=TijMK8uluC_w6> z6DIe{Plk#$3P1r1f+lH>FO_cZ+W04Sv%QuX$KSJN;3&j-ErFxXl_-F#mpX>uPK2i_ z5$9Os??rXN?b<$Aq7ODa>^4aLd>Em@F2{DBc$M#YPUedcA7eUV;FoKVP}^Tei++-z z?7X_axIGjukRfw=2+k7cRTDka?H2B|F^GhJb~@6#bim^QHU&1sNThE`sS8shB^DML zzLDXv9~$cGCw3P_nvmI&g?e$Zr7s_7S-$!D%{NY_qRHZtZpEt)vXcWMK3^aDQ?5yV zaP=vodsi~VgZ`;}Iu;OUfXsA3KYjDi<%>sF&|*r>Z@b#fcj+D#HJ_PBU1QTedb&E@ zIQFqnvFo+&@L0cAz?-h)w05`6Lle>N|I6fm)%E}BU6#;_Al)GS5OP{&^iR_*nI&f$ zAFS^Km`^eZR!K$C4$CaRCUzBt>Y;v(YHXCaSEFpgXEt`FujVqfr{(N={v1}ZM774t zeW|Y_xHRs<{Dq~ykmSHW8Dad;6sx;?@5X}6%lqruOgulgEg3J-*GwzO)@RTC-QpNr zvXI1XL}<^yt!a+@*1B&vFR!pma&tQgLq%V1V;RN!bHC&$w;c20H8}oQyj!tGH2*0C zhmjU7^hlvJf6Y6U0%XN`SV-F)9^(S#N8)7k6=k&ezkrf;P2Ala=3%Q)(MEqr;FC8q zy7H=k{qlR;!s`8Bd7fOh93JVaXxo+pSHngflH2WbQG7Sgcs8j9fZAO0K!u%!-QasI z+aoXJ1&^L9gbzP~MxkSf9!pzwSUd`PPeS^h#Gt`{f4O?U&RRl*q@|tQ+jN}W5|#Jd z{R1d-&1-GWU$&(=h~wahWC?M1mOzx7&AFL-t9`a@z)FkljH##CO6~K67F5w(Vdf>% zr_N30T+}x=0@J#}UecobFuSigA$Xpm+rfG6Z*%H_Zbel5op?7mh)7})2}uI@w7iSnax(%*hm`7jAZw9JrE%9alDIw}Y9zEb_MY!-8Rh&|Df(&GXC^ zF%nZ-6$SWm@k@*=j-!dl9P>~rQxHC|jFHmgA0AM+AGtd{cdS%`#MsmG+tWku$-)o{ zf|Bh6!v3FFYN`fzqgl#_5ujOOZPbBE8Deu{A{m|jXjQKS_(SgwZ2;wh>2U1D*bv!l z`*+6ecS)2B(!HC1m(@Hu#cfnQcZU^UwH$X7z2IjNlI$K(t-}yF;o^PZps@75WRE?w z1PK!w4q66Ulxsqm$|+E>)0q8*j5wB`i+Aj+ZFL({iuGA9a|?r?X&3S#+J`JHbjLV8l2&!lpN3TBjH|bCp{~^)WDKki=uNv1GH? zp_UW|n|03V!x5mi{Hl5tQbKNg*2z8IW`+h3k)p~!g;aa}rlMFs=vMV9WX1nzYCxJL zfPEl@a2^~gSTKRi7EUqenNzr!t}L=5F@QxKC_%!z-%I>t{E2KFfW^fgy5_ikBqWIg zq5O`!!)V3GVqWTcDwTBD?X~$;$@D*oM0suz-&0wukOht}5i|9MLeoxwh<})qGjSc6 z7wPyz3ECGD+^i?kYz1EZzVj4$tZ)Nm)NWfDU7PXfw@#o*oVnCD86Y}!7u$67SveE- zT(kcp2|Lr~GOsVDU0MwAL7oBtW9#ILYhtU^-*CtLTAkAYv-8!t;GYFeEan4>wjSh?L6GM{_mt36Z) zk)wWUwoq;~k%Klf`@>+z-O`HzS^YueMgiZm1MZ@(869*##(;(9weJzECn~)~@#i{t z>~d)i=3R6v1qp9M6~VSZle(Q|rnlH~QybS{8?P#?T@)9C$k<*#E4`r>Yw&oebBvj6 z5S`8~;?{EH;xcdSH3LGCOunF%_7UTkiIaC@NQzb7V80{3UkKAOqQ_9o>r0W|9IVF^ zYF>3W13-=Yu<-bq_>U>j7vRAQMmBk z7m@pjj@F-8`4xPk&|sTg5)2U@pOq9DM}Pfpd+@d1x@7v9e3S2t1eL+&Z${h5tzT)g zpvhg@qR!U-vU1yaHswfDwzP}!9cy_D8AqRnv+A3ttC}jmzR#mpB<%JMa{6=OP_#RY)@d` ze;Zr4CS32-MermTAsGa9tv+c92ZF{StV*FEMvzqmQ4hyRg~6Fs`|OJTRA_G5Na z2w4b*#VbMfGGug^&M|4KG+k`qn;g)m9Or13qgJ**Ji+x#+B1O)L%-19&OMJ@HhFo@ zV^=2N6<3xhD-CrG=vbQXmYRY>tGOzDy>qH%6B}}D0CU!h!T=W*k#$j`nhIvoTdM(E zM$HW0t-sU83ITUcWeB7Try)D!7)|MVRX&JJdI60%$>*P$K@C-$jT>fiVw$hAppG!h zDjUcrSM)^|F8JEH+NP&0AcH1%^jT)@+k$C7!tr-l6y_Qv&t8SH#KZ@YNf_E%NYauV ze>xe7;Tbm*f0`!Jq4_+}Whg5BUnnkAg*1by2;4FxM8p~;4I*3Asx>mDmWzR3@Q$n8 zzJ0wXz_>K#j_Mll;C-$V3p$G-R8`CKmv8A}Bs`e3$toP?5a_E(FLS(-6@19|cE(DZ zyvVhM_$unkTxm3cBRLAgDH708v@b`yBNSzjO_9wziz`Kh@0E?2L6<>H zJf)e8Of^QNQY#{ZVSJP437OMkcMc?pq#_%yEq*S_tn+ydrh-OPHmQwPpGEw@Jv~*N z^r)08_92KcJ-z3$gzxGD-T z+P_y}C_rCfk8}6)WfpI4oiG7H{w}UG5T1!vg)0C~!|TPDSuDr>_{N=-Pr=nS0y@P< zKaHfw2Tc{Ua+neahSaykU&>Cr%RBjTeXlXQq3tjb6%ftpG;xn9k`Lbe)a8&fg`y^} zh)~q6F9^74Ul4V>aT{0m?tW+DAyPuc3*w8K=A{zZ_~WKG>pjOEUWHJ$ORE65M-><* z2|HcpHOQk+3}=PjimSQ1`}=3XD0{{S_SX79(wbbyq|VII&|(@fqn|cSdH8nh@weLi zSPxmbcK_9KWq&%^j*P^jyE6wH0p=?1aCg}C@4*#gq6x0EEWeAzbN4rU{ci9n_HWC;&R{6^DzzLcWot?ODeyN1 zY-nhVscFc((I-tkKkgURY98Y*g|ytbu-423Bnz=fAa&844SH-W&skKNdBy)9Mo>;K literal 0 HcmV?d00001 diff --git a/hrms/modules.txt b/hrms/modules.txt new file mode 100644 index 0000000..dd0bf64 --- /dev/null +++ b/hrms/modules.txt @@ -0,0 +1,2 @@ +HR +Payroll \ No newline at end of file diff --git a/hrms/overrides/company.py b/hrms/overrides/company.py new file mode 100644 index 0000000..04a5572 --- /dev/null +++ b/hrms/overrides/company.py @@ -0,0 +1,101 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import json + +import frappe +from frappe import _ + +from erpnext.accounts.doctype.account.account import get_account_currency + + +def make_company_fixtures(doc, method=None): + if not frappe.flags.country_change: + return + + run_regional_setup(doc.country) + make_salary_components(doc.country) + + +def run_regional_setup(country): + try: + module_name = f"hrms.regional.{frappe.scrub(country)}.setup.setup" + frappe.get_attr(module_name)() + except ImportError: + pass + except Exception: + frappe.log_error("Unable to setup country fixtures for HRMS") + frappe.throw( + _("Failed to setup defaults for country {0}. Please contact support.").format( + frappe.bold(country) + ) + ) + + +def make_salary_components(country): + docs = [] + + file_name = "salary_components.json" + + # default components already added + if not frappe.db.exists("Salary Component", "Basic"): + file_path = frappe.get_app_path("hrms", "payroll", "data", file_name) + docs.extend(json.loads(read_data_file(file_path))) + + file_path = frappe.get_app_path("hrms", "regional", frappe.scrub(country), "data", file_name) + docs.extend(json.loads(read_data_file(file_path))) + + for d in docs: + try: + doc = frappe.get_doc(d) + doc.flags.ignore_permissions = True + doc.insert(ignore_if_duplicate=True) + except frappe.NameError: + frappe.clear_messages() + except frappe.DuplicateEntryError: + frappe.clear_messages() + + +def read_data_file(file_path): + try: + with open(file_path, "r") as f: + return f.read() + except IOError: + return "{}" + + +def set_default_hr_accounts(doc, method=None): + if frappe.local.flags.ignore_chart_of_accounts: + return + + if not doc.default_payroll_payable_account: + payroll_payable_account = frappe.db.get_value( + "Account", {"account_name": _("Payroll Payable"), "company": doc.name, "is_group": 0} + ) + + doc.db_set("default_payroll_payable_account", payroll_payable_account) + + if not doc.default_employee_advance_account: + employe_advance_account = frappe.db.get_value( + "Account", {"account_name": _("Employee Advances"), "company": doc.name, "is_group": 0} + ) + + doc.db_set("default_employee_advance_account", employe_advance_account) + + +def validate_default_accounts(doc, method=None): + if doc.default_payroll_payable_account: + for_company = frappe.db.get_value("Account", doc.default_payroll_payable_account, "company") + if for_company != doc.name: + frappe.throw( + _("Account {0} does not belong to company: {1}").format( + doc.default_payroll_payable_account, doc.name + ) + ) + + if get_account_currency(doc.default_payroll_payable_account) != doc.default_currency: + frappe.throw( + _( + "{0} currency must be same as company's default currency. Please select another account." + ).format(frappe.bold("Default Payroll Payable Account")) + ) diff --git a/hrms/overrides/dashboard_overrides.py b/hrms/overrides/dashboard_overrides.py new file mode 100644 index 0000000..5c5fd15 --- /dev/null +++ b/hrms/overrides/dashboard_overrides.py @@ -0,0 +1,76 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from frappe import _ + + +def get_dashboard_for_employee(data): + return { + "heatmap": True, + "heatmap_message": _("This is based on the attendance of this Employee"), + "fieldname": "employee", + "non_standard_fieldnames": {"Bank Account": "party", "Employee Grievance": "raised_by"}, + "method": "hrms.overrides.employee_master.get_timeline_data", + "transactions": [ + {"label": _("Attendance"), "items": ["Attendance", "Attendance Request", "Employee Checkin"]}, + { + "label": _("Leave"), + "items": ["Leave Application", "Leave Allocation", "Leave Policy Assignment"], + }, + { + "label": _("Lifecycle"), + "items": [ + "Employee Onboarding", + "Employee Transfer", + "Employee Promotion", + "Employee Grievance", + ], + }, + { + "label": _("Exit"), + "items": ["Employee Separation", "Exit Interview", "Full and Final Statement"], + }, + {"label": _("Shift"), "items": ["Shift Request", "Shift Assignment"]}, + {"label": _("Expense"), "items": ["Expense Claim", "Travel Request", "Employee Advance"]}, + {"label": _("Benefit"), "items": ["Employee Benefit Application", "Employee Benefit Claim"]}, + { + "label": _("Payroll"), + "items": [ + "Salary Structure Assignment", + "Salary Slip", + "Additional Salary", + "Timesheet", + "Employee Incentive", + "Retention Bonus", + "Bank Account", + ], + }, + { + "label": _("Training"), + "items": ["Training Event", "Training Result", "Training Feedback", "Employee Skill Map"], + }, + {"label": _("Evaluation"), "items": ["Appraisal"]}, + ], + } + + +def get_dashboard_for_holiday_list(data): + data["non_standard_fieldnames"].update({"Leave Period": "optional_holiday_list"}) + + data["transactions"].append({"items": ["Leave Period", "Shift Type"]}) + + return data + + +def get_dashboard_for_timesheet(data): + data["transactions"].append({"label": _("Payroll"), "items": ["Salary Slip"]}) + + return data + + +def get_dashboard_for_project(data): + data["transactions"].append( + {"label": _("Claims"), "items": ["Expense Claim"]}, + ) + + return data diff --git a/hrms/overrides/employee_master.py b/hrms/overrides/employee_master.py new file mode 100644 index 0000000..18e17a8 --- /dev/null +++ b/hrms/overrides/employee_master.py @@ -0,0 +1,116 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe import _ +from frappe.model.naming import set_name_by_naming_series +from frappe.utils import add_days, add_years, cint, getdate + +from erpnext.setup.doctype.employee.employee import Employee + + +class EmployeeMaster(Employee): + def autoname(self): + naming_method = frappe.db.get_value("HR Settings", None, "emp_created_by") + if not naming_method: + frappe.throw(_("Please setup Employee Naming System in Human Resource > HR Settings")) + else: + if naming_method == "Naming Series": + set_name_by_naming_series(self) + elif naming_method == "Employee Number": + self.name = self.employee_number + elif naming_method == "Full Name": + self.set_employee_name() + self.name = self.employee_name + + self.employee = self.name + + +def validate_onboarding_process(doc, method=None): + """Validates Employee Creation for linked Employee Onboarding""" + if not doc.job_applicant: + return + + employee_onboarding = frappe.get_all( + "Employee Onboarding", + filters={ + "job_applicant": doc.job_applicant, + "docstatus": 1, + "boarding_status": ("!=", "Completed"), + }, + ) + if employee_onboarding: + onboarding = frappe.get_doc("Employee Onboarding", employee_onboarding[0].name) + onboarding.validate_employee_creation() + onboarding.db_set("employee", doc.name) + + +def update_to_date_in_work_history(doc, method=None): + if not doc.internal_work_history: + return + + for idx, row in enumerate(doc.internal_work_history): + if not row.from_date or idx == 0: + continue + + doc.internal_work_history[idx - 1].to_date = add_days(row.from_date, -1) + + doc.internal_work_history[-1].to_date = None + + +def update_approver_role(doc, method=None): + """Adds relevant approver role for the user linked to Employee""" + if doc.leave_approver: + user = frappe.get_doc("User", doc.leave_approver) + user.flags.ignore_permissions = True + user.add_roles("Leave Approver") + + if doc.expense_approver: + user = frappe.get_doc("User", doc.expense_approver) + user.flags.ignore_permissions = True + user.add_roles("Expense Approver") + + +def update_employee_transfer(doc, method=None): + """Unsets Employee ID in Employee Transfer if doc is deleted""" + if frappe.db.exists("Employee Transfer", {"new_employee_id": doc.name, "docstatus": 1}): + emp_transfer = frappe.get_doc("Employee Transfer", {"new_employee_id": doc.name, "docstatus": 1}) + emp_transfer.db_set("new_employee_id", "") + + +@frappe.whitelist() +def get_timeline_data(doctype, name): + """Return timeline for attendance""" + from frappe.desk.notifications import get_open_count + + out = {} + + open_count = get_open_count(doctype, name) + out["count"] = open_count["count"] + + timeline_data = dict( + frappe.db.sql( + """ + select unix_timestamp(attendance_date), count(*) + from `tabAttendance` where employee=%s + and attendance_date > date_sub(curdate(), interval 1 year) + and status in ('Present', 'Half Day') + group by attendance_date""", + name, + ) + ) + + out["timeline_data"] = timeline_data + return out + + +@frappe.whitelist() +def get_retirement_date(date_of_birth=None): + if date_of_birth: + try: + retirement_age = cint(frappe.db.get_single_value("HR Settings", "retirement_age") or 60) + dt = add_years(getdate(date_of_birth), retirement_age) + return dt.strftime("%Y-%m-%d") + except ValueError: + # invalid date + return diff --git a/hrms/overrides/employee_payment_entry.py b/hrms/overrides/employee_payment_entry.py new file mode 100644 index 0000000..f108210 --- /dev/null +++ b/hrms/overrides/employee_payment_entry.py @@ -0,0 +1,263 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe.utils import flt, nowdate + +import erpnext +from erpnext.accounts.doctype.payment_entry.payment_entry import ( + PaymentEntry, + get_bank_cash_account, + get_reference_details, +) +from erpnext.accounts.utils import get_account_currency +from erpnext.setup.utils import get_exchange_rate + +from hrms.hr.doctype.expense_claim.expense_claim import get_outstanding_amount_for_claim + + +class EmployeePaymentEntry(PaymentEntry): + def get_valid_reference_doctypes(self): + if self.party_type == "Customer": + return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning") + elif self.party_type == "Supplier": + return ("Purchase Order", "Purchase Invoice", "Journal Entry") + elif self.party_type == "Shareholder": + return ("Journal Entry",) + elif self.party_type == "Employee": + return ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity") + + def set_missing_ref_details(self, force=False): + for d in self.get("references"): + if d.allocated_amount: + ref_details = get_payment_reference_details( + d.reference_doctype, d.reference_name, self.party_account_currency + ) + + for field, value in ref_details.items(): + if d.exchange_gain_loss: + # for cases where gain/loss is booked into invoice + # exchange_gain_loss is calculated from invoice & populated + # and row.exchange_rate is already set to payment entry's exchange rate + # refer -> `update_reference_in_payment_entry()` in utils.py + continue + + if field == "exchange_rate" or not d.get(field) or force: + d.db_set(field, value) + + +@frappe.whitelist() +def get_payment_entry_for_employee(dt, dn, party_amount=None, bank_account=None, bank_amount=None): + """Function to make Payment Entry for Employee Advance, Gratuity, Expense Claim""" + doc = frappe.get_doc(dt, dn) + + party_type = "Employee" + party_account = get_party_account(doc) + party_account_currency = get_account_currency(party_account) + payment_type = "Pay" + grand_total, outstanding_amount = get_grand_total_and_outstanding_amount( + doc, party_amount, party_account_currency + ) + + # bank or cash + bank = get_bank_cash_account(doc, bank_account) + + paid_amount, received_amount = get_paid_amount_and_received_amount( + doc, party_account_currency, bank, outstanding_amount, payment_type, bank_amount + ) + + pe = frappe.new_doc("Payment Entry") + pe.payment_type = payment_type + pe.company = doc.company + pe.cost_center = doc.get("cost_center") + pe.posting_date = nowdate() + pe.mode_of_payment = doc.get("mode_of_payment") + pe.party_type = "Employee" + pe.party = doc.get("employee") + pe.contact_person = doc.get("contact_person") + pe.contact_email = doc.get("contact_email") + pe.letter_head = doc.get("letter_head") + pe.paid_from = bank.account + pe.paid_to = party_account + pe.paid_from_account_currency = bank.account_currency + pe.paid_to_account_currency = party_account_currency + pe.paid_amount = paid_amount + pe.received_amount = received_amount + + pe.append( + "references", + { + "reference_doctype": dt, + "reference_name": dn, + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + "total_amount": grand_total, + "outstanding_amount": outstanding_amount, + "allocated_amount": outstanding_amount, + }, + ) + + pe.setup_party_account_field() + pe.set_missing_values() + + if party_account and bank: + reference_doc = None + if dt == "Employee Advance": + reference_doc = doc + pe.set_exchange_rate(ref_doc=reference_doc) + pe.set_amounts() + + return pe + + +def get_party_account(doc): + party_account = None + + if doc.doctype == "Employee Advance": + party_account = doc.advance_account + elif doc.doctype in ("Expense Claim", "Gratuity"): + party_account = doc.payable_account + + return party_account + + +def get_grand_total_and_outstanding_amount(doc, party_amount, party_account_currency): + grand_total = outstanding_amount = 0 + + if party_amount: + grand_total = outstanding_amount = party_amount + + elif doc.doctype == "Expense Claim": + grand_total = flt(doc.total_sanctioned_amount) + flt(doc.total_taxes_and_charges) + outstanding_amount = flt(doc.grand_total) - flt(doc.total_amount_reimbursed) + + elif doc.doctype == "Employee Advance": + grand_total = flt(doc.advance_amount) + outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount) + if party_account_currency != doc.currency: + grand_total = flt(doc.advance_amount) * flt(doc.exchange_rate) + outstanding_amount = (flt(doc.advance_amount) - flt(doc.paid_amount)) * flt(doc.exchange_rate) + + elif doc.doctype == "Gratuity": + grand_total = doc.amount + outstanding_amount = flt(doc.amount) - flt(doc.paid_amount) + + else: + if party_account_currency == doc.company_currency: + grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) + else: + grand_total = flt(doc.get("rounded_total") or doc.grand_total) + outstanding_amount = grand_total - flt(doc.advance_paid) + + return grand_total, outstanding_amount + + +def get_paid_amount_and_received_amount( + doc, party_account_currency, bank, outstanding_amount, payment_type, bank_amount +): + paid_amount = received_amount = 0 + + if party_account_currency == bank.account_currency: + paid_amount = received_amount = abs(outstanding_amount) + + elif payment_type == "Receive": + paid_amount = abs(outstanding_amount) + if bank_amount: + received_amount = bank_amount + else: + received_amount = paid_amount * doc.get("conversion_rate", 1) + if doc.doctype == "Employee Advance": + received_amount = paid_amount * doc.get("exchange_rate", 1) + + else: + received_amount = abs(outstanding_amount) + if bank_amount: + paid_amount = bank_amount + else: + # if party account currency and bank currency is different then populate paid amount as well + paid_amount = received_amount * doc.get("conversion_rate", 1) + if doc.doctype == "Employee Advance": + paid_amount = received_amount * doc.get("exchange_rate", 1) + + return paid_amount, received_amount + + +@frappe.whitelist() +def get_payment_reference_details(reference_doctype, reference_name, party_account_currency): + if reference_doctype in ("Expense Claim", "Employee Advance", "Gratuity"): + return get_reference_details_for_employee( + reference_doctype, reference_name, party_account_currency + ) + else: + return get_reference_details(reference_doctype, reference_name, party_account_currency) + + +@frappe.whitelist() +def get_reference_details_for_employee(reference_doctype, reference_name, party_account_currency): + """ + Returns payment reference details for employee related doctypes: + Employee Advance, Expense Claim, Gratuity + """ + total_amount = outstanding_amount = exchange_rate = None + + ref_doc = frappe.get_doc(reference_doctype, reference_name) + company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency( + ref_doc.company + ) + + total_amount, exchange_rate = get_total_amount_and_exchange_rate( + ref_doc, party_account_currency, company_currency + ) + + if reference_doctype == "Expense Claim": + outstanding_amount = get_outstanding_amount_for_claim(ref_doc) + elif reference_doctype == "Employee Advance": + outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount) + if party_account_currency != ref_doc.currency: + outstanding_amount = flt(outstanding_amount) * flt(exchange_rate) + elif reference_doctype == "Gratuity": + outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount) + else: + outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid) + + return frappe._dict( + { + "due_date": ref_doc.get("due_date"), + "total_amount": flt(total_amount), + "outstanding_amount": flt(outstanding_amount), + "exchange_rate": flt(exchange_rate), + } + ) + + +def get_total_amount_and_exchange_rate(ref_doc, party_account_currency, company_currency): + total_amount = exchange_rate = None + + if ref_doc.doctype == "Expense Claim": + total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges) + elif ref_doc.doctype == "Employee Advance": + total_amount = ref_doc.advance_amount + exchange_rate = ref_doc.get("exchange_rate") + if party_account_currency != ref_doc.currency: + total_amount = flt(total_amount) * flt(exchange_rate) + if party_account_currency == company_currency: + exchange_rate = 1 + + elif ref_doc.doctype == "Gratuity": + total_amount = ref_doc.amount + + if not total_amount: + if party_account_currency == company_currency: + total_amount = ref_doc.base_grand_total + exchange_rate = 1 + else: + total_amount = ref_doc.grand_total + + if not exchange_rate: + # Get the exchange rate from the original ref doc + # or get it based on the posting date of the ref doc. + exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate( + party_account_currency, company_currency, ref_doc.posting_date + ) + + return total_amount, exchange_rate diff --git a/hrms/overrides/employee_project.py b/hrms/overrides/employee_project.py new file mode 100644 index 0000000..f393494 --- /dev/null +++ b/hrms/overrides/employee_project.py @@ -0,0 +1,56 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe.query_builder.functions import Max, Min, Sum +from frappe.utils import flt + +from erpnext.projects.doctype.project.project import Project + + +class EmployeeProject(Project): + def calculate_gross_margin(self): + expense_amount = ( + flt(self.total_costing_amount) + # add expense claim amount + + flt(self.total_expense_claim) + + flt(self.total_purchase_cost) + + flt(self.get("total_consumed_material_cost", 0)) + ) + + self.gross_margin = flt(self.total_billed_amount) - expense_amount + if self.total_billed_amount: + self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) * 100 + + def update_costing(self): + ExpenseClaim = frappe.qb.DocType("Expense Claim") + self.total_expense_claim = ( + frappe.qb.from_(ExpenseClaim) + .select(Sum(ExpenseClaim.total_sanctioned_amount)) + .where((ExpenseClaim.docstatus == 1) & (ExpenseClaim.project == self.name)) + ).run()[0][0] + + TimesheetDetail = frappe.qb.DocType("Timesheet Detail") + from_time_sheet = ( + frappe.qb.from_(TimesheetDetail) + .select( + Sum(TimesheetDetail.costing_amount).as_("costing_amount"), + Sum(TimesheetDetail.billing_amount).as_("billing_amount"), + Min(TimesheetDetail.from_time).as_("start_date"), + Max(TimesheetDetail.to_time).as_("end_date"), + Sum(TimesheetDetail.hours).as_("time"), + ) + .where((TimesheetDetail.project == self.name) & (TimesheetDetail.docstatus == 1)) + ).run(as_dict=True)[0] + + self.actual_start_date = from_time_sheet.start_date + self.actual_end_date = from_time_sheet.end_date + + self.total_costing_amount = from_time_sheet.costing_amount + self.total_billable_amount = from_time_sheet.billing_amount + self.actual_time = from_time_sheet.time + + self.update_purchase_costing() + self.update_sales_amount() + self.update_billed_amount() + self.calculate_gross_margin() diff --git a/hrms/overrides/employee_timesheet.py b/hrms/overrides/employee_timesheet.py new file mode 100644 index 0000000..a2469e4 --- /dev/null +++ b/hrms/overrides/employee_timesheet.py @@ -0,0 +1,18 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from erpnext.projects.doctype.timesheet.timesheet import Timesheet + + +class EmployeeTimesheet(Timesheet): + def set_status(self): + self.status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[str(self.docstatus or 0)] + + if self.per_billed == 100: + self.status = "Billed" + + if self.salary_slip: + self.status = "Payslip" + + if self.sales_invoice and self.salary_slip: + self.status = "Completed" diff --git a/hrms/patches.txt b/hrms/patches.txt new file mode 100644 index 0000000..8fbb211 --- /dev/null +++ b/hrms/patches.txt @@ -0,0 +1,5 @@ +[pre_model_sync] + + +[post_model_sync] +hrms.patches.v1_0.rearrange_employee_fields \ No newline at end of file diff --git a/hrms/patches/post_install/add_expense_claim_default_account.py b/hrms/patches/post_install/add_expense_claim_default_account.py new file mode 100644 index 0000000..b82eb21 --- /dev/null +++ b/hrms/patches/post_install/add_expense_claim_default_account.py @@ -0,0 +1,16 @@ +import frappe + + +def execute(): + frappe.reload_doc("setup", "doctype", "company") + + companies = frappe.get_all("Company", fields=["name", "default_payable_account"]) + + for company in companies: + if not company.default_expense_claim_payable_account and company.default_payable_account: + frappe.db.set_value( + "Company", + company.name, + "default_expense_claim_payable_account", + company.default_payable_account, + ) diff --git a/hrms/patches/post_install/create_country_fixtures.py b/hrms/patches/post_install/create_country_fixtures.py new file mode 100644 index 0000000..9048d66 --- /dev/null +++ b/hrms/patches/post_install/create_country_fixtures.py @@ -0,0 +1,9 @@ +import frappe + +from hrms.overrides.company import make_salary_components, run_regional_setup + + +def execute(): + for country in frappe.get_all("Company", pluck="country", distinct=True): + run_regional_setup(country) + make_salary_components(country) diff --git a/hrms/patches/post_install/delete_employee_transfer_property_doctype.py b/hrms/patches/post_install/delete_employee_transfer_property_doctype.py new file mode 100644 index 0000000..b50e010 --- /dev/null +++ b/hrms/patches/post_install/delete_employee_transfer_property_doctype.py @@ -0,0 +1,5 @@ +import frappe + + +def execute(): + frappe.delete_doc("DocType", "Employee Transfer Property", ignore_missing=True) diff --git a/hrms/patches/post_install/drop_column_max_days_allowed.py b/hrms/patches/post_install/drop_column_max_days_allowed.py new file mode 100644 index 0000000..bfc8163 --- /dev/null +++ b/hrms/patches/post_install/drop_column_max_days_allowed.py @@ -0,0 +1,7 @@ +import frappe + + +def execute(): + if frappe.db.exists("DocType", "Leave Type"): + if frappe.db.has_column("Leave Type", "max_days_allowed"): + frappe.db.sql("alter table `tabLeave Type` drop column max_days_allowed") diff --git a/hrms/patches/post_install/generate_leave_ledger_entries.py b/hrms/patches/post_install/generate_leave_ledger_entries.py new file mode 100644 index 0000000..54c6df6 --- /dev/null +++ b/hrms/patches/post_install/generate_leave_ledger_entries.py @@ -0,0 +1,107 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe.utils import getdate, today + + +def execute(): + """Generates leave ledger entries for leave allocation/application/encashment + for last allocation""" + frappe.reload_doc("HR", "doctype", "Leave Ledger Entry") + frappe.reload_doc("HR", "doctype", "Leave Encashment") + frappe.reload_doc("HR", "doctype", "Leave Type") + + # no need to create leave ledger entries if they already exist + # they were introduced in v12 and this patch was added to create entries for existing data + # ref: https://github.com/frappe/erpnext/pull/17624 + if frappe.db.a_row_exists("Leave Ledger Entry"): + return + + if not frappe.get_meta("Leave Allocation").has_field("unused_leaves"): + frappe.reload_doc("HR", "doctype", "Leave Allocation") + update_leave_allocation_fieldname() + + generate_allocation_ledger_entries() + generate_application_leave_ledger_entries() + generate_encashment_leave_ledger_entries() + generate_expiry_allocation_ledger_entries() + + +def update_leave_allocation_fieldname(): + """maps data from old field to the new field""" + if frappe.db.has_column("Leave Allocation", "carry_forwarded_leaves"): + frappe.db.sql( + """ + UPDATE `tabLeave Allocation` + SET `unused_leaves` = `carry_forwarded_leaves` + """ + ) + + +def generate_allocation_ledger_entries(): + """fix ledger entries for missing leave allocation transaction""" + allocation_list = get_allocation_records() + + for allocation in allocation_list: + if not frappe.db.exists( + "Leave Ledger Entry", + {"transaction_type": "Leave Allocation", "transaction_name": allocation.name}, + ): + allocation_obj = frappe.get_doc("Leave Allocation", allocation) + allocation_obj.create_leave_ledger_entry() + + +def generate_application_leave_ledger_entries(): + """fix ledger entries for missing leave application transaction""" + leave_applications = get_leaves_application_records() + + for application in leave_applications: + if not frappe.db.exists( + "Leave Ledger Entry", + {"transaction_type": "Leave Application", "transaction_name": application.name}, + ): + frappe.get_doc("Leave Application", application.name).create_leave_ledger_entry() + + +def generate_encashment_leave_ledger_entries(): + """fix ledger entries for missing leave encashment transaction""" + leave_encashments = get_leave_encashment_records() + + for encashment in leave_encashments: + if not frappe.db.exists( + "Leave Ledger Entry", + {"transaction_type": "Leave Encashment", "transaction_name": encashment.name}, + ): + frappe.get_doc("Leave Encashment", encashment).create_leave_ledger_entry() + + +def generate_expiry_allocation_ledger_entries(): + """fix ledger entries for missing leave allocation transaction""" + from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation + + allocation_list = get_allocation_records() + + for allocation in allocation_list: + if not frappe.db.exists( + "Leave Ledger Entry", + {"transaction_type": "Leave Allocation", "transaction_name": allocation.name, "is_expired": 1}, + ): + allocation_obj = frappe.get_doc("Leave Allocation", allocation) + if allocation_obj.to_date <= getdate(today()): + expire_allocation(allocation_obj) + + +def get_allocation_records(): + return frappe.get_all( + "Leave Allocation", filters={"docstatus": 1}, fields=["name"], order_by="to_date ASC" + ) + + +def get_leaves_application_records(): + return frappe.get_all("Leave Application", filters={"docstatus": 1}, fields=["name"]) + + +def get_leave_encashment_records(): + return frappe.get_all("Leave Encashment", filters={"docstatus": 1}, fields=["name"]) diff --git a/hrms/patches/post_install/migrate_daily_work_summary_settings_to_daily_work_summary_group.py b/hrms/patches/post_install/migrate_daily_work_summary_settings_to_daily_work_summary_group.py new file mode 100644 index 0000000..aa8632f --- /dev/null +++ b/hrms/patches/post_install/migrate_daily_work_summary_settings_to_daily_work_summary_group.py @@ -0,0 +1,58 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + if not frappe.db.table_exists("Daily Work Summary Group"): + frappe.reload_doc("hr", "doctype", "daily_work_summary_group") + frappe.reload_doc("hr", "doctype", "daily_work_summary_group_user") + + # check if Daily Work Summary Settings Company table exists + try: + frappe.db.sql("DESC `tabDaily Work Summary Settings Company`") + except Exception: + return + + # get the previously saved settings + previous_setting = get_previous_setting() + if previous_setting["companies"]: + for d in previous_setting["companies"]: + users = frappe.get_list( + "Employee", dict(company=d.company, user_id=("!=", " ")), "user_id as user" + ) + if len(users): + # create new group entry for each company entry + new_group = frappe.get_doc( + dict( + doctype="Daily Work Summary Group", + name="Daily Work Summary for " + d.company, + users=users, + send_emails_at=d.send_emails_at, + subject=previous_setting["subject"], + message=previous_setting["message"], + ) + ) + new_group.flags.ignore_permissions = True + new_group.flags.ignore_validate = True + new_group.insert(ignore_if_duplicate=True) + + frappe.delete_doc_if_exists("DocType", "Daily Work Summary Settings") + frappe.delete_doc_if_exists("DocType", "Daily Work Summary Settings Company") + + +def get_previous_setting(): + obj = {} + setting_data = frappe.db.sql( + "select field, value from tabSingles where doctype='Daily Work Summary Settings'" + ) + for field, value in setting_data: + obj[field] = value + obj["companies"] = get_setting_companies() + return obj + + +def get_setting_companies(): + return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True) diff --git a/hrms/patches/post_install/move_doctype_reports_and_notification_from_hr_to_payroll.py b/hrms/patches/post_install/move_doctype_reports_and_notification_from_hr_to_payroll.py new file mode 100644 index 0000000..1dd2b34 --- /dev/null +++ b/hrms/patches/post_install/move_doctype_reports_and_notification_from_hr_to_payroll.py @@ -0,0 +1,50 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + if frappe.db.exists("DocType", {"module": "Payroll", "name": "Employee Incentive"}): + return + + frappe.db.sql( + """UPDATE `tabPrint Format` + SET module = 'Payroll' + WHERE name IN ('Salary Slip Based On Timesheet', 'Salary Slip Standard')""" + ) + + frappe.db.sql("""UPDATE `tabNotification` SET module='Payroll' WHERE name='Retention Bonus';""") + + doctypes_moved = [ + "Employee Benefit Application Detail", + "Employee Tax Exemption Declaration Category", + "Salary Component", + "Employee Tax Exemption Proof Submission Detail", + "Income Tax Slab Other Charges", + "Taxable Salary Slab", + "Payroll Period Date", + "Salary Slip Timesheet", + "Payroll Employee Detail", + "Salary Detail", + "Employee Tax Exemption Sub Category", + "Employee Tax Exemption Category", + "Employee Benefit Claim", + "Employee Benefit Application", + "Employee Other Income", + "Employee Tax Exemption Proof Submission", + "Employee Tax Exemption Declaration", + "Employee Incentive", + "Retention Bonus", + "Additional Salary", + "Income Tax Slab", + "Payroll Period", + "Salary Slip", + "Payroll Entry", + "Salary Structure Assignment", + "Salary Structure", + ] + + for doctype in doctypes_moved: + frappe.delete_doc_if_exists("DocType", {"name": doctype, "module": "HR"}) diff --git a/hrms/patches/post_install/move_due_advance_amount_to_pending_amount.py b/hrms/patches/post_install/move_due_advance_amount_to_pending_amount.py new file mode 100644 index 0000000..05ce525 --- /dev/null +++ b/hrms/patches/post_install/move_due_advance_amount_to_pending_amount.py @@ -0,0 +1,18 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + """Move from due_advance_amount to pending_amount""" + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql( + """ + UPDATE `tabEmployee Advance` + SET pending_amount=due_advance_amount + WHERE pending_amount IS NULL AND due_advance_amount IS NOT NULL + """ + ) diff --git a/hrms/patches/post_install/move_leave_approvers_from_employee.py b/hrms/patches/post_install/move_leave_approvers_from_employee.py new file mode 100644 index 0000000..a8e9c02 --- /dev/null +++ b/hrms/patches/post_install/move_leave_approvers_from_employee.py @@ -0,0 +1,34 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + + +def execute(): + frappe.reload_doc("hr", "doctype", "department_approver") + frappe.reload_doc("setup", "doctype", "employee") + frappe.reload_doc("setup", "doctype", "department") + + if frappe.db.has_column("Department", "leave_approver"): + rename_field("Department", "leave_approver", "leave_approvers") + + if frappe.db.has_column("Department", "expense_approver"): + rename_field("Department", "expense_approver", "expense_approvers") + + if not frappe.db.table_exists("Employee Leave Approver"): + return + + approvers = frappe.db.sql( + """select distinct app.leave_approver, emp.department from + `tabEmployee Leave Approver` app, `tabEmployee` emp + where app.parenttype = 'Employee' + and emp.name = app.parent + """, + as_dict=True, + ) + + for record in approvers: + if record.department: + department = frappe.get_doc("Department", record.department) + if not department: + return + if not len(department.leave_approvers): + department.append("leave_approvers", {"approver": record.leave_approver}).db_insert() diff --git a/hrms/patches/post_install/move_payroll_setting_separately_from_hr_settings.py b/hrms/patches/post_install/move_payroll_setting_separately_from_hr_settings.py new file mode 100644 index 0000000..37a3c35 --- /dev/null +++ b/hrms/patches/post_install/move_payroll_setting_separately_from_hr_settings.py @@ -0,0 +1,30 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + data = frappe.db.sql( + """SELECT * + FROM `tabSingles` + WHERE + doctype = "HR Settings" + AND + field in ( + "encrypt_salary_slips_in_emails", + "email_salary_slip_to_employee", + "daily_wages_fraction_for_half_day", + "disable_rounded_total", + "include_holidays_in_total_working_days", + "max_working_hours_against_timesheet", + "payroll_based_on", + "password_policy" + ) + """, + as_dict=1, + ) + + for d in data: + frappe.db.set_value("Payroll Settings", None, d.field, d.value) diff --git a/hrms/patches/post_install/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/hrms/patches/post_install/move_tax_slabs_from_payroll_period_to_income_tax_slab.py new file mode 100644 index 0000000..43dc412 --- /dev/null +++ b/hrms/patches/post_install/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -0,0 +1,146 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + if not ( + frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab") + ): + return + + if frappe.db.a_row_exists("Income Tax Slab"): + return + + for doctype in ( + "income_tax_slab", + "salary_structure_assignment", + "employee_other_income", + "income_tax_slab_other_charges", + ): + frappe.reload_doc("Payroll", "doctype", doctype) + + standard_tax_exemption_amount_exists = frappe.db.has_column( + "Payroll Period", "standard_tax_exemption_amount" + ) + + select_fields = "name, start_date, end_date" + if standard_tax_exemption_amount_exists: + select_fields = "name, start_date, end_date, standard_tax_exemption_amount" + + for company in frappe.get_all("Company"): + payroll_periods = frappe.db.sql( + """ + SELECT + {0} + FROM + `tabPayroll Period` + WHERE company=%s + ORDER BY start_date DESC + """.format( + select_fields + ), + company.name, + as_dict=1, + ) + + for i, period in enumerate(payroll_periods): + income_tax_slab = frappe.new_doc("Income Tax Slab") + income_tax_slab.name = "Tax Slab:" + period.name + + if i == 0: + income_tax_slab.disabled = 0 + else: + income_tax_slab.disabled = 1 + + income_tax_slab.effective_from = period.start_date + income_tax_slab.company = company.name + income_tax_slab.allow_tax_exemption = 1 + if standard_tax_exemption_amount_exists: + income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount + + income_tax_slab.flags.ignore_mandatory = True + income_tax_slab.submit() + + frappe.db.sql( + """ UPDATE `tabTaxable Salary Slab` + SET parent = %s , parentfield = 'slabs' , parenttype = "Income Tax Slab" + WHERE parent = %s + """, + (income_tax_slab.name, period.name), + as_dict=1, + ) + + if i == 0: + frappe.db.sql( + """ + UPDATE + `tabSalary Structure Assignment` + set + income_tax_slab = %s + where + company = %s + and from_date >= %s + and docstatus < 2 + """, + (income_tax_slab.name, company.name, period.start_date), + ) + + # move other incomes to separate document + if not frappe.db.table_exists("Employee Tax Exemption Proof Submission"): + return + + if not frappe.db.has_column( + "Employee Tax Exemption Proof Submission", "income_from_other_sources" + ): + return + + migrated = [] + proofs = frappe.get_all( + "Employee Tax Exemption Proof Submission", + filters={"docstatus": 1}, + fields=["payroll_period", "employee", "company", "income_from_other_sources"], + ) + for proof in proofs: + if proof.income_from_other_sources: + employee_other_income = frappe.new_doc("Employee Other Income") + employee_other_income.employee = proof.employee + employee_other_income.payroll_period = proof.payroll_period + employee_other_income.company = proof.company + employee_other_income.amount = proof.income_from_other_sources + + try: + employee_other_income.submit() + migrated.append([proof.employee, proof.payroll_period]) + except Exception: + pass + + if not frappe.db.table_exists("Employee Tax Exemption Declaration"): + return + + if not frappe.db.has_column("Employee Tax Exemption Declaration", "income_from_other_sources"): + return + + declerations = frappe.get_all( + "Employee Tax Exemption Declaration", + filters={"docstatus": 1}, + fields=["payroll_period", "employee", "company", "income_from_other_sources"], + ) + + for declaration in declerations: + if ( + declaration.income_from_other_sources + and [declaration.employee, declaration.payroll_period] not in migrated + ): + employee_other_income = frappe.new_doc("Employee Other Income") + employee_other_income.employee = declaration.employee + employee_other_income.payroll_period = declaration.payroll_period + employee_other_income.company = declaration.company + employee_other_income.amount = declaration.income_from_other_sources + + try: + employee_other_income.submit() + except Exception: + pass diff --git a/hrms/patches/post_install/remove_denied_leaves_from_leave_ledger.py b/hrms/patches/post_install/remove_denied_leaves_from_leave_ledger.py new file mode 100644 index 0000000..4029a3f --- /dev/null +++ b/hrms/patches/post_install/remove_denied_leaves_from_leave_ledger.py @@ -0,0 +1,35 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + """Delete leave ledger entry created + via leave applications with status != Approved""" + if not frappe.db.a_row_exists("Leave Ledger Entry"): + return + + leave_application_list = get_denied_leave_application_list() + if leave_application_list: + delete_denied_leaves_from_leave_ledger_entry(leave_application_list) + + +def get_denied_leave_application_list(): + return frappe.db.sql_list( + """ Select name from `tabLeave Application` where status <> 'Approved' """ + ) + + +def delete_denied_leaves_from_leave_ledger_entry(leave_application_list): + if leave_application_list: + frappe.db.sql( + """ Delete + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Application' + AND transaction_name in (%s) """ + % (", ".join(["%s"] * len(leave_application_list))), # nosec + tuple(leave_application_list), + ) diff --git a/hrms/patches/post_install/remove_duplicate_leave_ledger_entries.py b/hrms/patches/post_install/remove_duplicate_leave_ledger_entries.py new file mode 100644 index 0000000..8247734 --- /dev/null +++ b/hrms/patches/post_install/remove_duplicate_leave_ledger_entries.py @@ -0,0 +1,55 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + """Delete duplicate leave ledger entries of type allocation created.""" + frappe.reload_doc("hr", "doctype", "leave_ledger_entry") + if not frappe.db.a_row_exists("Leave Ledger Entry"): + return + + duplicate_records_list = get_duplicate_records() + delete_duplicate_ledger_entries(duplicate_records_list) + + +def get_duplicate_records(): + """Fetch all but one duplicate records from the list of expired leave allocation.""" + return frappe.db.sql( + """ + SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Allocation' + AND docstatus = 1 + AND is_expired = 1 + GROUP BY + employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + HAVING + count(name) > 1 + ORDER BY + creation + """ + ) + + +def delete_duplicate_ledger_entries(duplicate_records_list): + """Delete duplicate leave ledger entries.""" + if not duplicate_records_list: + return + for d in duplicate_records_list: + frappe.db.sql( + """ + DELETE FROM `tabLeave Ledger Entry` + WHERE name != %s + AND employee = %s + AND transaction_name = %s + AND leave_type = %s + AND is_carry_forward = %s + AND from_date = %s + AND to_date = %s + """, + tuple(d), + ) diff --git a/hrms/patches/post_install/rename_additional_salary_component_additional_salary.py b/hrms/patches/post_install/rename_additional_salary_component_additional_salary.py new file mode 100644 index 0000000..036ae8e --- /dev/null +++ b/hrms/patches/post_install/rename_additional_salary_component_additional_salary.py @@ -0,0 +1,11 @@ +import frappe + +# this patch should have been included with this PR https://github.com/frappe/erpnext/pull/14302 + + +def execute(): + if frappe.db.table_exists("Additional Salary Component"): + if not frappe.db.table_exists("Additional Salary"): + frappe.rename_doc("DocType", "Additional Salary Component", "Additional Salary") + + frappe.delete_doc("DocType", "Additional Salary Component") diff --git a/hrms/patches/post_install/rename_depends_on_lwp.py b/hrms/patches/post_install/rename_depends_on_lwp.py new file mode 100644 index 0000000..f7c3155 --- /dev/null +++ b/hrms/patches/post_install/rename_depends_on_lwp.py @@ -0,0 +1,14 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import scrub +from frappe.model.utils.rename_field import rename_field + + +def execute(): + for doctype in ("Salary Component", "Salary Detail"): + if frappe.db.has_column(doctype, "depends_on_lwp"): + frappe.reload_doc("Payroll", "doctype", scrub(doctype)) + rename_field(doctype, "depends_on_lwp", "depends_on_payment_days") diff --git a/hrms/patches/post_install/rename_field_max_days_allowed.py b/hrms/patches/post_install/rename_field_max_days_allowed.py new file mode 100644 index 0000000..a179178 --- /dev/null +++ b/hrms/patches/post_install/rename_field_max_days_allowed.py @@ -0,0 +1,18 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + + +def execute(): + if not frappe.db.has_column("Leave Type", "max_days_allowed"): + return + + frappe.db.sql( + """ + UPDATE `tabLeave Type` + SET max_days_allowed = '0' + WHERE trim(coalesce(max_days_allowed, '')) = '' + """ + ) + frappe.db.sql_ddl("""ALTER table `tabLeave Type` modify max_days_allowed int(8) NOT NULL""") + frappe.reload_doc("hr", "doctype", "leave_type") + rename_field("Leave Type", "max_days_allowed", "max_continuous_days_allowed") diff --git a/hrms/patches/post_install/rename_offer_letter_to_job_offer.py b/hrms/patches/post_install/rename_offer_letter_to_job_offer.py new file mode 100644 index 0000000..b92250c --- /dev/null +++ b/hrms/patches/post_install/rename_offer_letter_to_job_offer.py @@ -0,0 +1,11 @@ +import frappe + + +def execute(): + if frappe.db.table_exists("Offer Letter") and not frappe.db.table_exists("Job Offer"): + frappe.rename_doc("DocType", "Offer Letter", "Job Offer", force=True) + frappe.rename_doc("DocType", "Offer Letter Term", "Job Offer Term", force=True) + frappe.reload_doc("hr", "doctype", "job_offer") + frappe.reload_doc("hr", "doctype", "job_offer_term") + + frappe.delete_doc_if_exists("Print Format", {"name": "Offer Letter", "standard": "Yes"}) diff --git a/hrms/patches/post_install/rename_stop_to_send_birthday_reminders.py b/hrms/patches/post_install/rename_stop_to_send_birthday_reminders.py new file mode 100644 index 0000000..434dbb4 --- /dev/null +++ b/hrms/patches/post_install/rename_stop_to_send_birthday_reminders.py @@ -0,0 +1,21 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + + +def execute(): + frappe.reload_doc("hr", "doctype", "hr_settings") + + try: + # Rename the field + rename_field("HR Settings", "stop_birthday_reminders", "send_birthday_reminders") + + # Reverse the value + old_value = frappe.db.get_single_value("HR Settings", "send_birthday_reminders") + + frappe.db.set_value( + "HR Settings", "HR Settings", "send_birthday_reminders", 1 if old_value == 0 else 0 + ) + + except Exception as e: + if e.args[0] != 1054: + raise diff --git a/hrms/patches/post_install/set_company_in_leave_ledger_entry.py b/hrms/patches/post_install/set_company_in_leave_ledger_entry.py new file mode 100644 index 0000000..2251994 --- /dev/null +++ b/hrms/patches/post_install/set_company_in_leave_ledger_entry.py @@ -0,0 +1,20 @@ +import frappe + + +def execute(): + frappe.reload_doc("HR", "doctype", "Leave Allocation") + frappe.reload_doc("HR", "doctype", "Leave Ledger Entry") + frappe.db.sql( + """ + UPDATE `tabLeave Ledger Entry` as lle + SET company = (select company from `tabEmployee` where employee = lle.employee) + WHERE company IS NULL + """ + ) + frappe.db.sql( + """ + UPDATE `tabLeave Allocation` as la + SET company = (select company from `tabEmployee` where employee = la.employee) + WHERE company IS NULL + """ + ) diff --git a/hrms/patches/post_install/set_department_for_doctypes.py b/hrms/patches/post_install/set_department_for_doctypes.py new file mode 100644 index 0000000..3c3bd63 --- /dev/null +++ b/hrms/patches/post_install/set_department_for_doctypes.py @@ -0,0 +1,37 @@ +import frappe + +# Set department value based on employee value + + +def execute(): + doctypes_to_update = { + "hr": [ + "Appraisal", + "Leave Allocation", + "Expense Claim", + "Salary Slip", + "Attendance", + "Training Feedback", + "Training Result Employee", + "Leave Application", + "Employee Advance", + "Training Event Employee", + "Payroll Employee Detail", + ], + "education": ["Instructor"], + "projects": ["Activity Cost", "Timesheet"], + "setup": ["Sales Person"], + } + + for module, doctypes in doctypes_to_update.items(): + for doctype in doctypes: + if frappe.db.table_exists(doctype): + frappe.reload_doc(module, "doctype", frappe.scrub(doctype)) + frappe.db.sql( + """ + update `tab%s` dt + set department=(select department from `tabEmployee` where name=dt.employee) + where coalesce(`tab%s`.`department`, '') = '' + """ + % doctype + ) diff --git a/hrms/patches/post_install/set_employee_preferred_emails.py b/hrms/patches/post_install/set_employee_preferred_emails.py new file mode 100644 index 0000000..c145956 --- /dev/null +++ b/hrms/patches/post_install/set_employee_preferred_emails.py @@ -0,0 +1,20 @@ +import frappe + + +def execute(): + employees = frappe.get_all( + "Employee", + filters={"prefered_email": ""}, + fields=["name", "prefered_contact_email", "company_email", "personal_email", "user_id"], + ) + + for employee in employees: + if not employee.prefered_contact_email: + continue + + preferred_email_field = frappe.scrub(employee.prefered_contact_email) + + preferred_email = employee.get(preferred_email_field) + frappe.db.set_value( + "Employee", employee.name, "prefered_email", preferred_email, update_modified=False + ) diff --git a/hrms/patches/post_install/set_job_offer_applicant_email.py b/hrms/patches/post_install/set_job_offer_applicant_email.py new file mode 100644 index 0000000..0e3b5c4 --- /dev/null +++ b/hrms/patches/post_install/set_job_offer_applicant_email.py @@ -0,0 +1,14 @@ +import frappe + + +def execute(): + frappe.reload_doc("hr", "doctype", "job_offer") + + frappe.db.sql( + """ + UPDATE + `tabJob Offer` AS offer + SET + applicant_email = (SELECT email_id FROM `tabJob Applicant` WHERE name = offer.job_applicant) + """ + ) diff --git a/hrms/patches/post_install/set_payroll_cost_centers.py b/hrms/patches/post_install/set_payroll_cost_centers.py new file mode 100644 index 0000000..0951e39 --- /dev/null +++ b/hrms/patches/post_install/set_payroll_cost_centers.py @@ -0,0 +1,29 @@ +import frappe + + +def execute(): + frappe.reload_doc("payroll", "doctype", "employee_cost_center") + frappe.reload_doc("payroll", "doctype", "salary_structure_assignment") + + employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"]) + + employee_cost_center = {} + for d in employees: + cost_center = d.payroll_cost_center + if not cost_center and d.department: + cost_center = frappe.get_cached_value("Department", d.department, "payroll_cost_center") + + if cost_center: + employee_cost_center.setdefault(d.name, cost_center) + + salary_structure_assignments = frappe.get_all( + "Salary Structure Assignment", filters={"docstatus": ["!=", 2]}, fields=["name", "employee"] + ) + + for d in salary_structure_assignments: + cost_center = employee_cost_center.get(d.employee) + if cost_center: + assignment = frappe.get_doc("Salary Structure Assignment", d.name) + if not assignment.get("payroll_cost_centers"): + assignment.append("payroll_cost_centers", {"cost_center": cost_center, "percentage": 100}) + assignment.save() diff --git a/hrms/patches/post_install/set_payroll_entry_status.py b/hrms/patches/post_install/set_payroll_entry_status.py new file mode 100644 index 0000000..bb55b97 --- /dev/null +++ b/hrms/patches/post_install/set_payroll_entry_status.py @@ -0,0 +1,18 @@ +import frappe +from frappe.query_builder import Case + + +def execute(): + PayrollEntry = frappe.qb.DocType("Payroll Entry") + + ( + frappe.qb.update(PayrollEntry) + .set( + "status", + Case() + .when(PayrollEntry.docstatus == 0, "Draft") + .when(PayrollEntry.docstatus == 1, "Submitted") + .else_("Cancelled"), + ) + .where((PayrollEntry.status.notin(["Queued", "Failed"]))) + ).run() diff --git a/hrms/patches/post_install/set_salary_details_submittable.py b/hrms/patches/post_install/set_salary_details_submittable.py new file mode 100644 index 0000000..e5ecce6 --- /dev/null +++ b/hrms/patches/post_install/set_salary_details_submittable.py @@ -0,0 +1,11 @@ +import frappe + + +def execute(): + frappe.db.sql( + """ + update `tabSalary Structure` ss, `tabSalary Detail` sd + set sd.docstatus=1 + where ss.name=sd.parent and ss.docstatus=1 and sd.parenttype='Salary Structure' + """ + ) diff --git a/hrms/patches/post_install/set_training_event_attendance.py b/hrms/patches/post_install/set_training_event_attendance.py new file mode 100644 index 0000000..8d2b229 --- /dev/null +++ b/hrms/patches/post_install/set_training_event_attendance.py @@ -0,0 +1,27 @@ +import frappe + + +def execute(): + frappe.reload_doc("hr", "doctype", "training_event") + frappe.reload_doc("hr", "doctype", "training_event_employee") + + # no need to run the update query as there is no old data + if not frappe.db.exists( + "Training Event Employee", {"attendance": ("in", ("Mandatory", "Optional"))} + ): + return + + frappe.db.sql( + """ + UPDATE `tabTraining Event Employee` + SET is_mandatory = 1 + WHERE attendance = 'Mandatory' + """ + ) + frappe.db.sql( + """ + UPDATE `tabTraining Event Employee` + SET attendance = 'Present' + WHERE attendance in ('Mandatory', 'Optional') + """ + ) diff --git a/hrms/patches/post_install/update_employee_advance_status.py b/hrms/patches/post_install/update_employee_advance_status.py new file mode 100644 index 0000000..fc9e05e --- /dev/null +++ b/hrms/patches/post_install/update_employee_advance_status.py @@ -0,0 +1,29 @@ +import frappe + + +def execute(): + frappe.reload_doc("hr", "doctype", "employee_advance") + + advance = frappe.qb.DocType("Employee Advance") + ( + frappe.qb.update(advance) + .set(advance.status, "Returned") + .where( + (advance.docstatus == 1) + & ((advance.return_amount) & (advance.paid_amount == advance.return_amount)) + & (advance.status == "Paid") + ) + ).run() + + ( + frappe.qb.update(advance) + .set(advance.status, "Partly Claimed and Returned") + .where( + (advance.docstatus == 1) + & ( + (advance.claimed_amount & advance.return_amount) + & (advance.paid_amount == (advance.return_amount + advance.claimed_amount)) + ) + & (advance.status == "Paid") + ) + ).run() diff --git a/hrms/patches/post_install/update_expense_claim_status_for_paid_advances.py b/hrms/patches/post_install/update_expense_claim_status_for_paid_advances.py new file mode 100644 index 0000000..2bc17ae --- /dev/null +++ b/hrms/patches/post_install/update_expense_claim_status_for_paid_advances.py @@ -0,0 +1,25 @@ +import frappe + + +def execute(): + """ + Update Expense Claim status to Paid if: + - the entire required amount is already covered via linked advances + - the claim is partially paid via advances and the rest is reimbursed + """ + + ExpenseClaim = frappe.qb.DocType("Expense Claim") + + ( + frappe.qb.update(ExpenseClaim) + .set(ExpenseClaim.status, "Paid") + .where( + ( + (ExpenseClaim.grand_total == 0) + | (ExpenseClaim.grand_total == ExpenseClaim.total_amount_reimbursed) + ) + & (ExpenseClaim.approval_status == "Approved") + & (ExpenseClaim.docstatus == 1) + & (ExpenseClaim.total_sanctioned_amount > 0) + ) + ).run() diff --git a/hrms/patches/post_install/update_reason_for_resignation_in_employee.py b/hrms/patches/post_install/update_reason_for_resignation_in_employee.py new file mode 100644 index 0000000..6ee7090 --- /dev/null +++ b/hrms/patches/post_install/update_reason_for_resignation_in_employee.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + + +import frappe + + +def execute(): + frappe.reload_doc("setup", "doctype", "employee") + + if frappe.db.has_column("Employee", "reason_for_resignation"): + frappe.db.sql( + """ UPDATE `tabEmployee` + SET reason_for_leaving = reason_for_resignation + WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null + """ + ) diff --git a/hrms/patches/post_install/update_start_end_date_for_old_shift_assignment.py b/hrms/patches/post_install/update_start_end_date_for_old_shift_assignment.py new file mode 100644 index 0000000..6d26ac5 --- /dev/null +++ b/hrms/patches/post_install/update_start_end_date_for_old_shift_assignment.py @@ -0,0 +1,15 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + frappe.reload_doc("hr", "doctype", "shift_assignment") + if frappe.db.has_column("Shift Assignment", "date"): + frappe.db.sql( + """update `tabShift Assignment` + set end_date=date, start_date=date + where date IS NOT NULL and start_date IS NULL and end_date IS NULL;""" + ) diff --git a/hrms/patches/post_install/updates_for_multi_currency_payroll.py b/hrms/patches/post_install/updates_for_multi_currency_payroll.py new file mode 100644 index 0000000..8031b26 --- /dev/null +++ b/hrms/patches/post_install/updates_for_multi_currency_payroll.py @@ -0,0 +1,139 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe import _ +from frappe.model.utils.rename_field import rename_field + + +def execute(): + + frappe.reload_doc("payroll", "doctype", "Salary Component Account") + if frappe.db.has_column("Salary Component Account", "default_account"): + rename_field("Salary Component Account", "default_account", "account") + + doctype_list = [ + {"module": "HR", "doctype": "Employee Advance"}, + {"module": "HR", "doctype": "Leave Encashment"}, + {"module": "Payroll", "doctype": "Additional Salary"}, + {"module": "Payroll", "doctype": "Employee Benefit Application"}, + {"module": "Payroll", "doctype": "Employee Benefit Claim"}, + {"module": "Payroll", "doctype": "Employee Incentive"}, + {"module": "Payroll", "doctype": "Employee Tax Exemption Declaration"}, + {"module": "Payroll", "doctype": "Employee Tax Exemption Proof Submission"}, + {"module": "Payroll", "doctype": "Income Tax Slab"}, + {"module": "Payroll", "doctype": "Payroll Entry"}, + {"module": "Payroll", "doctype": "Retention Bonus"}, + {"module": "Payroll", "doctype": "Salary Structure"}, + {"module": "Payroll", "doctype": "Salary Structure Assignment"}, + {"module": "Payroll", "doctype": "Salary Slip"}, + ] + + for item in doctype_list: + frappe.reload_doc(item["module"], "doctype", item["doctype"]) + + # update company in employee advance based on employee company + for dt in [ + "Employee Incentive", + "Leave Encashment", + "Employee Benefit Application", + "Employee Benefit Claim", + ]: + frappe.db.sql( + """ + update `tab{doctype}` + set company = (select company from tabEmployee where name=`tab{doctype}`.employee) + where company IS NULL + """.format( + doctype=dt + ) + ) + + # update exchange rate for employee advance + frappe.db.sql("update `tabEmployee Advance` set exchange_rate=1 where exchange_rate is NULL") + + # get all companies and it's currency + all_companies = frappe.db.get_all( + "Company", fields=["name", "default_currency", "default_payroll_payable_account"] + ) + for d in all_companies: + company = d.name + company_currency = d.default_currency + default_payroll_payable_account = d.default_payroll_payable_account + + if not default_payroll_payable_account: + default_payroll_payable_account = frappe.db.get_value( + "Account", + { + "account_name": _("Payroll Payable"), + "company": company, + "account_currency": company_currency, + "is_group": 0, + }, + ) + + # update currency in following doctypes based on company currency + doctypes_for_currency = [ + "Employee Advance", + "Leave Encashment", + "Employee Benefit Application", + "Employee Benefit Claim", + "Employee Incentive", + "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Income Tax Slab", + "Retention Bonus", + "Salary Structure", + ] + + for dt in doctypes_for_currency: + frappe.db.sql( + """update `tab{doctype}` set currency = %s where company=%s and currency IS NULL""".format( + doctype=dt + ), + (company_currency, company), + ) + + # update fields in payroll entry + frappe.db.sql( + """ + update `tabPayroll Entry` + set currency = %s, + exchange_rate = 1, + payroll_payable_account=%s + where company=%s + and currency IS NULL + """, + (company_currency, default_payroll_payable_account, company), + ) + + # update fields in Salary Structure Assignment + frappe.db.sql( + """ + update `tabSalary Structure Assignment` + set currency = %s, + payroll_payable_account=%s + where company=%s + and currency IS NULL + """, + (company_currency, default_payroll_payable_account, company), + ) + + # update fields in Salary Slip + frappe.db.sql( + """ + update `tabSalary Slip` + set currency = %s, + exchange_rate = 1, + base_hour_rate = hour_rate, + base_gross_pay = gross_pay, + base_total_deduction = total_deduction, + base_net_pay = net_pay, + base_rounded_total = rounded_total, + base_total_in_words = total_in_words + where company=%s + and currency IS NULL + """, + (company_currency, company), + ) diff --git a/hrms/patches/v1_0/rearrange_employee_fields.py b/hrms/patches/v1_0/rearrange_employee_fields.py new file mode 100644 index 0000000..8814463 --- /dev/null +++ b/hrms/patches/v1_0/rearrange_employee_fields.py @@ -0,0 +1,153 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + + +def execute(): + custom_fields = { + "Employee": [ + { + "fieldname": "employment_type", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Employment Type", + "oldfieldname": "employment_type", + "oldfieldtype": "Link", + "options": "Employment Type", + "insert_after": "department", + }, + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "label": "Job Applicant", + "options": "Job Applicant", + "insert_after": "employment_details", + }, + { + "fieldname": "grade", + "fieldtype": "Link", + "label": "Grade", + "options": "Employee Grade", + "insert_after": "branch", + }, + { + "fieldname": "default_shift", + "fieldtype": "Link", + "label": "Default Shift", + "options": "Shift Type", + "insert_after": "holiday_list", + }, + { + "collapsible": 1, + "fieldname": "health_insurance_section", + "fieldtype": "Section Break", + "label": "Health Insurance", + "insert_after": "health_details", + }, + { + "fieldname": "health_insurance_provider", + "fieldtype": "Link", + "label": "Health Insurance Provider", + "options": "Employee Health Insurance", + "insert_after": "health_insurance_section", + }, + { + "depends_on": "eval:doc.health_insurance_provider", + "fieldname": "health_insurance_no", + "fieldtype": "Data", + "label": "Health Insurance No", + "insert_after": "health_insurance_provider", + }, + { + "fieldname": "approvers_section", + "fieldtype": "Section Break", + "label": "Approvers", + "insert_after": "default_shift", + }, + { + "fieldname": "expense_approver", + "fieldtype": "Link", + "label": "Expense Approver", + "options": "User", + "insert_after": "approvers_section", + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User", + "insert_after": "expense_approver", + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break", + "insert_after": "leave_approver", + }, + { + "fieldname": "shift_request_approver", + "fieldtype": "Link", + "label": "Shift Request Approver", + "options": "User", + "insert_after": "column_break_45", + }, + { + "fieldname": "salary_cb", + "fieldtype": "Column Break", + "insert_after": "salary_mode", + }, + { + "fetch_from": "department.payroll_cost_center", + "fetch_if_empty": 1, + "fieldname": "payroll_cost_center", + "fieldtype": "Link", + "label": "Payroll Cost Center", + "options": "Cost Center", + "insert_after": "salary_cb", + }, + ], + } + + if frappe.db.exists("Company", {"country": "India"}): + custom_fields["Employee"].extend( + [ + { + "fieldname": "bank_cb", + "fieldtype": "Column Break", + "insert_after": "bank_ac_no", + }, + { + "fieldname": "ifsc_code", + "label": "IFSC Code", + "fieldtype": "Data", + "insert_after": "bank_cb", + "print_hide": 1, + "depends_on": 'eval:doc.salary_mode == "Bank"', + "translatable": 0, + }, + { + "fieldname": "pan_number", + "label": "PAN Number", + "fieldtype": "Data", + "insert_after": "payroll_cost_center", + "print_hide": 1, + "translatable": 0, + }, + { + "fieldname": "micr_code", + "label": "MICR Code", + "fieldtype": "Data", + "insert_after": "ifsc_code", + "print_hide": 1, + "depends_on": 'eval:doc.salary_mode == "Bank"', + "translatable": 0, + }, + { + "fieldname": "provident_fund_account", + "label": "Provident Fund Account", + "fieldtype": "Data", + "insert_after": "pan_number", + "translatable": 0, + }, + ] + ) + + create_custom_fields(custom_fields) diff --git a/hrms/payroll/__init__.py b/hrms/payroll/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json b/hrms/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json new file mode 100644 index 0000000..61ae86f --- /dev/null +++ b/hrms/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json @@ -0,0 +1,30 @@ +{ + "aggregate_function_based_on": "rounded_total", + "chart_name": "Department Wise Salary(Last Month)", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:34.511940", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Salary Slip", + "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false],[\"Salary Slip\",\"start_date\",\"Timespan\",\"last month\",false]]", + "group_by_based_on": "department", + "group_by_type": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-22 12:46:05.272076", + "modified": "2020-07-22 12:48:12.080992", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Department Wise Salary(Last Month)", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json b/hrms/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json new file mode 100644 index 0000000..b3c4e59 --- /dev/null +++ b/hrms/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json @@ -0,0 +1,30 @@ +{ + "aggregate_function_based_on": "rounded_total", + "chart_name": "Designation Wise Salary(Last Month)", + "chart_type": "Group By", + "creation": "2020-07-22 11:56:34.550339", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Salary Slip", + "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false],[\"Salary Slip\",\"start_date\",\"Timespan\",\"last month\",false]]", + "group_by_based_on": "designation", + "group_by_type": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-22 12:22:18.412822", + "modified": "2020-07-22 12:39:07.923382", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Designation Wise Salary(Last Month)", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json b/hrms/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json new file mode 100644 index 0000000..c77c8a5 --- /dev/null +++ b/hrms/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json @@ -0,0 +1,29 @@ +{ + "based_on": "end_date", + "chart_name": "Outgoing Salary", + "chart_type": "Sum", + "creation": "2020-07-22 11:56:34.478848", + "custom_options": "", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Salary Slip", + "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false]]", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-22 12:11:27.481231", + "modified": "2020-07-22 12:20:05.777715", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Outgoing Salary", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "rounded_total", + "y_axis": [] +} \ No newline at end of file diff --git a/hrms/payroll/data/salary_components.json b/hrms/payroll/data/salary_components.json new file mode 100644 index 0000000..f0686a0 --- /dev/null +++ b/hrms/payroll/data/salary_components.json @@ -0,0 +1,27 @@ +[ + { + "doctype": "Salary Component", + "salary_component": "Income Tax", + "description": "Income Tax", + "type": "Deduction", + "is_income_tax_component": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "Basic", + "description": "Basic", + "type": "Earning" + }, + { + "doctype": "Salary Component", + "salary_component": "Arrear", + "description": "Arrear", + "type": "Earning" + }, + { + "doctype": "Salary Component", + "salary_component": "Leave Encashment", + "description": "Leave Encashment", + "type": "Earning" + } +] \ No newline at end of file diff --git a/hrms/payroll/doctype/__init__.py b/hrms/payroll/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/additional_salary/__init__.py b/hrms/payroll/doctype/additional_salary/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/additional_salary/additional_salary.js b/hrms/payroll/doctype/additional_salary/additional_salary.js new file mode 100644 index 0000000..74222d4 --- /dev/null +++ b/hrms/payroll/doctype/additional_salary/additional_salary.js @@ -0,0 +1,84 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Additional Salary', { + setup: function(frm) { + frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date"); + + frm.set_query("employee", function() { + return { + filters: { + company: frm.doc.company + } + }; + }); + }, + + onload: function(frm) { + if (frm.doc.type) { + frm.trigger('set_component_query'); + } + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('set_company') + ]); + } else { + frm.set_value("company", null); + } + }, + + set_company: function(frm) { + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Employee", + fieldname: "company", + filters: { + name: frm.doc.employee + } + }, + callback: function(data) { + if (data.message) { + frm.set_value("company", data.message.company); + } + } + }); + }, + + company: function(frm) { + frm.set_value("type", ""); + frm.trigger('set_component_query'); + }, + + set_component_query: function(frm) { + if (!frm.doc.company) return; + let filters = {company: frm.doc.company}; + if (frm.doc.type) { + filters.type = frm.doc.type; + } + frm.set_query("salary_component", function() { + return { + filters: filters + }; + }); + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, +}); diff --git a/hrms/payroll/doctype/additional_salary/additional_salary.json b/hrms/payroll/doctype/additional_salary/additional_salary.json new file mode 100644 index 0000000..9c897a7 --- /dev/null +++ b/hrms/payroll/doctype/additional_salary/additional_salary.json @@ -0,0 +1,249 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2018-05-10 12:04:08.396461", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee_details_section", + "naming_series", + "employee", + "employee_name", + "column_break_5", + "company", + "department", + "salary_details_section", + "salary_component", + "type", + "currency", + "amount", + "column_break_13", + "is_recurring", + "payroll_date", + "from_date", + "to_date", + "properties_and_references_section", + "deduct_full_tax_on_selected_payroll_date", + "ref_doctype", + "ref_docname", + "column_break_22", + "overwrite_salary_structure_amount", + "amended_from" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HR-ADS-.YY.-.MM.-", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Salary Component", + "options": "Salary Component", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "overwrite_salary_structure_amount", + "fieldtype": "Check", + "label": "Overwrite Salary Structure Amount" + }, + { + "default": "0", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:(doc.is_recurring==0)", + "description": "The date on which Salary Component with Amount will contribute for Earnings/Deduction in Salary Slip. ", + "fieldname": "payroll_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Payroll Date", + "mandatory_depends_on": "eval:(doc.is_recurring==0)", + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fetch_from": "salary_component.type", + "fieldname": "type", + "fieldtype": "Data", + "label": "Salary Component Type", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Additional Salary", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_recurring", + "fieldtype": "Check", + "label": "Is Recurring" + }, + { + "depends_on": "eval:(doc.is_recurring==1)", + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory_depends_on": "eval:(doc.is_recurring==1)" + }, + { + "depends_on": "eval:(doc.is_recurring==1)", + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory_depends_on": "eval:(doc.is_recurring==1)" + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Dynamic Link", + "label": "Reference Document", + "no_copy": 1, + "options": "ref_doctype", + "read_only": 1 + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "employee_details_section", + "fieldtype": "Section Break", + "label": "Employee Details" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "fieldname": "salary_details_section", + "fieldtype": "Section Break", + "label": "Salary Details" + }, + { + "fieldname": "properties_and_references_section", + "fieldtype": "Section Break", + "label": "Properties and References" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:56:51.765353", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Additional Salary", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/additional_salary/additional_salary.py b/hrms/payroll/doctype/additional_salary/additional_salary.py new file mode 100644 index 0000000..a4ffd09 --- /dev/null +++ b/hrms/payroll/doctype/additional_salary/additional_salary.py @@ -0,0 +1,213 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _, bold +from frappe.model.document import Document +from frappe.utils import comma_and, date_diff, formatdate, getdate + +from hrms.hr.utils import validate_active_employee + + +class AdditionalSalary(Document): + def on_submit(self): + self.update_return_amount_in_employee_advance() + self.update_employee_referral() + + def on_cancel(self): + self.update_return_amount_in_employee_advance() + self.update_employee_referral(cancel=True) + + def validate(self): + validate_active_employee(self.employee) + self.validate_dates() + self.validate_salary_structure() + self.validate_recurring_additional_salary_overlap() + self.validate_employee_referral() + + if self.amount < 0: + frappe.throw(_("Amount should not be less than zero")) + + def validate_salary_structure(self): + if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): + frappe.throw( + _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( + self.employee + ) + ) + + def validate_recurring_additional_salary_overlap(self): + if self.is_recurring: + additional_salaries = frappe.db.sql( + """ + SELECT + name + FROM `tabAdditional Salary` + WHERE + employee=%s + AND name <> %s + AND docstatus=1 + AND is_recurring=1 + AND salary_component = %s + AND to_date >= %s + AND from_date <= %s""", + (self.employee, self.name, self.salary_component, self.from_date, self.to_date), + as_dict=1, + ) + + additional_salaries = [salary.name for salary in additional_salaries] + + if additional_salaries and len(additional_salaries): + frappe.throw( + _( + "Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}" + ).format( + bold(comma_and(additional_salaries)), + bold(self.salary_component), + bold(formatdate(self.from_date)), + bold(formatdate(self.to_date)), + ) + ) + + def validate_dates(self): + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) + + if getdate(self.from_date) > getdate(self.to_date): + frappe.throw(_("From Date can not be greater than To Date.")) + + if date_of_joining: + if self.payroll_date and getdate(self.payroll_date) < getdate(date_of_joining): + frappe.throw(_("Payroll date can not be less than employee's joining date.")) + elif self.from_date and getdate(self.from_date) < getdate(date_of_joining): + frappe.throw(_("From date can not be less than employee's joining date.")) + + if relieving_date: + if self.to_date and getdate(self.to_date) > getdate(relieving_date): + frappe.throw(_("To date can not be greater than employee's relieving date.")) + if self.payroll_date and getdate(self.payroll_date) > getdate(relieving_date): + frappe.throw(_("Payroll date can not be greater than employee's relieving date.")) + + def validate_employee_referral(self): + if self.ref_doctype == "Employee Referral": + referral_details = frappe.db.get_value( + "Employee Referral", + self.ref_docname, + ["is_applicable_for_referral_bonus", "status"], + as_dict=1, + ) + + if not referral_details.is_applicable_for_referral_bonus: + frappe.throw( + _("Employee Referral {0} is not applicable for referral bonus.").format(self.ref_docname) + ) + + if self.type == "Deduction": + frappe.throw(_("Earning Salary Component is required for Employee Referral Bonus.")) + + if referral_details.status != "Accepted": + frappe.throw( + _( + "Additional Salary for referral bonus can only be created against Employee Referral with status {0}" + ).format(frappe.bold("Accepted")) + ) + + def update_return_amount_in_employee_advance(self): + if self.ref_doctype == "Employee Advance" and self.ref_docname: + return_amount = frappe.db.get_value("Employee Advance", self.ref_docname, "return_amount") + + if self.docstatus == 2: + return_amount -= self.amount + else: + return_amount += self.amount + + frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount) + advance = frappe.get_doc("Employee Advance", self.ref_docname) + advance.set_status(update=True) + + def update_employee_referral(self, cancel=False): + if self.ref_doctype == "Employee Referral": + status = "Unpaid" if cancel else "Paid" + frappe.db.set_value("Employee Referral", self.ref_docname, "referral_payment_status", status) + + def get_amount(self, sal_start_date, sal_end_date): + start_date = getdate(sal_start_date) + end_date = getdate(sal_end_date) + total_days = date_diff(getdate(self.to_date), getdate(self.from_date)) + 1 + amount_per_day = self.amount / total_days + if getdate(sal_start_date) <= getdate(self.from_date): + start_date = getdate(self.from_date) + if getdate(sal_end_date) > getdate(self.to_date): + end_date = getdate(self.to_date) + no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1 + return amount_per_day * no_of_days + + +@frappe.whitelist() +def get_additional_salaries(employee, start_date, end_date, component_type): + from frappe.query_builder import Criterion + + comp_type = "Earning" if component_type == "earnings" else "Deduction" + + additional_sal = frappe.qb.DocType("Additional Salary") + component_field = additional_sal.salary_component.as_("component") + overwrite_field = additional_sal.overwrite_salary_structure_amount.as_("overwrite") + + additional_salary_list = ( + frappe.qb.from_(additional_sal) + .select( + additional_sal.name, + component_field, + additional_sal.type, + additional_sal.amount, + additional_sal.is_recurring, + overwrite_field, + additional_sal.deduct_full_tax_on_selected_payroll_date, + ) + .where( + (additional_sal.employee == employee) + & (additional_sal.docstatus == 1) + & (additional_sal.type == comp_type) + ) + .where( + Criterion.any( + [ + Criterion.all( + [ # is recurring and additional salary dates fall within the payroll period + additional_sal.is_recurring == 1, + additional_sal.from_date <= end_date, + additional_sal.to_date >= end_date, + ] + ), + Criterion.all( + [ # is not recurring and additional salary's payroll date falls within the payroll period + additional_sal.is_recurring == 0, + additional_sal.payroll_date[start_date:end_date], + ] + ), + ] + ) + ) + .run(as_dict=True) + ) + + additional_salaries = [] + components_to_overwrite = [] + + for d in additional_salary_list: + if d.overwrite: + if d.component in components_to_overwrite: + frappe.throw( + _( + "Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}." + ).format(frappe.bold(d.component), start_date, end_date), + title=_("Error"), + ) + + components_to_overwrite.append(d.component) + + additional_salaries.append(d) + + return additional_salaries diff --git a/hrms/payroll/doctype/additional_salary/test_additional_salary.py b/hrms/payroll/doctype/additional_salary/test_additional_salary.py new file mode 100644 index 0000000..8f21a6f --- /dev/null +++ b/hrms/payroll/doctype/additional_salary/test_additional_salary.py @@ -0,0 +1,104 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, add_months, nowdate + +import erpnext +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.payroll.doctype.salary_component.test_salary_component import create_salary_component +from hrms.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + +class TestAdditionalSalary(FrappeTestCase): + def setUp(self): + setup_test() + + def test_recurring_additional_salary(self): + amount = 0 + salary_component = None + emp_id = make_employee("test_additional@salary.com") + frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800)) + salary_structure = make_salary_structure( + "Test Salary Structure Additional Salary", "Monthly", employee=emp_id + ) + add_sal = get_additional_salary(emp_id) + + ss = make_employee_salary_slip( + "test_additional@salary.com", "Monthly", salary_structure=salary_structure.name + ) + for earning in ss.earnings: + if earning.salary_component == "Recurring Salary Component": + amount = earning.amount + salary_component = earning.salary_component + break + + self.assertEqual(amount, add_sal.amount) + self.assertEqual(salary_component, add_sal.salary_component) + + def test_non_recurring_additional_salary(self): + amount = 0 + salary_component = None + date = nowdate() + + emp_id = make_employee("test_additional@salary.com") + frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(date, 1800)) + salary_structure = make_salary_structure( + "Test Salary Structure Additional Salary", "Monthly", employee=emp_id + ) + add_sal = get_additional_salary(emp_id, recurring=False, payroll_date=date) + + ss = make_employee_salary_slip( + "test_additional@salary.com", "Monthly", salary_structure=salary_structure.name + ) + + amount, salary_component = None, None + for earning in ss.earnings: + if earning.salary_component == "Recurring Salary Component": + amount = earning.amount + salary_component = earning.salary_component + break + + self.assertEqual(amount, add_sal.amount) + self.assertEqual(salary_component, add_sal.salary_component) + + # should not show up in next months + ss.posting_date = add_months(date, 1) + ss.start_date = ss.end_date = None + ss.earnings = [] + ss.deductions = [] + ss.save() + + amount, salary_component = None, None + for earning in ss.earnings: + if earning.salary_component == "Recurring Salary Component": + amount = earning.amount + salary_component = earning.salary_component + break + + self.assertIsNone(amount) + self.assertIsNone(salary_component) + + +def get_additional_salary(emp_id, recurring=True, payroll_date=None): + create_salary_component("Recurring Salary Component") + add_sal = frappe.new_doc("Additional Salary") + add_sal.employee = emp_id + add_sal.salary_component = "Recurring Salary Component" + + add_sal.is_recurring = 1 if recurring else 0 + add_sal.from_date = add_days(nowdate(), -50) + add_sal.to_date = add_days(nowdate(), 180) + add_sal.payroll_date = payroll_date + + add_sal.amount = 5000 + add_sal.currency = erpnext.get_default_currency() + add_sal.save() + add_sal.submit() + + return add_sal diff --git a/hrms/payroll/doctype/employee_benefit_application/__init__.py b/hrms/payroll/doctype/employee_benefit_application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.js b/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.js new file mode 100644 index 0000000..7eb3f6f --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.js @@ -0,0 +1,127 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Benefit Application', { + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('set_earning_component') + ]); + } + var method, args; + if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ + method = "hrms.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; + args = { + employee: frm.doc.employee, + on_date: frm.doc.date, + payroll_period: frm.doc.payroll_period + }; + get_max_benefits(frm, method, args); + } + else if(frm.doc.employee && frm.doc.date){ + method = "hrms.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits"; + args = { + employee: frm.doc.employee, + on_date: frm.doc.date + }; + get_max_benefits(frm, method, args); + } + }, + + date: function(frm) { + frm.trigger('set_earning_component'); + }, + + set_earning_component: function(frm) { + if(!frm.doc.employee && !frm.doc.date) return; + frm.set_query("earning_component", "employee_benefits", function() { + return { + query : "hrms.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + filters: {date: frm.doc.date, employee: frm.doc.employee} + }; + }); + }, + + get_employee_currency: function(frm) { + if (frm.doc.employee) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + } + }, + + payroll_period: function(frm) { + var method, args; + if (frm.doc.employee && frm.doc.date && frm.doc.payroll_period) { + method = "hrms.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; + args = { + employee: frm.doc.employee, + on_date: frm.doc.date, + payroll_period: frm.doc.payroll_period + }; + get_max_benefits(frm, method, args); + } + }, + max_benefits: function(frm) { + calculate_all(frm.doc); + } +}); + +var get_max_benefits=function(frm, method, args) { + frappe.call({ + method: method, + args: args, + callback: function (data) { + if (!data.exc) { + if (data.message) { + frm.set_value("max_benefits", data.message); + } else { + frm.set_value("max_benefits", 0); + } + } + frm.refresh_fields(); + } + }); +}; + +frappe.ui.form.on("Employee Benefit Application Detail",{ + amount: function(frm) { + calculate_all(frm.doc); + }, + employee_benefits_remove: function(frm) { + calculate_all(frm.doc); + } +}); + +var calculate_all = function(doc) { + var tbl = doc.employee_benefits || []; + var pro_rata_dispensed_amount = 0; + var total_amount = 0; + if (doc.max_benefits === 0) { + doc.employee_benefits = []; + } else { + for (var i = 0; i < tbl.length; i++) { + if (cint(tbl[i].amount) > 0) { + total_amount += flt(tbl[i].amount); + } + if (tbl[i].pay_against_benefit_claim != 1) { + pro_rata_dispensed_amount += flt(tbl[i].amount); + } + } + } + + doc.total_amount = total_amount; + doc.remaining_benefit = doc.max_benefits - total_amount; + doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount; + refresh_many(['pro_rata_dispensed_amount', 'total_amount','remaining_benefit']); +}; diff --git a/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.json new file mode 100644 index 0000000..2e4b64e --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -0,0 +1,222 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-BEN-APP-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:31:39.190787", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "currency", + "max_benefits", + "remaining_benefit", + "column_break_2", + "date", + "payroll_period", + "department", + "company", + "amended_from", + "section_break_4", + "employee_benefits", + "totals", + "total_amount", + "column_break", + "pro_rata_dispensed_amount" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "max_benefits", + "fieldtype": "Currency", + "label": "Max Benefits (Yearly)", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "remaining_benefit", + "fieldtype": "Currency", + "label": "Remaining Benefits (Yearly)", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Benefit Application", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Benefits Applied" + }, + { + "fieldname": "employee_benefits", + "fieldtype": "Table", + "label": "Employee Benefits", + "options": "Employee Benefit Application Detail", + "reqd": 1 + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "label": "Totals" + }, + { + "fieldname": "total_amount", + "fieldtype": "Currency", + "label": "Total Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "pro_rata_dispensed_amount", + "fieldtype": "Currency", + "label": "Dispensed Amount (Pro-rated)", + "options": "currency", + "read_only": 1 + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "column_break", + "fieldtype": "Column Break" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:58:31.664468", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Application", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.py new file mode 100644 index 0000000..399a902 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -0,0 +1,371 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import add_days, cstr, date_diff, flt, getdate, rounded + +from hrms.hr.utils import ( + get_holiday_dates_for_employee, + get_previous_claimed_amount, + get_sal_slip_total_benefit_given, + validate_active_employee, +) +from hrms.payroll.doctype.payroll_period.payroll_period import ( + get_payroll_period_days, + get_period_factor, +) +from hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( + get_assigned_salary_structure, +) + + +class EmployeeBenefitApplication(Document): + def validate(self): + validate_active_employee(self.employee) + self.validate_duplicate_on_payroll_period() + if not self.max_benefits: + self.max_benefits = flt( + get_max_benefits_remaining(self.employee, self.date, self.payroll_period), + self.precision("max_benefits"), + ) + if self.max_benefits and self.max_benefits > 0: + self.validate_max_benefit_for_component() + self.validate_prev_benefit_claim() + if self.remaining_benefit and self.remaining_benefit > 0: + self.validate_remaining_benefit_amount() + else: + frappe.throw( + _("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee) + ) + + def validate_prev_benefit_claim(self): + if self.employee_benefits: + for benefit in self.employee_benefits: + if benefit.pay_against_benefit_claim == 1: + payroll_period = frappe.get_doc("Payroll Period", self.payroll_period) + benefit_claimed = get_previous_claimed_amount( + self.employee, payroll_period, component=benefit.earning_component + ) + benefit_given = get_sal_slip_total_benefit_given( + self.employee, payroll_period, component=benefit.earning_component + ) + benefit_claim_remining = benefit_claimed - benefit_given + if benefit_claimed > 0 and benefit_claim_remining > benefit.amount: + frappe.throw( + _( + "An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}" + ).format( + benefit_claimed, benefit.earning_component, benefit_claim_remining + ) + ) + + def validate_remaining_benefit_amount(self): + # check salary structure earnings have flexi component (sum of max_benefit_amount) + # without pro-rata which satisfy the remaining_benefit + # else pro-rata component for the amount + # again comes the same validation and satisfy or throw + benefit_components = [] + if self.employee_benefits: + for employee_benefit in self.employee_benefits: + benefit_components.append(employee_benefit.earning_component) + salary_struct_name = get_assigned_salary_structure(self.employee, self.date) + if salary_struct_name: + non_pro_rata_amount = 0 + pro_rata_amount = 0 + salary_structure = frappe.get_doc("Salary Structure", salary_struct_name) + if salary_structure.earnings: + for earnings in salary_structure.earnings: + if earnings.is_flexible_benefit == 1 and earnings.salary_component not in benefit_components: + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( + "Salary Component", + earnings.salary_component, + ["pay_against_benefit_claim", "max_benefit_amount"], + ) + if pay_against_benefit_claim != 1: + pro_rata_amount += max_benefit_amount + else: + non_pro_rata_amount += max_benefit_amount + + if pro_rata_amount == 0 and non_pro_rata_amount == 0: + frappe.throw( + _("Please add the remaining benefits {0} to any of the existing component").format( + self.remaining_benefit + ) + ) + elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit): + frappe.throw( + _( + "You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component" + ).format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount) + ) + elif non_pro_rata_amount == 0: + frappe.throw( + _("Please add the remaining benefits {0} to the application as pro-rata component").format( + self.remaining_benefit + ) + ) + + def validate_max_benefit_for_component(self): + if self.employee_benefits: + max_benefit_amount = 0 + for employee_benefit in self.employee_benefits: + self.validate_max_benefit(employee_benefit.earning_component) + max_benefit_amount += flt(employee_benefit.amount) + if max_benefit_amount > self.max_benefits: + frappe.throw( + _("Maximum benefit amount of employee {0} exceeds {1}").format( + self.employee, self.max_benefits + ) + ) + + def validate_max_benefit(self, earning_component_name): + max_benefit_amount = frappe.db.get_value( + "Salary Component", earning_component_name, "max_benefit_amount" + ) + benefit_amount = 0 + for employee_benefit in self.employee_benefits: + if employee_benefit.earning_component == earning_component_name: + benefit_amount += flt(employee_benefit.amount) + + prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given( + self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name + ) + benefit_amount += prev_sal_slip_flexi_amount + if rounded(benefit_amount, 2) > max_benefit_amount: + frappe.throw( + _("Maximum benefit amount of component {0} exceeds {1}").format( + earning_component_name, max_benefit_amount + ) + ) + + def validate_duplicate_on_payroll_period(self): + application = frappe.db.exists( + "Employee Benefit Application", + {"employee": self.employee, "payroll_period": self.payroll_period, "docstatus": 1}, + ) + if application: + frappe.throw( + _("Employee {0} already submited an apllication {1} for the payroll period {2}").format( + self.employee, application, self.payroll_period + ) + ) + + +@frappe.whitelist() +def get_max_benefits(employee, on_date): + sal_struct = get_assigned_salary_structure(employee, on_date) + if sal_struct: + max_benefits = frappe.db.get_value("Salary Structure", sal_struct, "max_benefits") + if max_benefits > 0: + return max_benefits + return False + + +@frappe.whitelist() +def get_max_benefits_remaining(employee, on_date, payroll_period): + max_benefits = get_max_benefits(employee, on_date) + if max_benefits and max_benefits > 0: + have_depends_on_payment_days = False + per_day_amount_total = 0 + payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[1] + payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) + + # Get all salary slip flexi amount in the payroll period + prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj) + + if prev_sal_slip_flexi_total > 0: + # Check salary structure hold depends_on_payment_days component + # If yes then find the amount per day of each component and find the sum + sal_struct_name = get_assigned_salary_structure(employee, on_date) + if sal_struct_name: + sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) + for sal_struct_row in sal_struct.get("earnings"): + salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component) + if ( + salary_component.depends_on_payment_days == 1 + and salary_component.pay_against_benefit_claim != 1 + ): + have_depends_on_payment_days = True + benefit_amount = get_benefit_amount_based_on_pro_rata( + sal_struct, salary_component.max_benefit_amount + ) + amount_per_day = benefit_amount / payroll_period_days + per_day_amount_total += amount_per_day + + # Then the sum multiply with the no of lwp in that period + # Include that amount to the prev_sal_slip_flexi_total to get the actual + if have_depends_on_payment_days and per_day_amount_total > 0: + holidays = get_holiday_dates_for_employee(employee, payroll_period_obj.start_date, on_date) + working_days = date_diff(on_date, payroll_period_obj.start_date) + 1 + leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days) + leave_days_amount = leave_days * per_day_amount_total + prev_sal_slip_flexi_total += leave_days_amount + + return max_benefits - prev_sal_slip_flexi_total + return max_benefits + + +def calculate_lwp(employee, start_date, holidays, working_days): + lwp = 0 + holidays = "','".join(holidays) + + for d in range(working_days): + date = add_days(cstr(getdate(start_date)), d) + + LeaveApplication = frappe.qb.DocType("Leave Application") + LeaveType = frappe.qb.DocType("Leave Type") + + is_half_day = ( + frappe.qb.terms.Case() + .when( + ( + (LeaveApplication.half_day_date == date) + | (LeaveApplication.from_date == LeaveApplication.to_date) + ), + LeaveApplication.half_day, + ) + .else_(0) + ).as_("is_half_day") + + query = ( + frappe.qb.from_(LeaveApplication) + .inner_join(LeaveType) + .on((LeaveType.name == LeaveApplication.leave_type)) + .select(LeaveApplication.name, is_half_day) + .where( + (LeaveType.is_lwp == 1) + & (LeaveApplication.docstatus == 1) + & (LeaveApplication.status == "Approved") + & (LeaveApplication.employee == employee) + & ((LeaveApplication.from_date <= date) & (date <= LeaveApplication.to_date)) + ) + ) + + # if it's a holiday only include if leave type has "include holiday" enabled + if date in holidays: + query = query.where((LeaveType.include_holiday == "1")) + leaves = query.run(as_dict=True) + + if leaves: + lwp += 0.5 if leaves[0].is_half_day else 1 + + return lwp + + +def get_benefit_component_amount( + employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period +): + if not payroll_period: + frappe.msgprint( + _("Start and end dates not in a valid Payroll Period, cannot calculate {0}").format( + salary_component + ) + ) + return False + + # Considering there is only one application for a year + benefit_application = frappe.db.sql( + """ + select name + from `tabEmployee Benefit Application` + where + payroll_period=%(payroll_period)s + and employee=%(employee)s + and docstatus = 1 + """, + {"employee": employee, "payroll_period": payroll_period.name}, + ) + + current_benefit_amount = 0.0 + component_max_benefit, depends_on_payment_days = frappe.db.get_value( + "Salary Component", salary_component, ["max_benefit_amount", "depends_on_payment_days"] + ) + + benefit_amount = 0 + if benefit_application: + benefit_amount = frappe.db.get_value( + "Employee Benefit Application Detail", + {"parent": benefit_application[0][0], "earning_component": salary_component}, + "amount", + ) + elif component_max_benefit: + benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit) + + current_benefit_amount = 0 + if benefit_amount: + total_sub_periods = get_period_factor( + employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days + )[0] + + current_benefit_amount = benefit_amount / total_sub_periods + + return current_benefit_amount + + +def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit): + max_benefits_total = 0 + benefit_amount = 0 + for d in sal_struct.get("earnings"): + if d.is_flexible_benefit == 1: + component = frappe.db.get_value( + "Salary Component", + d.salary_component, + ["max_benefit_amount", "pay_against_benefit_claim"], + as_dict=1, + ) + if not component.pay_against_benefit_claim: + max_benefits_total += component.max_benefit_amount + + if max_benefits_total > 0: + benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total + if benefit_amount > component_max_benefit: + benefit_amount = component_max_benefit + + return benefit_amount + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_earning_components(doctype, txt, searchfield, start, page_len, filters): + if len(filters) < 2: + return {} + + salary_structure = get_assigned_salary_structure(filters["employee"], filters["date"]) + + if salary_structure: + return frappe.db.sql( + """ + select salary_component + from `tabSalary Detail` + where parent = %s and is_flexible_benefit = 1 + order by name + """, + salary_structure, + ) + else: + frappe.throw( + _("Salary Structure not found for employee {0} and date {1}").format( + filters["employee"], filters["date"] + ) + ) + + +@frappe.whitelist() +def get_earning_components_max_benefits(employee, date, earning_component): + salary_structure = get_assigned_salary_structure(employee, date) + amount = frappe.db.sql( + """ + select amount + from `tabSalary Detail` + where parent = %s and is_flexible_benefit = 1 + and salary_component = %s + order by name + """, + salary_structure, + earning_component, + ) + + return amount if amount else 0 diff --git a/hrms/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py b/hrms/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py new file mode 100644 index 0000000..988a497 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py @@ -0,0 +1,85 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, date_diff, get_year_ending, get_year_start, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.utils import get_holiday_dates_for_employee +from hrms.payroll.doctype.employee_benefit_application.employee_benefit_application import ( + calculate_lwp, +) +from hrms.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( + create_payroll_period, +) +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_holiday_list, + make_leave_application, +) +from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure +from hrms.tests.test_utils import get_first_sunday + + +class TestEmployeeBenefitApplication(FrappeTestCase): + def setUp(self): + date = getdate() + make_holiday_list(from_date=get_year_start(date), to_date=get_year_ending(date)) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_employee_benefit_application(self): + payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") + employee = make_employee("test_employee_benefits@salary.com", company="_Test Company") + first_sunday = get_first_sunday("Salary Slip Test Holiday List") + + leave_application = make_leave_application( + employee, + add_days(first_sunday, 1), + add_days(first_sunday, 3), + "Leave Without Pay", + half_day=1, + half_day_date=add_days(first_sunday, 1), + submit=True, + ) + + frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) + salary_structure = make_salary_structure( + "Test Employee Benefits", + "Monthly", + other_details={"max_benefits": 100000}, + include_flexi_benefits=True, + employee=employee, + payroll_period=payroll_period, + ) + salary_slip = make_salary_slip(salary_structure.name, employee=employee, posting_date=getdate()) + salary_slip.insert() + salary_slip.submit() + + application = make_employee_benefit_application( + employee, payroll_period.name, date=leave_application.to_date + ) + self.assertEqual(application.employee_benefits[0].max_benefit_amount, 15000) + + holidays = get_holiday_dates_for_employee(employee, payroll_period.start_date, application.date) + working_days = date_diff(application.date, payroll_period.start_date) + 1 + lwp = calculate_lwp(employee, payroll_period.start_date, holidays, working_days) + self.assertEqual(lwp, 2.5) + + +def make_employee_benefit_application(employee, payroll_period, date): + frappe.db.delete("Employee Benefit Application") + + return frappe.get_doc( + { + "doctype": "Employee Benefit Application", + "employee": employee, + "date": date, + "payroll_period": payroll_period, + "employee_benefits": [{"earning_component": "Medical Allowance", "amount": 1500}], + } + ).insert() diff --git a/hrms/payroll/doctype/employee_benefit_application_detail/__init__.py b/hrms/payroll/doctype/employee_benefit_application_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json new file mode 100644 index 0000000..c93d356 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -0,0 +1,60 @@ +{ + "actions": [], + "creation": "2018-04-13 16:36:18.389786", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "earning_component", + "pay_against_benefit_claim", + "max_benefit_amount", + "amount" + ], + "fields": [ + { + "fieldname": "earning_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Earning Component", + "options": "Salary Component", + "reqd": 1 + }, + { + "default": "0", + "fetch_from": "earning_component.pay_against_benefit_claim", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "label": "Pay Against Benefit Claim", + "read_only": 1 + }, + { + "fetch_from": "earning_component.max_benefit_amount", + "fieldname": "max_benefit_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Benefit Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-09-29 16:22:15.783854", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Application Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py b/hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py new file mode 100644 index 0000000..51aa2c9 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeBenefitApplicationDetail(Document): + pass diff --git a/hrms/payroll/doctype/employee_benefit_claim/__init__.py b/hrms/payroll/doctype/employee_benefit_claim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js b/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js new file mode 100644 index 0000000..8d3fc22 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -0,0 +1,34 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Benefit Claim', { + setup: function(frm) { + frm.set_query("earning_component", function() { + return { + query : "hrms.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + filters: {date: frm.doc.claim_date, employee: frm.doc.employee} + }; + }); + }, + employee: function(frm) { + frm.set_value("earning_component", null); + if (frm.doc.employee) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + } + } + }); + } + if (!frm.doc.earning_component) { + frm.doc.max_amount_eligible = null; + frm.doc.claimed_amount = null; + } + frm.refresh_fields(); + } +}); diff --git a/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json new file mode 100644 index 0000000..5deb0a5 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -0,0 +1,218 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:43:10.386409", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_3", + "claim_date", + "currency", + "company", + "benefit_type_and_amount", + "earning_component", + "max_amount_eligible", + "pay_against_benefit_claim", + "claimed_amount", + "salary_slip", + "amended_from", + "section_break_9", + "attachments" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "claim_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Claim Date", + "reqd": 1 + }, + { + "fieldname": "benefit_type_and_amount", + "fieldtype": "Section Break", + "label": "Benefit Type and Amount" + }, + { + "fieldname": "earning_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Claim Benefit For", + "options": "Salary Component", + "reqd": 1 + }, + { + "fetch_from": "earning_component.max_benefit_amount", + "fieldname": "max_amount_eligible", + "fieldtype": "Currency", + "label": "Max Amount Eligible", + "options": "currency", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "earning_component.pay_against_benefit_claim", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "hidden": 1, + "label": "Pay Against Benefit Claim", + "read_only": 1 + }, + { + "fieldname": "claimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Claimed Amount", + "options": "currency", + "reqd": 1 + }, + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Benefit Claim", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Expense Proof" + }, + { + "fieldname": "attachments", + "fieldtype": "Attach", + "label": "Attachments" + }, + { + "depends_on": "eval: doc.employee", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:59:15.699118", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Claim", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py new file mode 100644 index 0000000..6bff537 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -0,0 +1,241 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt + +from hrms.hr.utils import get_previous_claimed_amount, validate_active_employee +from hrms.payroll.doctype.employee_benefit_application.employee_benefit_application import ( + get_max_benefits, +) +from hrms.payroll.doctype.payroll_period.payroll_period import get_payroll_period +from hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( + get_assigned_salary_structure, +) + + +class EmployeeBenefitClaim(Document): + def validate(self): + validate_active_employee(self.employee) + max_benefits = get_max_benefits(self.employee, self.claim_date) + if not max_benefits or max_benefits <= 0: + frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) + payroll_period = get_payroll_period( + self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company") + ) + if not payroll_period: + frappe.throw( + _("{0} is not in a valid Payroll Period").format( + frappe.format(self.claim_date, dict(fieldtype="Date")) + ) + ) + self.validate_max_benefit_for_component(payroll_period) + self.validate_max_benefit_for_sal_struct(max_benefits) + self.validate_benefit_claim_amount(max_benefits, payroll_period) + if self.pay_against_benefit_claim: + self.validate_non_pro_rata_benefit_claim(max_benefits, payroll_period) + + def validate_benefit_claim_amount(self, max_benefits, payroll_period): + claimed_amount = self.claimed_amount + claimed_amount += get_previous_claimed_amount(self.employee, payroll_period) + if max_benefits < claimed_amount: + frappe.throw( + _( + "Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed amount" + ).format(self.employee, max_benefits, claimed_amount - max_benefits) + ) + + def validate_max_benefit_for_sal_struct(self, max_benefits): + if self.claimed_amount > max_benefits: + frappe.throw( + _("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits) + ) + + def validate_max_benefit_for_component(self, payroll_period): + if self.max_amount_eligible: + claimed_amount = self.claimed_amount + claimed_amount += get_previous_claimed_amount( + self.employee, payroll_period, component=self.earning_component + ) + if claimed_amount > self.max_amount_eligible: + frappe.throw( + _("Maximum amount eligible for the component {0} exceeds {1}").format( + self.earning_component, self.max_amount_eligible + ) + ) + + def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period): + claimed_amount = self.claimed_amount + pro_rata_amount = self.get_pro_rata_amount_in_application(payroll_period.name) + if not pro_rata_amount: + pro_rata_amount = 0 + # Get pro_rata_amount if there is no application, + # get salary structure for the date and calculate pro-rata amount + sal_struct_name = get_assigned_salary_structure(self.employee, self.claim_date) + if sal_struct_name: + sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) + pro_rata_amount = get_benefit_pro_rata_ratio_amount(self.employee, self.claim_date, sal_struct) + + claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata=True) + if max_benefits < pro_rata_amount + claimed_amount: + frappe.throw( + _( + "Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component amount and previous claimed amount" + ).format( + self.employee, max_benefits, pro_rata_amount + claimed_amount - max_benefits + ) + ) + + def get_pro_rata_amount_in_application(self, payroll_period): + application = frappe.db.exists( + "Employee Benefit Application", + {"employee": self.employee, "payroll_period": payroll_period, "docstatus": 1}, + ) + if application: + return frappe.db.get_value( + "Employee Benefit Application", application, "pro_rata_dispensed_amount" + ) + return False + + +def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): + total_pro_rata_max = 0 + benefit_amount_total = 0 + for sal_struct_row in sal_struct.get("earnings"): + try: + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( + "Salary Component", + sal_struct_row.salary_component, + ["pay_against_benefit_claim", "max_benefit_amount"], + ) + except TypeError: + # show the error in tests? + frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component)) + if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: + total_pro_rata_max += max_benefit_amount + if total_pro_rata_max > 0: + for sal_struct_row in sal_struct.get("earnings"): + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( + "Salary Component", + sal_struct_row.salary_component, + ["pay_against_benefit_claim", "max_benefit_amount"], + ) + + if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: + component_max = max_benefit_amount + benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max + if benefit_amount > component_max: + benefit_amount = component_max + benefit_amount_total += benefit_amount + return benefit_amount_total + + +def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None): + query = """ + select sum(claimed_amount) + from `tabEmployee Benefit Claim` + where + employee=%(employee)s + and docstatus = 1 + and pay_against_benefit_claim = 1 + and claim_date between %(start_date)s and %(end_date)s + """ + + if salary_component: + query += " and earning_component = %(earning_component)s" + + claimed_amount = flt( + frappe.db.sql( + query, + { + "employee": employee, + "start_date": start_date, + "end_date": end_date, + "earning_component": salary_component, + }, + )[0][0] + ) + + return claimed_amount + + +def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period): + pro_rata_amount = 0 + claimed_amount = 0 + application = frappe.db.exists( + "Employee Benefit Application", + {"employee": employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ) + if application: + application_obj = frappe.get_doc("Employee Benefit Application", application) + pro_rata_amount = ( + application_obj.pro_rata_dispensed_amount + + application_obj.max_benefits + - application_obj.remaining_benefit + ) + else: + pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct) + + claimed_amount += get_benefit_claim_amount( + employee, payroll_period.start_date, payroll_period.end_date + ) + + return claimed_amount + pro_rata_amount + + +def get_last_payroll_period_benefits( + employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct +): + max_benefits = get_max_benefits(employee, payroll_period.end_date) + if not max_benefits: + max_benefits = 0 + remaining_benefit = max_benefits - get_total_benefit_dispensed( + employee, sal_struct, sal_slip_start_date, payroll_period + ) + if remaining_benefit > 0: + have_remaining = True + # Set the remaining benefits to flexi non pro-rata component in the salary structure + salary_components_array = [] + for d in sal_struct.get("earnings"): + if d.is_flexible_benefit == 1: + salary_component = frappe.get_doc("Salary Component", d.salary_component) + if salary_component.pay_against_benefit_claim == 1: + claimed_amount = get_benefit_claim_amount( + employee, payroll_period.start_date, sal_slip_end_date, d.salary_component + ) + amount_fit_to_component = salary_component.max_benefit_amount - claimed_amount + if amount_fit_to_component > 0: + if remaining_benefit > amount_fit_to_component: + amount = amount_fit_to_component + remaining_benefit -= amount_fit_to_component + else: + amount = remaining_benefit + have_remaining = False + current_claimed_amount = get_benefit_claim_amount( + employee, sal_slip_start_date, sal_slip_end_date, d.salary_component + ) + amount += current_claimed_amount + struct_row = {} + salary_components_dict = {} + struct_row["depends_on_payment_days"] = salary_component.depends_on_payment_days + struct_row["salary_component"] = salary_component.name + struct_row["abbr"] = salary_component.salary_component_abbr + struct_row["do_not_include_in_total"] = salary_component.do_not_include_in_total + struct_row["is_tax_applicable"] = (salary_component.is_tax_applicable,) + struct_row["is_flexible_benefit"] = (salary_component.is_flexible_benefit,) + struct_row[ + "variable_based_on_taxable_salary" + ] = salary_component.variable_based_on_taxable_salary + salary_components_dict["amount"] = amount + salary_components_dict["struct_row"] = struct_row + salary_components_array.append(salary_components_dict) + if not have_remaining: + break + + if len(salary_components_array) > 0: + return salary_components_array + + return False diff --git a/hrms/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/hrms/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py new file mode 100644 index 0000000..b1d3c66 --- /dev/null +++ b/hrms/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeBenefitClaim(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/employee_cost_center/__init__.py b/hrms/payroll/doctype/employee_cost_center/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_cost_center/employee_cost_center.json b/hrms/payroll/doctype/employee_cost_center/employee_cost_center.json new file mode 100644 index 0000000..8fed9f7 --- /dev/null +++ b/hrms/payroll/doctype/employee_cost_center/employee_cost_center.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2021-12-23 12:44:38.389283", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center", + "percentage" + ], + "fields": [ + { + "allow_on_submit": 1, + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "percentage", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Percentage (%)", + "non_negative": 1, + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-12-23 17:39:03.410924", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Cost Center", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_cost_center/employee_cost_center.py b/hrms/payroll/doctype/employee_cost_center/employee_cost_center.py new file mode 100644 index 0000000..6c5be97 --- /dev/null +++ b/hrms/payroll/doctype/employee_cost_center/employee_cost_center.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class EmployeeCostCenter(Document): + pass diff --git a/hrms/payroll/doctype/employee_incentive/__init__.py b/hrms/payroll/doctype/employee_incentive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_incentive/employee_incentive.js b/hrms/payroll/doctype/employee_incentive/employee_incentive.js new file mode 100644 index 0000000..4056094 --- /dev/null +++ b/hrms/payroll/doctype/employee_incentive/employee_incentive.js @@ -0,0 +1,69 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Incentive', { + setup: function(frm) { + frm.set_query("employee", function() { + return { + filters: { + "status": "Active" + } + }; + }); + frm.trigger('set_earning_component'); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('set_company') + ]); + } else { + frm.set_value("company", null); + } + }, + + set_company: function(frm) { + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Employee", + fieldname: "company", + filters: { + name: frm.doc.employee + } + }, + callback: function(data) { + if (data.message) { + frm.set_value("company", data.message.company); + frm.trigger('set_earning_component'); + } + } + }); + }, + + set_earning_component: function(frm) { + if (!frm.doc.company) return; + frm.set_query("salary_component", function() { + return { + filters: {type: "earning", company: frm.doc.company} + }; + }); + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, +}); diff --git a/hrms/payroll/doctype/employee_incentive/employee_incentive.json b/hrms/payroll/doctype/employee_incentive/employee_incentive.json new file mode 100644 index 0000000..64fb8c5 --- /dev/null +++ b/hrms/payroll/doctype/employee_incentive/employee_incentive.json @@ -0,0 +1,146 @@ +{ + "actions": [], + "autoname": "HR-EINV-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:13:43.404546", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "company", + "currency", + "incentive_amount", + "column_break_5", + "salary_component", + "payroll_date", + "department", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "incentive_amount", + "fieldtype": "Currency", + "label": "Incentive Amount", + "options": "currency", + "reqd": 1 + }, + { + "fieldname": "payroll_date", + "fieldtype": "Date", + "label": "Payroll Date", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Incentive", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "salary_component", + "fieldtype": "Link", + "label": "Salary Component", + "options": "Salary Component", + "reqd": 1 + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:52:19.850710", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Incentive", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_incentive/employee_incentive.py b/hrms/payroll/doctype/employee_incentive/employee_incentive.py new file mode 100644 index 0000000..157ed1d --- /dev/null +++ b/hrms/payroll/doctype/employee_incentive/employee_incentive.py @@ -0,0 +1,38 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + +from hrms.hr.utils import validate_active_employee + + +class EmployeeIncentive(Document): + def validate(self): + validate_active_employee(self.employee) + self.validate_salary_structure() + + def validate_salary_structure(self): + if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): + frappe.throw( + _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( + self.employee + ) + ) + + def on_submit(self): + company = frappe.db.get_value("Employee", self.employee, "company") + + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.employee = self.employee + additional_salary.currency = self.currency + additional_salary.salary_component = self.salary_component + additional_salary.overwrite_salary_structure_amount = 0 + additional_salary.amount = self.incentive_amount + additional_salary.payroll_date = self.payroll_date + additional_salary.company = company + additional_salary.ref_doctype = self.doctype + additional_salary.ref_docname = self.name + additional_salary.submit() diff --git a/hrms/payroll/doctype/employee_incentive/test_employee_incentive.py b/hrms/payroll/doctype/employee_incentive/test_employee_incentive.py new file mode 100644 index 0000000..e296fdf --- /dev/null +++ b/hrms/payroll/doctype/employee_incentive/test_employee_incentive.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeIncentive(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/employee_other_income/__init__.py b/hrms/payroll/doctype/employee_other_income/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_other_income/employee_other_income.js b/hrms/payroll/doctype/employee_other_income/employee_other_income.js new file mode 100644 index 0000000..c1a74e8 --- /dev/null +++ b/hrms/payroll/doctype/employee_other_income/employee_other_income.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Other Income', { + // refresh: function(frm) { + + // } +}); diff --git a/hrms/payroll/doctype/employee_other_income/employee_other_income.json b/hrms/payroll/doctype/employee_other_income/employee_other_income.json new file mode 100644 index 0000000..04ce9f7 --- /dev/null +++ b/hrms/payroll/doctype/employee_other_income/employee_other_income.json @@ -0,0 +1,139 @@ +{ + "actions": [], + "autoname": "HR-INCOME-.######", + "creation": "2020-03-18 15:04:40.767434", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "payroll_period", + "column_break_3", + "company", + "source", + "amount", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "source", + "fieldtype": "Data", + "label": "Source" + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Other Income", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:58:43.255900", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Other Income", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_other_income/employee_other_income.py b/hrms/payroll/doctype/employee_other_income/employee_other_income.py new file mode 100644 index 0000000..51059a1 --- /dev/null +++ b/hrms/payroll/doctype/employee_other_income/employee_other_income.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeOtherIncome(Document): + pass diff --git a/hrms/payroll/doctype/employee_other_income/test_employee_other_income.py b/hrms/payroll/doctype/employee_other_income/test_employee_other_income.py new file mode 100644 index 0000000..8f0f637 --- /dev/null +++ b/hrms/payroll/doctype/employee_other_income/test_employee_other_income.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestEmployeeOtherIncome(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/employee_tax_exemption_category/__init__.py b/hrms/payroll/doctype/employee_tax_exemption_category/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js b/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js new file mode 100644 index 0000000..1df609f --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Tax Exemption Category', { + refresh: function(frm) { + + } +}); diff --git a/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json new file mode 100644 index 0000000..f2556d7 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 16:51:36.971140", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "max_amount", + "is_active" + ], + "fields": [ + { + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Exemption Amount" + }, + { + "default": "1", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + } + ], + "links": [], + "modified": "2020-06-22 23:16:47.472910", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Category", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py b/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py new file mode 100644 index 0000000..5c109de --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py @@ -0,0 +1,9 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class EmployeeTaxExemptionCategory(Document): + pass diff --git a/hrms/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py b/hrms/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py new file mode 100644 index 0000000..84e6183 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeTaxExemptionCategory(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration/__init__.py b/hrms/payroll/doctype/employee_tax_exemption_declaration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js new file mode 100644 index 0000000..68237c2 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js @@ -0,0 +1,72 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Tax Exemption Declaration', { + setup: function(frm) { + frm.set_query('employee', function() { + return { + filters: { + 'status': "Active" + } + } + }); + + frm.set_query('payroll_period', function() { + const fields = {'employee': 'Employee', 'company': 'Company'}; + + for (let [field, label] of Object.entries(fields)) { + if (!frm.doc[field]) { + frappe.msgprint(__("Please select {0}", [label])) + } + }; + + if (frm.doc.employee && frm.doc.company){ + return { + filters: { + 'company': frm.doc.company + } + } + } + }); + + frm.set_query('exemption_sub_category', 'declarations', function() { + return { + filters: { + 'is_active': 1 + } + } + }); + }, + + refresh: function(frm) { + if(frm.doc.docstatus==1) { + frm.add_custom_button(__('Submit Proof'), function() { + frappe.model.open_mapped_doc({ + method: "hrms.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + frm: frm + }); + }).addClass("btn-primary"); + } + }, + + employee: function(frm) { + if (frm.doc.employee) { + frm.trigger('get_employee_currency'); + } + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + } +}); diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json new file mode 100644 index 0000000..5ef373e --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -0,0 +1,196 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-TAX-DEC-.YYYY.-.#####", + "creation": "2018-04-13 16:53:36.175504", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_2", + "payroll_period", + "company", + "currency", + "amended_from", + "section_break_8", + "declarations", + "section_break_10", + "total_declared_amount", + "column_break_12", + "total_exemption_amount" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Tax Exemption Declaration", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "declarations", + "fieldtype": "Table", + "label": "Declarations", + "options": "Employee Tax Exemption Declaration Category" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_declared_amount", + "fieldtype": "Currency", + "label": "Total Declared Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_exemption_amount", + "fieldtype": "Currency", + "label": "Total Exemption Amount", + "options": "currency", + "read_only": 1 + }, + { + "depends_on": "eval: doc.employee", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:58:54.707871", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Declaration", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py new file mode 100644 index 0000000..303bc3c --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -0,0 +1,78 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +from frappe.utils import flt + +from hrms.hr.utils import ( + calculate_annual_eligible_hra_exemption, + get_total_exemption_amount, + validate_active_employee, + validate_duplicate_exemption_for_payroll_period, + validate_tax_declaration, +) + + +class EmployeeTaxExemptionDeclaration(Document): + def validate(self): + validate_active_employee(self.employee) + validate_tax_declaration(self.declarations) + validate_duplicate_exemption_for_payroll_period( + self.doctype, self.name, self.payroll_period, self.employee + ) + self.set_total_declared_amount() + self.set_total_exemption_amount() + self.calculate_hra_exemption() + + def set_total_declared_amount(self): + self.total_declared_amount = 0.0 + for d in self.declarations: + self.total_declared_amount += flt(d.amount) + + def set_total_exemption_amount(self): + self.total_exemption_amount = flt( + get_total_exemption_amount(self.declarations), self.precision("total_exemption_amount") + ) + + def calculate_hra_exemption(self): + self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0 + if self.get("monthly_house_rent"): + hra_exemption = calculate_annual_eligible_hra_exemption(self) + if hra_exemption: + self.total_exemption_amount += hra_exemption["annual_exemption"] + self.total_exemption_amount = flt( + self.total_exemption_amount, self.precision("total_exemption_amount") + ) + self.salary_structure_hra = flt( + hra_exemption["hra_amount"], self.precision("salary_structure_hra") + ) + self.annual_hra_exemption = flt( + hra_exemption["annual_exemption"], self.precision("annual_hra_exemption") + ) + self.monthly_hra_exemption = flt( + hra_exemption["monthly_exemption"], self.precision("monthly_hra_exemption") + ) + + +@frappe.whitelist() +def make_proof_submission(source_name, target_doc=None): + doclist = get_mapped_doc( + "Employee Tax Exemption Declaration", + source_name, + { + "Employee Tax Exemption Declaration": { + "doctype": "Employee Tax Exemption Proof Submission", + "field_no_map": ["monthly_house_rent", "monthly_hra_exemption"], + }, + "Employee Tax Exemption Declaration Category": { + "doctype": "Employee Tax Exemption Proof Submission Detail", + "add_if_empty": True, + }, + }, + target_doc, + ) + + return doclist diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py new file mode 100644 index 0000000..904fd89 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -0,0 +1,491 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_months, getdate + +import erpnext +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.utils import DuplicateDeclarationError + + +class TestEmployeeTaxExemptionDeclaration(FrappeTestCase): + def setUp(self): + make_employee("employee@taxexemption.com", company="_Test Company") + make_employee("employee1@taxexemption.com", company="_Test Company") + create_payroll_period(company="_Test Company") + create_exemption_category() + frappe.db.delete("Employee Tax Exemption Declaration") + frappe.db.delete("Salary Structure Assignment") + + def test_duplicate_category_in_declaration(self): + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=100000, + ), + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=50000, + ), + ], + } + ) + self.assertRaises(frappe.ValidationError, declaration.save) + + def test_duplicate_entry_for_payroll_period(self): + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=100000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=50000, + ), + ], + } + ).insert() + + duplicate_declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=100000, + ) + ], + } + ) + self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert) + duplicate_declaration.employee = frappe.get_value( + "Employee", {"user_id": "employee1@taxexemption.com"}, "name" + ) + self.assertTrue(duplicate_declaration.insert) + + def test_exemption_amount(self): + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=80000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + self.assertEqual(declaration.total_exemption_amount, 100000) + + def test_india_hra_exemption(self): + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + setup_hra_exemption_prerequisites("Monthly") + employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") + + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "company": "_Test Company", + "payroll_period": "_Test Payroll Period", + "currency": "INR", + "monthly_house_rent": 50000, + "rented_in_metro_city": 1, + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=80000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + # Monthly HRA received = 3000 + # should set HRA exemption as per actual annual HRA because that's the minimum + self.assertEqual(declaration.monthly_hra_exemption, 3000) + self.assertEqual(declaration.annual_hra_exemption, 36000) + # 100000 Standard Exemption + 36000 HRA exemption + self.assertEqual(declaration.total_exemption_amount, 136000) + + # reset + frappe.flags.country = current_country + + def test_india_hra_exemption_with_daily_payroll_frequency(self): + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + setup_hra_exemption_prerequisites("Daily") + employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") + + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "company": "_Test Company", + "payroll_period": "_Test Payroll Period", + "currency": "INR", + "monthly_house_rent": 170000, + "rented_in_metro_city": 1, + "declarations": [ + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + # Daily HRA received = 3000 + # should set HRA exemption as per (rent - 10% of Basic Salary), that's the minimum + self.assertEqual(declaration.monthly_hra_exemption, 17916.67) + self.assertEqual(declaration.annual_hra_exemption, 215000) + # 50000 Standard Exemption + 215000 HRA exemption + self.assertEqual(declaration.total_exemption_amount, 265000) + + # reset + frappe.flags.country = current_country + + def test_india_hra_exemption_with_weekly_payroll_frequency(self): + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + setup_hra_exemption_prerequisites("Weekly") + employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") + + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "company": "_Test Company", + "payroll_period": "_Test Payroll Period", + "currency": "INR", + "monthly_house_rent": 170000, + "rented_in_metro_city": 1, + "declarations": [ + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + # Weekly HRA received = 3000 + # should set HRA exemption as per actual annual HRA because that's the minimum + self.assertEqual(declaration.monthly_hra_exemption, 13000) + self.assertEqual(declaration.annual_hra_exemption, 156000) + # 50000 Standard Exemption + 156000 HRA exemption + self.assertEqual(declaration.total_exemption_amount, 206000) + + # reset + frappe.flags.country = current_country + + def test_india_hra_exemption_with_fortnightly_payroll_frequency(self): + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + setup_hra_exemption_prerequisites("Fortnightly") + employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") + + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "company": "_Test Company", + "payroll_period": "_Test Payroll Period", + "currency": "INR", + "monthly_house_rent": 170000, + "rented_in_metro_city": 1, + "declarations": [ + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + # Fortnightly HRA received = 3000 + # should set HRA exemption as per actual annual HRA because that's the minimum + self.assertEqual(declaration.monthly_hra_exemption, 6500) + self.assertEqual(declaration.annual_hra_exemption, 78000) + # 50000 Standard Exemption + 78000 HRA exemption + self.assertEqual(declaration.total_exemption_amount, 128000) + + # reset + frappe.flags.country = current_country + + def test_india_hra_exemption_with_bimonthly_payroll_frequency(self): + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + setup_hra_exemption_prerequisites("Bimonthly") + employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") + + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "company": "_Test Company", + "payroll_period": "_Test Payroll Period", + "currency": "INR", + "monthly_house_rent": 50000, + "rented_in_metro_city": 1, + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=80000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + # Bimonthly HRA received = 3000 + # should set HRA exemption as per actual annual HRA because that's the minimum + self.assertEqual(declaration.monthly_hra_exemption, 1500) + self.assertEqual(declaration.annual_hra_exemption, 18000) + # 100000 Standard Exemption + 18000 HRA exemption + self.assertEqual(declaration.total_exemption_amount, 118000) + + # reset + frappe.flags.country = current_country + + def test_india_hra_exemption_with_multiple_salary_structure_assignments(self): + from hrms.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab + from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + make_salary_structure, + ) + + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + employee = make_employee("employee@taxexemption2.com", company="_Test Company") + payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company") + + create_tax_slab( + payroll_period, + allow_tax_exemption=True, + currency="INR", + effective_date=getdate("2019-04-01"), + company="_Test Company", + ) + + frappe.db.set_value( + "Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"} + ) + + # salary structure with base 50000, HRA 3000 + make_salary_structure( + "Monthly Structure for HRA Exemption 1", + "Monthly", + employee=employee, + company="_Test Company", + currency="INR", + payroll_period=payroll_period.name, + from_date=payroll_period.start_date, + ) + + # salary structure with base 70000, HRA = base * 0.2 = 14000 + salary_structure = make_salary_structure( + "Monthly Structure for HRA Exemption 2", + "Monthly", + employee=employee, + company="_Test Company", + currency="INR", + payroll_period=payroll_period.name, + from_date=payroll_period.start_date, + dont_submit=True, + ) + for component_row in salary_structure.earnings: + if component_row.salary_component == "HRA": + component_row.amount = 0 + component_row.amount_based_on_formula = 1 + component_row.formula = "base * 0.2" + break + + salary_structure.submit() + + create_salary_structure_assignment( + employee, + salary_structure.name, + from_date=add_months(payroll_period.start_date, 6), + company="_Test Company", + currency="INR", + payroll_period=payroll_period.name, + base=70000, + allow_duplicate=True, + ) + + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "company": "_Test Company", + "payroll_period": payroll_period.name, + "currency": "INR", + "monthly_house_rent": 50000, + "rented_in_metro_city": 1, + "declarations": [ + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() + + # Monthly HRA received = 50000 * 6 months + 70000 * 6 months + # should set HRA exemption as per actual annual HRA because that's the minimum + self.assertEqual(declaration.monthly_hra_exemption, 8500) + self.assertEqual(declaration.annual_hra_exemption, 102000) + # 50000 Standard Exemption + 102000 HRA exemption + self.assertEqual(declaration.total_exemption_amount, 152000) + + # reset + frappe.flags.country = current_country + + +def create_payroll_period(**args): + args = frappe._dict(args) + name = args.name or "_Test Payroll Period" + if not frappe.db.exists("Payroll Period", name): + from datetime import date + + payroll_period = frappe.get_doc( + dict( + doctype="Payroll Period", + name=name, + company=args.company or erpnext.get_default_company(), + start_date=args.start_date or date(date.today().year, 1, 1), + end_date=args.end_date or date(date.today().year, 12, 31), + ) + ).insert() + return payroll_period + else: + return frappe.get_doc("Payroll Period", name) + + +def create_exemption_category(): + if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"): + category = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Category", + "name": "_Test Category", + "deduction_component": "Income Tax", + "max_amount": 100000, + } + ).insert() + if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Sub Category"): + frappe.get_doc( + { + "doctype": "Employee Tax Exemption Sub Category", + "name": "_Test Sub Category", + "exemption_category": "_Test Category", + "max_amount": 100000, + "is_active": 1, + } + ).insert() + if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test1 Sub Category"): + frappe.get_doc( + { + "doctype": "Employee Tax Exemption Sub Category", + "name": "_Test1 Sub Category", + "exemption_category": "_Test Category", + "max_amount": 50000, + "is_active": 1, + } + ).insert() + + +def setup_hra_exemption_prerequisites(frequency, employee=None): + from hrms.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company") + if not employee: + employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") + + create_tax_slab( + payroll_period, + allow_tax_exemption=True, + currency="INR", + effective_date=getdate("2019-04-01"), + company="_Test Company", + ) + + make_salary_structure( + f"{frequency} Structure for HRA Exemption", + frequency, + employee=employee, + company="_Test Company", + currency="INR", + payroll_period=payroll_period, + ) + + frappe.db.set_value( + "Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"} + ) diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py b/hrms/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json new file mode 100644 index 0000000..723a3df --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -0,0 +1,63 @@ +{ + "actions": [], + "creation": "2018-04-13 16:56:23.333041", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_sub_category", + "exemption_category", + "max_amount", + "amount" + ], + "fields": [ + { + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Sub Category", + "options": "Employee Tax Exemption Sub Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.exemption_category", + "fieldname": "exemption_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Category", + "options": "Employee Tax Exemption Category", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.max_amount", + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Maximum Exempted Amount", + "options": "currency", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Declared Amount", + "options": "currency", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-10-20 16:43:09.606265", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Declaration Category", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py b/hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py new file mode 100644 index 0000000..4322f31 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeTaxExemptionDeclarationCategory(Document): + pass diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js new file mode 100644 index 0000000..bd7fbba --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js @@ -0,0 +1,83 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Tax Exemption Proof Submission', { + setup: function(frm) { + frm.set_query('employee', function() { + return { + filters: { + 'status': "Active" + } + } + }); + + frm.set_query('payroll_period', function() { + if(frm.doc.employee && frm.doc.company){ + return { + filters: { + 'company': frm.doc.company + } + } + }else { + frappe.msgprint(__("Please select Employee")); + } + }); + + frm.set_query('exemption_sub_category', 'tax_exemption_proofs', function() { + return { + filters: { + 'is_active': 1 + } + } + }); + }, + + refresh: function(frm) { + if(frm.doc.docstatus === 0) { + let filters = { + docstatus: 1, + company: frm.doc.company + }; + if(frm.doc.employee) filters["employee"] = frm.doc.employee; + if(frm.doc.payroll_period) filters["payroll_period"] = frm.doc.payroll_period; + + frm.add_custom_button(__('Get Details From Declaration'), function() { + erpnext.utils.map_current_doc({ + method: "hrms.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + source_doctype: "Employee Tax Exemption Declaration", + target: frm, + date_field: "creation", + setters: { + employee: frm.doc.employee || undefined + }, + get_query_filters: filters + }); + }); + } + }, + + currency: function(frm) { + frm.refresh_fields(); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frm.trigger('get_employee_currency'); + } + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, +}); diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json new file mode 100644 index 0000000..bb90051 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -0,0 +1,219 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-TAX-PRF-.YYYY.-.#####", + "creation": "2018-04-13 17:24:11.456132", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "currency", + "column_break_2", + "submission_date", + "payroll_period", + "company", + "section_break_5", + "tax_exemption_proofs", + "section_break_10", + "total_actual_amount", + "column_break_12", + "exemption_amount", + "attachment_section", + "attachments", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "submission_date", + "fieldtype": "Date", + "label": "Submission Date", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "tax_exemption_proofs", + "fieldtype": "Table", + "label": "Tax Exemption Proofs", + "options": "Employee Tax Exemption Proof Submission Detail" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_actual_amount", + "fieldtype": "Currency", + "label": "Total Actual Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "exemption_amount", + "fieldtype": "Currency", + "label": "Total Exemption Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "attachment_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "attachments", + "fieldtype": "Attach", + "label": "Attachments" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Tax Exemption Proof Submission", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval: doc.employee", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:58:24.244546", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Proof Submission", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py new file mode 100644 index 0000000..d1a39cf --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -0,0 +1,53 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document +from frappe.utils import flt + +from hrms.hr.utils import ( + calculate_hra_exemption_for_period, + get_total_exemption_amount, + validate_active_employee, + validate_duplicate_exemption_for_payroll_period, + validate_tax_declaration, +) + + +class EmployeeTaxExemptionProofSubmission(Document): + def validate(self): + validate_active_employee(self.employee) + validate_tax_declaration(self.tax_exemption_proofs) + self.set_total_actual_amount() + self.set_total_exemption_amount() + self.calculate_hra_exemption() + validate_duplicate_exemption_for_payroll_period( + self.doctype, self.name, self.payroll_period, self.employee + ) + + def set_total_actual_amount(self): + self.total_actual_amount = flt(self.get("house_rent_payment_amount")) + for d in self.tax_exemption_proofs: + self.total_actual_amount += flt(d.amount) + + def set_total_exemption_amount(self): + self.exemption_amount = flt( + get_total_exemption_amount(self.tax_exemption_proofs), self.precision("exemption_amount") + ) + + def calculate_hra_exemption(self): + self.monthly_hra_exemption, self.monthly_house_rent, self.total_eligible_hra_exemption = 0, 0, 0 + if self.get("house_rent_payment_amount"): + hra_exemption = calculate_hra_exemption_for_period(self) + if hra_exemption: + self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] + self.exemption_amount = flt(self.exemption_amount, self.precision("exemption_amount")) + self.monthly_hra_exemption = flt( + hra_exemption["monthly_exemption"], self.precision("monthly_hra_exemption") + ) + self.monthly_house_rent = flt( + hra_exemption["monthly_house_rent"], self.precision("monthly_house_rent") + ) + self.total_eligible_hra_exemption = flt( + hra_exemption["total_eligible_hra_exemption"], self.precision("total_eligible_hra_exemption") + ) diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py new file mode 100644 index 0000000..7fcf371 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py @@ -0,0 +1,137 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( + create_exemption_category, + create_payroll_period, + setup_hra_exemption_prerequisites, +) + + +class TestEmployeeTaxExemptionProofSubmission(FrappeTestCase): + def setUp(self): + make_employee("employee@proofsubmission.com", company="_Test Company") + create_payroll_period(company="_Test Company") + create_exemption_category() + frappe.db.delete("Employee Tax Exemption Proof Submission") + frappe.db.delete("Salary Structure Assignment") + + def test_exemption_amount_lesser_than_category_max(self): + proof = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + type_of_proof="Test Proof", + exemption_category="_Test Category", + amount=150000, + ) + ], + } + ) + self.assertRaises(frappe.ValidationError, proof.save) + proof = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "payroll_period": "Test Payroll Period", + "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + type_of_proof="Test Proof", + exemption_category="_Test Category", + amount=100000, + ) + ], + } + ) + self.assertTrue(proof.save) + self.assertTrue(proof.submit) + + def test_duplicate_category_in_proof_submission(self): + proof = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + type_of_proof="Test Proof", + amount=100000, + ), + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=50000, + ), + ], + } + ) + self.assertRaises(frappe.ValidationError, proof.save) + + def test_india_hra_exemption(self): + # set country + current_country = frappe.flags.country + frappe.flags.country = "India" + + employee = frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name") + setup_hra_exemption_prerequisites("Monthly", employee) + payroll_period = frappe.db.get_value( + "Payroll Period", "_Test Payroll Period", ["start_date", "end_date"], as_dict=True + ) + + proof = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "employee": employee, + "company": "_Test Company", + "payroll_period": "_Test Payroll Period", + "currency": "INR", + "house_rent_payment_amount": 600000, + "rented_in_metro_city": 1, + "rented_from_date": payroll_period.start_date, + "rented_to_date": payroll_period.end_date, + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + type_of_proof="Test Proof", + amount=100000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + type_of_proof="Test Proof", + amount=50000, + ), + ], + } + ).insert() + + self.assertEqual(proof.monthly_house_rent, 50000) + + # Monthly HRA received = 3000 + # should set HRA exemption as per actual annual HRA because that's the minimum + self.assertEqual(proof.monthly_hra_exemption, 3000) + self.assertEqual(proof.total_eligible_hra_exemption, 36000) + + # total exemptions + house rent payment amount + self.assertEqual(proof.total_actual_amount, 750000) + + # 100000 Standard Exemption + 36000 HRA exemption + self.assertEqual(proof.exemption_amount, 136000) + + # reset + frappe.flags.country = current_country diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py b/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json new file mode 100644 index 0000000..2fd8b94 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -0,0 +1,68 @@ +{ + "actions": [], + "creation": "2018-04-13 17:19:03.006149", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_sub_category", + "exemption_category", + "max_amount", + "type_of_proof", + "amount" + ], + "fields": [ + { + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Sub Category", + "options": "Employee Tax Exemption Sub Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.exemption_category", + "fieldname": "exemption_category", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Exemption Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.max_amount", + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Maximum Exemption Amount", + "options": "currency", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "type_of_proof", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Type of Proof", + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Actual Amount", + "options": "currency" + } + ], + "istable": 1, + "links": [], + "modified": "2020-10-20 16:47:31.480870", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Proof Submission Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py b/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py new file mode 100644 index 0000000..37209e5 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class EmployeeTaxExemptionProofSubmissionDetail(Document): + pass diff --git a/hrms/payroll/doctype/employee_tax_exemption_sub_category/__init__.py b/hrms/payroll/doctype/employee_tax_exemption_sub_category/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js b/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js new file mode 100644 index 0000000..8a83a27 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Tax Exemption Sub Category', { + refresh: function(frm) { + + } +}); diff --git a/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json new file mode 100644 index 0000000..f8c4b8b --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json @@ -0,0 +1,86 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-05-09 12:47:26.983095", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_category", + "max_amount", + "is_active" + ], + "fields": [ + { + "fieldname": "exemption_category", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Tax Exemption Category", + "options": "Employee Tax Exemption Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_category.max_amount", + "fetch_if_empty": 1, + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Exemption Amount" + }, + { + "default": "1", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + } + ], + "links": [], + "modified": "2020-06-22 23:18:08.254645", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Sub Category", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py new file mode 100644 index 0000000..fb75d67 --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py @@ -0,0 +1,21 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt + + +class EmployeeTaxExemptionSubCategory(Document): + def validate(self): + category_max_amount = frappe.db.get_value( + "Employee Tax Exemption Category", self.exemption_category, "max_amount" + ) + if flt(self.max_amount) > flt(category_max_amount): + frappe.throw( + _( + "Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}" + ).format(category_max_amount, self.exemption_category) + ) diff --git a/hrms/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py b/hrms/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py new file mode 100644 index 0000000..64d2e3a --- /dev/null +++ b/hrms/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestEmployeeTaxExemptionSubCategory(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/gratuity/__init__.py b/hrms/payroll/doctype/gratuity/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/gratuity/gratuity.js b/hrms/payroll/doctype/gratuity/gratuity.js new file mode 100644 index 0000000..4f31158 --- /dev/null +++ b/hrms/payroll/doctype/gratuity/gratuity.js @@ -0,0 +1,73 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Gratuity', { + setup: function (frm) { + frm.set_query("salary_component", function () { + return { + filters: { + type: "Earning" + } + }; + }); + + frm.set_query("expense_account", function () { + return { + filters: { + "root_type": "Expense", + "is_group": 0, + "company": frm.doc.company + } + }; + }); + + frm.set_query("payable_account", function () { + return { + filters: { + "root_type": "Liability", + "is_group": 0, + "company": frm.doc.company + } + }; + }); + }, + refresh: function (frm) { + if (frm.doc.docstatus == 1 && !frm.doc.pay_via_salary_slip && frm.doc.status == "Unpaid") { + frm.add_custom_button(__("Create Payment Entry"), function () { + return frappe.call({ + method: "hrms.overrides.employee_payment_entry.get_payment_entry_for_employee", + args: { + "dt": frm.doc.doctype, + "dn": frm.doc.name + }, + callback: function (r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }); + } + }, + employee: function (frm) { + frm.events.calculate_work_experience_and_amount(frm); + }, + gratuity_rule: function (frm) { + frm.events.calculate_work_experience_and_amount(frm); + }, + calculate_work_experience_and_amount: function (frm) { + + if (frm.doc.employee && frm.doc.gratuity_rule) { + frappe.call({ + method: "hrms.payroll.doctype.gratuity.gratuity.calculate_work_experience_and_amount", + args: { + employee: frm.doc.employee, + gratuity_rule: frm.doc.gratuity_rule + } + }).then((r) => { + frm.set_value("current_work_experience", r.message['current_work_experience']); + frm.set_value("amount", r.message['amount']); + }); + } + } + +}); diff --git a/hrms/payroll/doctype/gratuity/gratuity.json b/hrms/payroll/doctype/gratuity/gratuity.json new file mode 100644 index 0000000..cd28425 --- /dev/null +++ b/hrms/payroll/doctype/gratuity/gratuity.json @@ -0,0 +1,234 @@ +{ + "actions": [], + "autoname": "HR-GRA-PAY-.#####", + "creation": "2022-01-27 16:24:28.200061", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "designation", + "column_break_3", + "posting_date", + "status", + "company", + "gratuity_rule", + "section_break_5", + "pay_via_salary_slip", + "payroll_date", + "salary_component", + "payable_account", + "expense_account", + "mode_of_payment", + "cost_center", + "column_break_15", + "current_work_experience", + "amount", + "paid_amount", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting date", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "current_work_experience", + "fieldtype": "Int", + "label": "Current Work Experience", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Total Amount", + "read_only": 1, + "reqd": 1 + }, + { + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Draft\nUnpaid\nPaid\nSubmitted\nCancelled", + "read_only": 1 + }, + { + "depends_on": "eval: !doc.pay_via_salary_slip", + "fieldname": "expense_account", + "fieldtype": "Link", + "label": "Expense Account", + "mandatory_depends_on": "eval: !doc.pay_via_salary_slip", + "options": "Account" + }, + { + "depends_on": "eval: !doc.pay_via_salary_slip", + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "mandatory_depends_on": "eval: !doc.pay_via_salary_slip", + "options": "Mode of Payment" + }, + { + "fieldname": "gratuity_rule", + "fieldtype": "Link", + "label": "Gratuity Rule", + "options": "Gratuity Rule", + "reqd": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Payment Configuration" + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Data", + "label": "Designation", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Gratuity", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.pay_via_salary_slip == 0", + "fieldname": "paid_amount", + "fieldtype": "Currency", + "label": "Paid Amount", + "no_copy": 1, + "read_only": 1 + }, + { + "depends_on": "eval: !doc.pay_via_salary_slip", + "fieldname": "payable_account", + "fieldtype": "Link", + "label": "Payable Account", + "mandatory_depends_on": "eval: !doc.pay_via_salary_slip", + "options": "Account" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "default": "1", + "fieldname": "pay_via_salary_slip", + "fieldtype": "Check", + "label": "Pay via Salary Slip" + }, + { + "depends_on": "pay_via_salary_slip", + "fieldname": "payroll_date", + "fieldtype": "Date", + "label": "Payroll Date", + "mandatory_depends_on": "pay_via_salary_slip" + }, + { + "depends_on": "pay_via_salary_slip", + "fieldname": "salary_component", + "fieldtype": "Link", + "label": "Salary Component", + "mandatory_depends_on": "pay_via_salary_slip", + "options": "Salary Component" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-07-20 11:35:36.553045", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Gratuity", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/gratuity/gratuity.py b/hrms/payroll/doctype/gratuity/gratuity.py new file mode 100644 index 0000000..06e218b --- /dev/null +++ b/hrms/payroll/doctype/gratuity/gratuity.py @@ -0,0 +1,356 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from math import floor + +import frappe +from frappe import _, bold +from frappe.query_builder.functions import Sum +from frappe.utils import flt, get_datetime, get_link_to_form + +from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.controllers.accounts_controller import AccountsController + + +class Gratuity(AccountsController): + def validate(self): + data = calculate_work_experience_and_amount(self.employee, self.gratuity_rule) + self.current_work_experience = data["current_work_experience"] + self.amount = data["amount"] + self.set_status() + + def set_status(self, update=False): + precision = self.precision("paid_amount") + status = None + + if self.docstatus == 0: + status = "Draft" + elif self.docstatus == 1: + if flt(self.paid_amount) > 0 and flt(self.amount, precision) == flt( + self.paid_amount, precision + ): + status = "Paid" + else: + status = "Unpaid" + elif self.docstatus == 2: + status = "Cancelled" + + if update: + self.db_set("status", status) + else: + self.status = status + + def on_submit(self): + if self.pay_via_salary_slip: + self.create_additional_salary() + else: + self.create_gl_entries() + + def on_cancel(self): + self.ignore_linked_doctypes = ["GL Entry"] + self.create_gl_entries(cancel=True) + self.set_status(update=True) + + def create_gl_entries(self, cancel=False): + gl_entries = self.get_gl_entries() + make_gl_entries(gl_entries, cancel) + + def get_gl_entries(self): + gl_entry = [] + # payable entry + if self.amount: + gl_entry.append( + self.get_gl_dict( + { + "account": self.payable_account, + "credit": self.amount, + "credit_in_account_currency": self.amount, + "against": self.expense_account, + "party_type": "Employee", + "party": self.employee, + "against_voucher_type": self.doctype, + "against_voucher": self.name, + "cost_center": self.cost_center, + }, + item=self, + ) + ) + + # expense entries + gl_entry.append( + self.get_gl_dict( + { + "account": self.expense_account, + "debit": self.amount, + "debit_in_account_currency": self.amount, + "against": self.payable_account, + "cost_center": self.cost_center, + }, + item=self, + ) + ) + else: + frappe.throw(_("Total Amount can not be zero")) + + return gl_entry + + def create_additional_salary(self): + if self.pay_via_salary_slip: + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.employee = self.employee + additional_salary.salary_component = self.salary_component + additional_salary.overwrite_salary_structure_amount = 0 + additional_salary.amount = self.amount + additional_salary.payroll_date = self.payroll_date + additional_salary.company = self.company + additional_salary.ref_doctype = self.doctype + additional_salary.ref_docname = self.name + additional_salary.submit() + + def set_total_advance_paid(self): + gle = frappe.qb.DocType("GL Entry") + paid_amount = ( + frappe.qb.from_(gle) + .select(Sum(gle.debit_in_account_currency).as_("paid_amount")) + .where( + (gle.against_voucher_type == "Gratuity") + & (gle.against_voucher == self.name) + & (gle.party_type == "Employee") + & (gle.party == self.employee) + & (gle.docstatus == 1) + & (gle.is_cancelled == 0) + ) + ).run(as_dict=True)[0].paid_amount or 0 + + if flt(paid_amount) > self.amount: + frappe.throw(_("Row {0}# Paid Amount cannot be greater than Total amount")) + + self.db_set("paid_amount", paid_amount) + self.set_status(update=True) + + +@frappe.whitelist() +def calculate_work_experience_and_amount(employee, gratuity_rule): + current_work_experience = calculate_work_experience(employee, gratuity_rule) or 0 + gratuity_amount = calculate_gratuity_amount(employee, gratuity_rule, current_work_experience) or 0 + + return {"current_work_experience": current_work_experience, "amount": gratuity_amount} + + +def calculate_work_experience(employee, gratuity_rule): + + total_working_days_per_year, minimum_year_for_gratuity = frappe.db.get_value( + "Gratuity Rule", gratuity_rule, ["total_working_days_per_year", "minimum_year_for_gratuity"] + ) + + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", employee, ["date_of_joining", "relieving_date"] + ) + if not relieving_date: + frappe.throw( + _("Please set Relieving Date for employee: {0}").format( + bold(get_link_to_form("Employee", employee)) + ) + ) + + method = frappe.db.get_value( + "Gratuity Rule", gratuity_rule, "work_experience_calculation_function" + ) + employee_total_workings_days = calculate_employee_total_workings_days( + employee, date_of_joining, relieving_date + ) + + current_work_experience = employee_total_workings_days / total_working_days_per_year or 1 + current_work_experience = get_work_experience_using_method( + method, current_work_experience, minimum_year_for_gratuity, employee + ) + return current_work_experience + + +def calculate_employee_total_workings_days(employee, date_of_joining, relieving_date): + employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days + + payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") or "Leave" + if payroll_based_on == "Leave": + total_lwp = get_non_working_days(employee, relieving_date, "On Leave") + employee_total_workings_days -= total_lwp + elif payroll_based_on == "Attendance": + total_absents = get_non_working_days(employee, relieving_date, "Absent") + employee_total_workings_days -= total_absents + + return employee_total_workings_days + + +def get_work_experience_using_method( + method, current_work_experience, minimum_year_for_gratuity, employee +): + if method == "Round off Work Experience": + current_work_experience = round(current_work_experience) + else: + current_work_experience = floor(current_work_experience) + + if current_work_experience < minimum_year_for_gratuity: + frappe.throw( + _("Employee: {0} have to complete minimum {1} years for gratuity").format( + bold(employee), minimum_year_for_gratuity + ) + ) + return current_work_experience + + +def get_non_working_days(employee, relieving_date, status): + + filters = { + "docstatus": 1, + "status": status, + "employee": employee, + "attendance_date": ("<=", get_datetime(relieving_date)), + } + + if status == "On Leave": + lwp_leave_types = frappe.get_list("Leave Type", filters={"is_lwp": 1}) + lwp_leave_types = [leave_type.name for leave_type in lwp_leave_types] + filters["leave_type"] = ("IN", lwp_leave_types) + + record = frappe.get_all("Attendance", filters=filters, fields=["COUNT(name) as total_lwp"]) + return record[0].total_lwp if len(record) else 0 + + +def calculate_gratuity_amount(employee, gratuity_rule, experience): + applicable_earnings_component = get_applicable_components(gratuity_rule) + total_applicable_components_amount = get_total_applicable_component_amount( + employee, applicable_earnings_component, gratuity_rule + ) + + calculate_gratuity_amount_based_on = frappe.db.get_value( + "Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on" + ) + gratuity_amount = 0 + slabs = get_gratuity_rule_slabs(gratuity_rule) + slab_found = False + year_left = experience + + for slab in slabs: + if calculate_gratuity_amount_based_on == "Current Slab": + slab_found, gratuity_amount = calculate_amount_based_on_current_slab( + slab.from_year, + slab.to_year, + experience, + total_applicable_components_amount, + slab.fraction_of_applicable_earnings, + ) + if slab_found: + break + + elif calculate_gratuity_amount_based_on == "Sum of all previous slabs": + if slab.to_year == 0 and slab.from_year == 0: + gratuity_amount += ( + year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings + ) + slab_found = True + break + + if experience > slab.to_year and experience > slab.from_year and slab.to_year != 0: + gratuity_amount += ( + (slab.to_year - slab.from_year) + * total_applicable_components_amount + * slab.fraction_of_applicable_earnings + ) + year_left -= slab.to_year - slab.from_year + slab_found = True + elif slab.from_year <= experience and (experience < slab.to_year or slab.to_year == 0): + gratuity_amount += ( + year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings + ) + slab_found = True + + if not slab_found: + frappe.throw( + _("No Suitable Slab found for Calculation of gratuity amount in Gratuity Rule: {0}").format( + bold(gratuity_rule) + ) + ) + return gratuity_amount + + +def get_applicable_components(gratuity_rule): + applicable_earnings_component = frappe.get_all( + "Gratuity Applicable Component", filters={"parent": gratuity_rule}, fields=["salary_component"] + ) + if len(applicable_earnings_component) == 0: + frappe.throw( + _("No Applicable Earnings Component found for Gratuity Rule: {0}").format( + bold(get_link_to_form("Gratuity Rule", gratuity_rule)) + ) + ) + applicable_earnings_component = [ + component.salary_component for component in applicable_earnings_component + ] + + return applicable_earnings_component + + +def get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule): + sal_slip = get_last_salary_slip(employee) + if not sal_slip: + frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee))) + component_and_amounts = frappe.get_all( + "Salary Detail", + filters={ + "docstatus": 1, + "parent": sal_slip, + "parentfield": "earnings", + "salary_component": ("in", applicable_earnings_component), + }, + fields=["amount"], + ) + total_applicable_components_amount = 0 + if not len(component_and_amounts): + frappe.throw(_("No Applicable Component is present in last month salary slip")) + for data in component_and_amounts: + total_applicable_components_amount += data.amount + return total_applicable_components_amount + + +def calculate_amount_based_on_current_slab( + from_year, + to_year, + experience, + total_applicable_components_amount, + fraction_of_applicable_earnings, +): + slab_found = False + gratuity_amount = 0 + if experience >= from_year and (to_year == 0 or experience < to_year): + gratuity_amount = ( + total_applicable_components_amount * experience * fraction_of_applicable_earnings + ) + if fraction_of_applicable_earnings: + slab_found = True + + return slab_found, gratuity_amount + + +def get_gratuity_rule_slabs(gratuity_rule): + return frappe.get_all( + "Gratuity Rule Slab", filters={"parent": gratuity_rule}, fields=["*"], order_by="idx" + ) + + +def get_salary_structure(employee): + return frappe.get_list( + "Salary Structure Assignment", + filters={"employee": employee, "docstatus": 1}, + fields=["from_date", "salary_structure"], + order_by="from_date desc", + )[0].salary_structure + + +def get_last_salary_slip(employee): + salary_slips = frappe.get_list( + "Salary Slip", filters={"employee": employee, "docstatus": 1}, order_by="start_date desc" + ) + if not salary_slips: + return + return salary_slips[0].name diff --git a/hrms/payroll/doctype/gratuity/gratuity_dashboard.py b/hrms/payroll/doctype/gratuity/gratuity_dashboard.py new file mode 100644 index 0000000..9396461 --- /dev/null +++ b/hrms/payroll/doctype/gratuity/gratuity_dashboard.py @@ -0,0 +1,11 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "reference_name", + "non_standard_fieldnames": { + "Additional Salary": "ref_docname", + }, + "transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Additional Salary"]}], + } diff --git a/hrms/payroll/doctype/gratuity/gratuity_list.js b/hrms/payroll/doctype/gratuity/gratuity_list.js new file mode 100644 index 0000000..20e3d5b --- /dev/null +++ b/hrms/payroll/doctype/gratuity/gratuity_list.js @@ -0,0 +1,12 @@ +frappe.listview_settings["Gratuity"] = { + get_indicator: function(doc) { + let status_color = { + "Draft": "red", + "Submitted": "blue", + "Cancelled": "red", + "Paid": "green", + "Unpaid": "orange", + }; + return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; + } +}; \ No newline at end of file diff --git a/hrms/payroll/doctype/gratuity/test_gratuity.py b/hrms/payroll/doctype/gratuity/test_gratuity.py new file mode 100644 index 0000000..3cd1397 --- /dev/null +++ b/hrms/payroll/doctype/gratuity/test_gratuity.py @@ -0,0 +1,237 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, add_months, floor, flt, get_datetime, get_first_day, getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee +from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list + +from hrms.hr.doctype.expense_claim.test_expense_claim import get_payable_account +from hrms.payroll.doctype.gratuity.gratuity import get_last_salary_slip +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + make_deduction_salary_component, + make_earning_salary_component, + make_employee_salary_slip, + make_holiday_list, +) +from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from hrms.regional.united_arab_emirates.setup import setup + +test_dependencies = ["Salary Component", "Salary Slip", "Account"] + + +class TestGratuity(FrappeTestCase): + def setUp(self): + frappe.db.delete("Gratuity") + frappe.db.delete("Salary Slip") + frappe.db.delete("Additional Salary", {"ref_doctype": "Gratuity"}) + + make_earning_salary_component( + setup=True, test_tax=True, company_list=["_Test Company"], include_flexi_benefits=True + ) + make_deduction_salary_component(setup=True, test_tax=True, company_list=["_Test Company"]) + make_holiday_list() + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_get_last_salary_slip_should_return_none_for_new_employee(self): + new_employee = make_employee("new_employee@salary.com", company="_Test Company") + salary_slip = get_last_salary_slip(new_employee) + self.assertIsNone(salary_slip) + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_gratuity_based_on_current_slab_via_additional_salary(self): + """ + Range | Fraction + 5-0 | 1 + """ + doj = add_days(getdate(), -(6 * 365)) + relieving_date = getdate() + + employee = make_employee( + "test_employee_gratuity@salary.com", + company="_Test Company", + date_of_joining=doj, + relieving_date=relieving_date, + ) + sal_slip = create_salary_slip("test_employee_gratuity@salary.com") + + rule = get_gratuity_rule("Rule Under Unlimited Contract on termination (UAE)") + gratuity = create_gratuity(pay_via_salary_slip=1, employee=employee, rule=rule.name) + + # work experience calculation + employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days + experience = floor(employee_total_workings_days / rule.total_working_days_per_year) + self.assertEqual(gratuity.current_work_experience, experience) + + # amount calculation + component_amount = frappe.get_all( + "Salary Detail", + filters={ + "docstatus": 1, + "parent": sal_slip, + "parentfield": "earnings", + "salary_component": "Basic Salary", + }, + fields=["amount"], + limit=1, + ) + gratuity_amount = component_amount[0].amount * experience + self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2)) + + # additional salary creation (Pay via salary slip) + self.assertTrue(frappe.db.exists("Additional Salary", {"ref_docname": gratuity.name})) + + # gratuity should be marked "Paid" on the next salary slip submission + salary_slip = make_salary_slip("Test Gratuity", employee=employee) + salary_slip.posting_date = getdate() + salary_slip.insert() + salary_slip.submit() + + gratuity.reload() + self.assertEqual(gratuity.status, "Paid") + + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_gratuity_based_on_all_previous_slabs_via_payment_entry(self): + """ + Range | Fraction + 0-1 | 0 + 1-5 | 0.7 + 5-0 | 1 + """ + from hrms.overrides.employee_payment_entry import get_payment_entry_for_employee + + doj = add_days(getdate(), -(6 * 365)) + relieving_date = getdate() + + employee = make_employee( + "test_employee_gratuity@salary.com", + company="_Test Company", + date_of_joining=doj, + relieving_date=relieving_date, + ) + + sal_slip = create_salary_slip("test_employee_gratuity@salary.com") + rule = get_gratuity_rule("Rule Under Limited Contract (UAE)") + set_mode_of_payment_account() + + gratuity = create_gratuity( + expense_account="Payment Account - _TC", mode_of_payment="Cash", employee=employee + ) + + # work experience calculation + employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days + experience = floor(employee_total_workings_days / rule.total_working_days_per_year) + self.assertEqual(gratuity.current_work_experience, experience) + + # amount calculation + component_amount = frappe.get_all( + "Salary Detail", + filters={ + "docstatus": 1, + "parent": sal_slip, + "parentfield": "earnings", + "salary_component": "Basic Salary", + }, + fields=["amount"], + limit=1, + ) + + gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount + self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2)) + self.assertEqual(gratuity.status, "Unpaid") + + pe = get_payment_entry_for_employee("Gratuity", gratuity.name) + pe.reference_no = "123467" + pe.reference_date = getdate() + pe.submit() + + gratuity.reload() + self.assertEqual(gratuity.status, "Paid") + self.assertEqual(flt(gratuity.paid_amount, 2), flt(gratuity.amount, 2)) + + pe.cancel() + gratuity.reload() + self.assertEqual(gratuity.status, "Unpaid") + self.assertEqual(gratuity.paid_amount, 0) + + +def get_gratuity_rule(name): + rule = frappe.db.exists("Gratuity Rule", name) + if not rule: + setup() + rule = frappe.get_doc("Gratuity Rule", name) + rule.applicable_earnings_component = [] + rule.append("applicable_earnings_component", {"salary_component": "Basic Salary"}) + rule.save() + + return rule + + +def create_gratuity(**args): + if args: + args = frappe._dict(args) + gratuity = frappe.new_doc("Gratuity") + gratuity.employee = args.employee + gratuity.posting_date = getdate() + gratuity.gratuity_rule = args.rule or "Rule Under Limited Contract (UAE)" + gratuity.pay_via_salary_slip = args.pay_via_salary_slip or 0 + if gratuity.pay_via_salary_slip: + gratuity.payroll_date = getdate() + gratuity.salary_component = "Performance Bonus" + else: + gratuity.expense_account = args.expense_account or "Payment Account - _TC" + gratuity.payable_account = args.payable_account or get_payable_account("_Test Company") + gratuity.mode_of_payment = args.mode_of_payment or "Cash" + + gratuity.save() + gratuity.submit() + + return gratuity + + +def set_mode_of_payment_account(): + if not frappe.db.exists("Account", "Payment Account - _TC"): + mode_of_payment = create_account() + + mode_of_payment = frappe.get_doc("Mode of Payment", "Cash") + + mode_of_payment.accounts = [] + mode_of_payment.append( + "accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"} + ) + mode_of_payment.save() + + +def create_account(): + return frappe.get_doc( + { + "doctype": "Account", + "company": "_Test Company", + "account_name": "Payment Account", + "root_type": "Asset", + "report_type": "Balance Sheet", + "currency": "INR", + "parent_account": "Bank Accounts - _TC", + "account_type": "Bank", + } + ).insert(ignore_permissions=True) + + +def create_salary_slip(employee): + if not frappe.db.exists("Salary Slip", {"employee": employee}): + posting_date = get_first_day(add_months(getdate(), -1)) + salary_slip = make_employee_salary_slip( + employee, "Monthly", "Test Gratuity", posting_date=posting_date + ) + salary_slip.start_date = posting_date + salary_slip.end_date = None + salary_slip.submit() + salary_slip = salary_slip.name + else: + salary_slip = get_last_salary_slip(employee) + + return salary_slip diff --git a/hrms/payroll/doctype/gratuity_applicable_component/__init__.py b/hrms/payroll/doctype/gratuity_applicable_component/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json b/hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json new file mode 100644 index 0000000..eea0e85 --- /dev/null +++ b/hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2020-08-05 19:00:28.097265", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component" + ], + "fields": [ + { + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Salary Component ", + "options": "Salary Component", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-08-05 20:17:13.855035", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Gratuity Applicable Component", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py b/hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py new file mode 100644 index 0000000..9c1657d --- /dev/null +++ b/hrms/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class GratuityApplicableComponent(Document): + pass diff --git a/hrms/payroll/doctype/gratuity_rule/__init__.py b/hrms/payroll/doctype/gratuity_rule/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/gratuity_rule/gratuity_rule.js b/hrms/payroll/doctype/gratuity_rule/gratuity_rule.js new file mode 100644 index 0000000..7290a9e --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule/gratuity_rule.js @@ -0,0 +1,40 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Gratuity Rule', { + // refresh: function(frm) { + + // } +}); + +frappe.ui.form.on('Gratuity Rule Slab', { + + /* + Slabs should be in order like + + from | to | fraction + 0 | 4 | 0.5 + 4 | 6 | 0.7 + + So, on row addition setting current_row.from = previous row.to. + On to_year insert we have to check that it is not less than from_year + + Wrong order may lead to Wrong Calculation + */ + + gratuity_rule_slabs_add(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + let array_idx = row.idx - 1; + if (array_idx > 0) { + row.from_year = cur_frm.doc.gratuity_rule_slabs[array_idx - 1].to_year; + frm.refresh(); + } + }, + + to_year(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.to_year <= row.from_year && row.to_year === 0) { + frappe.throw(__("To(Year) year can not be less than From(year)")); + } + } +}); diff --git a/hrms/payroll/doctype/gratuity_rule/gratuity_rule.json b/hrms/payroll/doctype/gratuity_rule/gratuity_rule.json new file mode 100644 index 0000000..84cdcf5 --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule/gratuity_rule.json @@ -0,0 +1,114 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-08-05 19:00:36.103500", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "applicable_earnings_component", + "work_experience_calculation_function", + "total_working_days_per_year", + "column_break_3", + "disable", + "calculate_gratuity_amount_based_on", + "minimum_year_for_gratuity", + "gratuity_rules_section", + "gratuity_rule_slabs" + ], + "fields": [ + { + "default": "0", + "fieldname": "disable", + "fieldtype": "Check", + "label": "Disable" + }, + { + "fieldname": "calculate_gratuity_amount_based_on", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Calculate Gratuity Amount Based On", + "options": "Current Slab\nSum of all previous slabs", + "reqd": 1 + }, + { + "description": "Salary components should be part of the Salary Structure.", + "fieldname": "applicable_earnings_component", + "fieldtype": "Table MultiSelect", + "label": "Applicable Earnings Component", + "options": "Gratuity Applicable Component", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "gratuity_rules_section", + "fieldtype": "Section Break", + "label": "Gratuity Rules" + }, + { + "description": "Leave From and To 0 for no upper and lower limit.", + "fieldname": "gratuity_rule_slabs", + "fieldtype": "Table", + "label": "Current Work Experience", + "options": "Gratuity Rule Slab", + "reqd": 1 + }, + { + "default": "Round off Work Experience", + "fieldname": "work_experience_calculation_function", + "fieldtype": "Select", + "label": "Work Experience Calculation method", + "options": "Round off Work Experience\nTake Exact Completed Years" + }, + { + "default": "365", + "fieldname": "total_working_days_per_year", + "fieldtype": "Int", + "label": "Total working Days Per Year" + }, + { + "fieldname": "minimum_year_for_gratuity", + "fieldtype": "Int", + "label": "Minimum Year for Gratuity" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-12-03 17:08:27.891535", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Gratuity Rule", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/gratuity_rule/gratuity_rule.py b/hrms/payroll/doctype/gratuity_rule/gratuity_rule.py new file mode 100644 index 0000000..5cde79a --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule/gratuity_rule.py @@ -0,0 +1,42 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class GratuityRule(Document): + def validate(self): + for current_slab in self.gratuity_rule_slabs: + if (current_slab.from_year > current_slab.to_year) and current_slab.to_year != 0: + frappe.throw( + _("Row {0}: From (Year) can not be greater than To (Year)").format(current_slab.idx) + ) + + if ( + current_slab.to_year == 0 and current_slab.from_year == 0 and len(self.gratuity_rule_slabs) > 1 + ): + frappe.throw( + _("You can not define multiple slabs if you have a slab with no lower and upper limits.") + ) + + +def get_gratuity_rule(name, slabs, **args): + args = frappe._dict(args) + + rule = frappe.new_doc("Gratuity Rule") + rule.name = name + rule.calculate_gratuity_amount_based_on = ( + args.calculate_gratuity_amount_based_on or "Current Slab" + ) + rule.work_experience_calculation_method = ( + args.work_experience_calculation_method or "Take Exact Completed Years" + ) + rule.minimum_year_for_gratuity = 1 + + for slab in slabs: + slab = frappe._dict(slab) + rule.append("gratuity_rule_slabs", slab) + return rule diff --git a/hrms/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py b/hrms/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py new file mode 100644 index 0000000..fa5a9de --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py @@ -0,0 +1,8 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "gratuity_rule", + "transactions": [{"label": _("Gratuity"), "items": ["Gratuity"]}], + } diff --git a/hrms/payroll/doctype/gratuity_rule/test_gratuity_rule.py b/hrms/payroll/doctype/gratuity_rule/test_gratuity_rule.py new file mode 100644 index 0000000..8393050 --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule/test_gratuity_rule.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestGratuityRule(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/gratuity_rule_slab/__init__.py b/hrms/payroll/doctype/gratuity_rule_slab/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json b/hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json new file mode 100644 index 0000000..bc37b0f --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "creation": "2020-08-05 19:12:49.423500", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_year", + "to_year", + "fraction_of_applicable_earnings" + ], + "fields": [ + { + "fieldname": "fraction_of_applicable_earnings", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Fraction of Applicable Earnings ", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "from_year", + "fieldtype": "Int", + "in_list_view": 1, + "label": "From(Year)", + "read_only": 1, + "reqd": 1 + }, + { + "default": "0", + "fieldname": "to_year", + "fieldtype": "Int", + "in_list_view": 1, + "label": "To(Year)", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-08-17 14:09:56.781712", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Gratuity Rule Slab", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py b/hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py new file mode 100644 index 0000000..2ae6b54 --- /dev/null +++ b/hrms/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class GratuityRuleSlab(Document): + pass diff --git a/hrms/payroll/doctype/income_tax_slab/__init__.py b/hrms/payroll/doctype/income_tax_slab/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/income_tax_slab/income_tax_slab.js b/hrms/payroll/doctype/income_tax_slab/income_tax_slab.js new file mode 100644 index 0000000..7d780d3 --- /dev/null +++ b/hrms/payroll/doctype/income_tax_slab/income_tax_slab.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Income Tax Slab', { + currency: function(frm) { + frm.refresh_fields(); + } +}); diff --git a/hrms/payroll/doctype/income_tax_slab/income_tax_slab.json b/hrms/payroll/doctype/income_tax_slab/income_tax_slab.json new file mode 100644 index 0000000..5a7de37 --- /dev/null +++ b/hrms/payroll/doctype/income_tax_slab/income_tax_slab.json @@ -0,0 +1,162 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-03-17 16:50:35.564915", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "effective_from", + "company", + "column_break_3", + "currency", + "standard_tax_exemption_amount", + "allow_tax_exemption", + "disabled", + "amended_from", + "taxable_salary_slabs_section", + "slabs", + "taxes_and_charges_on_income_tax_section", + "other_taxes_and_charges" + ], + "fields": [ + { + "fieldname": "effective_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Effective from", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If enabled, Tax Exemption Declaration will be considered for income tax calculation.", + "fieldname": "allow_tax_exemption", + "fieldtype": "Check", + "label": "Allow Tax Exemption" + }, + { + "fieldname": "taxable_salary_slabs_section", + "fieldtype": "Section Break", + "label": "Taxable Salary Slabs" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Income Tax Slab", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "slabs", + "fieldtype": "Table", + "label": "Taxable Salary Slabs", + "options": "Taxable Salary Slab", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "depends_on": "allow_tax_exemption", + "fieldname": "standard_tax_exemption_amount", + "fieldtype": "Currency", + "label": "Standard Tax Exemption Amount", + "options": "currency" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "collapsible": 1, + "collapsible_depends_on": "other_taxes_and_charges", + "fieldname": "taxes_and_charges_on_income_tax_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges on Income Tax" + }, + { + "fieldname": "other_taxes_and_charges", + "fieldtype": "Table", + "label": "Other Taxes and Charges", + "options": "Income Tax Slab Other Charges" + }, + { + "fetch_from": "company.default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2021-03-31 22:42:08.139520", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Income Tax Slab", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/income_tax_slab/income_tax_slab.py b/hrms/payroll/doctype/income_tax_slab/income_tax_slab.py new file mode 100644 index 0000000..e62d61f --- /dev/null +++ b/hrms/payroll/doctype/income_tax_slab/income_tax_slab.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + +# import frappe +import erpnext + + +class IncomeTaxSlab(Document): + def validate(self): + if self.company: + self.currency = erpnext.get_company_currency(self.company) diff --git a/hrms/payroll/doctype/income_tax_slab/test_income_tax_slab.py b/hrms/payroll/doctype/income_tax_slab/test_income_tax_slab.py new file mode 100644 index 0000000..680cb3b --- /dev/null +++ b/hrms/payroll/doctype/income_tax_slab/test_income_tax_slab.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestIncomeTaxSlab(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/income_tax_slab_other_charges/__init__.py b/hrms/payroll/doctype/income_tax_slab_other_charges/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json new file mode 100644 index 0000000..0dba338 --- /dev/null +++ b/hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -0,0 +1,75 @@ +{ + "actions": [], + "creation": "2020-04-24 11:46:59.041180", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "description", + "column_break_2", + "percent", + "conditions_section", + "min_taxable_income", + "column_break_7", + "max_taxable_income" + ], + "fields": [ + { + "columns": 4, + "fieldname": "description", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "percent", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Percent", + "reqd": 1 + }, + { + "fieldname": "conditions_section", + "fieldtype": "Section Break", + "label": "Conditions" + }, + { + "columns": 2, + "fieldname": "min_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Min Taxable Income", + "options": "currency" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "max_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Taxable Income", + "options": "currency" + } + ], + "istable": 1, + "links": [], + "modified": "2020-10-19 13:45:12.850090", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Income Tax Slab Other Charges", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py new file mode 100644 index 0000000..53911a9 --- /dev/null +++ b/hrms/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class IncomeTaxSlabOtherCharges(Document): + pass diff --git a/hrms/payroll/doctype/payroll_employee_detail/__init__.py b/hrms/payroll/doctype/payroll_employee_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json new file mode 100644 index 0000000..09c7eb9 --- /dev/null +++ b/hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json @@ -0,0 +1,65 @@ +{ + "actions": [], + "creation": "2017-11-30 06:07:33.477781", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "column_break_3", + "department", + "designation" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee" + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Designation", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-12-17 15:43:29.542977", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Employee Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py b/hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py new file mode 100644 index 0000000..8cc426b --- /dev/null +++ b/hrms/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class PayrollEmployeeDetail(Document): + pass diff --git a/hrms/payroll/doctype/payroll_entry/__init__.py b/hrms/payroll/doctype/payroll_entry/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/payroll_entry/payroll_entry.js b/hrms/payroll/doctype/payroll_entry/payroll_entry.js new file mode 100644 index 0000000..fb3e8a0 --- /dev/null +++ b/hrms/payroll/doctype/payroll_entry/payroll_entry.js @@ -0,0 +1,418 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +var in_progress = false; + +frappe.provide("erpnext.accounts.dimensions"); + +frappe.ui.form.on('Payroll Entry', { + onload: function (frm) { + if (!frm.doc.posting_date) { + frm.doc.posting_date = frappe.datetime.nowdate(); + } + frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + frm.events.department_filters(frm); + frm.events.payroll_payable_account_filters(frm); + }, + + department_filters: function (frm) { + frm.set_query("department", function () { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); + }, + + payroll_payable_account_filters: function (frm) { + frm.set_query("payroll_payable_account", function () { + return { + filters: { + "company": frm.doc.company, + "root_type": "Liability", + "is_group": 0, + } + }; + }); + }, + + refresh: function (frm) { + if (frm.doc.docstatus === 0 && !frm.is_new()) { + frm.page.clear_primary_action(); + frm.add_custom_button(__("Get Employees"), + function () { + frm.events.get_employee_details(frm); + } + ).toggleClass("btn-primary", !(frm.doc.employees || []).length); + } + + if ( + (frm.doc.employees || []).length + && !frappe.model.has_workflow(frm.doctype) + && !cint(frm.doc.salary_slips_created) + && (frm.doc.docstatus != 2) + ) { + if (frm.doc.docstatus == 0) { + frm.page.clear_primary_action(); + frm.page.set_primary_action(__("Create Salary Slips"), () => { + frm.save("Submit").then(() => { + frm.page.clear_primary_action(); + frm.refresh(); + frm.events.refresh(frm); + }); + }); + } else if (frm.doc.docstatus == 1 && frm.doc.status == "Failed") { + frm.add_custom_button(__("Create Salary Slip"), function () { + frm.call("create_salary_slips", {}, () => { + frm.reload_doc(); + }); + }).addClass("btn-primary"); + } + } + + if (frm.doc.docstatus == 1 && frm.doc.status == "Submitted") { + if (frm.custom_buttons) frm.clear_custom_buttons(); + frm.events.add_context_buttons(frm); + } + + if (frm.doc.status == "Failed" && frm.doc.error_message) { + const issue = `issue`; + let process = (cint(frm.doc.salary_slips_created)) ? "submission" : "creation"; + + frm.dashboard.set_headline( + __("Salary Slip {0} failed. You can resolve the {1} and retry {0}.", [process, issue]) + ); + + $("#jump_to_error").on("click", (e) => { + e.preventDefault(); + frappe.utils.scroll_to( + frm.get_field("error_message").$wrapper, + true, + 30 + ); + }); + } + + frappe.realtime.on("completed_salary_slip_creation", function() { + frm.reload_doc(); + }); + + frappe.realtime.on("completed_salary_slip_submission", function() { + frm.reload_doc(); + }); + }, + + get_employee_details: function (frm) { + return frappe.call({ + doc: frm.doc, + method: 'fill_employee_details', + }).then(r => { + if (r.docs && r.docs[0].employees) { + frm.employees = r.docs[0].employees; + frm.dirty(); + frm.save(); + frm.refresh(); + if (r.docs[0].validate_attendance) { + render_employee_attendance(frm, r.message); + } + } + }); + }, + + create_salary_slips: function (frm) { + frm.call({ + doc: frm.doc, + method: "create_salary_slips", + callback: function () { + frm.reload_doc(); + frm.toolbar.refresh(); + } + }); + }, + + add_context_buttons: function (frm) { + if (frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) { + frm.events.add_bank_entry_button(frm); + } else if (frm.doc.salary_slips_created && frm.doc.status != 'Queued') { + frm.add_custom_button(__("Submit Salary Slip"), function () { + submit_salary_slip(frm); + }).addClass("btn-primary"); + } + }, + + add_bank_entry_button: function (frm) { + frappe.call({ + method: 'hrms.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', + args: { + 'name': frm.doc.name + }, + callback: function (r) { + if (r.message && !r.message.submitted) { + frm.add_custom_button(__("Make Bank Entry"), function () { + make_bank_entry(frm); + }).addClass("btn-primary"); + } + } + }); + }, + + setup: function (frm) { + frm.add_fetch('company', 'cost_center', 'cost_center'); + + frm.set_query("payment_account", function () { + var account_types = ["Bank", "Cash"]; + return { + filters: { + "account_type": ["in", account_types], + "is_group": 0, + "company": frm.doc.company + } + }; + }); + + frm.set_query('employee', 'employees', () => { + let error_fields = []; + let mandatory_fields = ['company', 'payroll_frequency', 'start_date', 'end_date']; + + let message = __('Mandatory fields required in {0}', [__(frm.doc.doctype)]); + + mandatory_fields.forEach(field => { + if (!frm.doc[field]) { + error_fields.push(frappe.unscrub(field)); + } + }); + + if (error_fields && error_fields.length) { + message = message + '

  • ' + error_fields.join('
  • ') + "
"; + frappe.throw({ + message: message, + indicator: 'red', + title: __('Missing Fields') + }); + } + + return { + query: "hrms.payroll.doctype.payroll_entry.payroll_entry.employee_query", + filters: frm.events.get_employee_filters(frm) + }; + }); + }, + + get_employee_filters: function (frm) { + let filters = {}; + filters['salary_slip_based_on_timesheet'] = frm.doc.salary_slip_based_on_timesheet; + + let fields = ['company', 'start_date', 'end_date', 'payroll_frequency', 'payroll_payable_account', + 'currency', 'department', 'branch', 'designation']; + + fields.forEach(field => { + if (frm.doc[field]) { + filters[field] = frm.doc[field]; + } + }); + + if (frm.doc.employees) { + let employees = frm.doc.employees.filter(d => d.employee).map(d => d.employee); + if (employees && employees.length) { + filters['employees'] = employees; + } + } + return filters; + }, + + payroll_frequency: function (frm) { + frm.trigger("set_start_end_dates").then( ()=> { + frm.events.clear_employee_table(frm); + }); + }, + + company: function (frm) { + frm.events.clear_employee_table(frm); + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + frm.trigger("set_payable_account_and_currency"); + }, + + set_payable_account_and_currency: function (frm) { + frappe.db.get_value("Company", {"name": frm.doc.company}, "default_currency", (r) => { + frm.set_value('currency', r.default_currency); + }); + frappe.db.get_value("Company", {"name": frm.doc.company}, "default_payroll_payable_account", (r) => { + frm.set_value('payroll_payable_account', r.default_payroll_payable_account); + }); + }, + + currency: function (frm) { + var company_currency; + if (!frm.doc.company) { + company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } else { + company_currency = erpnext.get_currency(frm.doc.company); + } + if (frm.doc.currency) { + if (company_currency != frm.doc.currency) { + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: frm.doc.currency, + to_currency: company_currency, + }, + callback: function (r) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + } + }); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", ""); + } + } + }, + + department: function (frm) { + frm.events.clear_employee_table(frm); + }, + + designation: function (frm) { + frm.events.clear_employee_table(frm); + }, + + branch: function (frm) { + frm.events.clear_employee_table(frm); + }, + + start_date: function (frm) { + if (!in_progress && frm.doc.start_date) { + frm.trigger("set_end_date"); + } else { + // reset flag + in_progress = false; + } + frm.events.clear_employee_table(frm); + }, + + project: function (frm) { + frm.events.clear_employee_table(frm); + }, + + salary_slip_based_on_timesheet: function (frm) { + frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); + }, + + set_start_end_dates: function (frm) { + if (!frm.doc.salary_slip_based_on_timesheet) { + frappe.call({ + method: 'hrms.payroll.doctype.payroll_entry.payroll_entry.get_start_end_dates', + args: { + payroll_frequency: frm.doc.payroll_frequency, + start_date: frm.doc.posting_date + }, + callback: function (r) { + if (r.message) { + in_progress = true; + frm.set_value('start_date', r.message.start_date); + frm.set_value('end_date', r.message.end_date); + } + } + }); + } + }, + + set_end_date: function (frm) { + frappe.call({ + method: 'hrms.payroll.doctype.payroll_entry.payroll_entry.get_end_date', + args: { + frequency: frm.doc.payroll_frequency, + start_date: frm.doc.start_date + }, + callback: function (r) { + if (r.message) { + frm.set_value('end_date', r.message.end_date); + } + } + }); + }, + + validate_attendance: function (frm) { + if (frm.doc.validate_attendance && frm.doc.employees) { + frappe.call({ + method: 'validate_employee_attendance', + args: {}, + callback: function (r) { + render_employee_attendance(frm, r.message); + }, + doc: frm.doc, + freeze: true, + freeze_message: __('Validating Employee Attendance...') + }); + } else { + frm.fields_dict.attendance_detail_html.html(""); + } + }, + + clear_employee_table: function (frm) { + frm.clear_table('employees'); + frm.refresh(); + }, +}); + +// Submit salary slips + +const submit_salary_slip = function (frm) { + frappe.confirm(__('This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?'), + function () { + frappe.call({ + method: 'submit_salary_slips', + args: {}, + callback: function () { + frm.reload_doc(); + frm.events.refresh(frm); + }, + doc: frm.doc, + freeze: true, + freeze_message: __('Submitting Salary Slips and creating Journal Entry...') + }); + }, + function () { + if (frappe.dom.freeze_count) { + frappe.dom.unfreeze(); + frm.events.refresh(frm); + } + } + ); +}; + +let make_bank_entry = function (frm) { + var doc = frm.doc; + if (doc.payment_account) { + return frappe.call({ + doc: cur_frm.doc, + method: "make_payment_entry", + callback: function () { + frappe.set_route( + 'List', 'Journal Entry', { + "Journal Entry Account.reference_name": frm.doc.name + } + ); + }, + freeze: true, + freeze_message: __("Creating Payment Entries......") + }); + } else { + frappe.msgprint(__("Payment Account is mandatory")); + frm.scroll_to_field('payment_account'); + } +}; + +let render_employee_attendance = function (frm, data) { + frm.fields_dict.attendance_detail_html.html( + frappe.render_template('employees_to_mark_attendance', { + data: data + }) + ); +}; diff --git a/hrms/payroll/doctype/payroll_entry/payroll_entry.json b/hrms/payroll/doctype/payroll_entry/payroll_entry.json new file mode 100644 index 0000000..17882eb --- /dev/null +++ b/hrms/payroll/doctype/payroll_entry/payroll_entry.json @@ -0,0 +1,342 @@ +{ + "actions": [], + "allow_copy": 1, + "autoname": "HR-PRUN-.YYYY.-.#####", + "creation": "2017-10-23 15:22:29.291323", + "doctype": "DocType", + "document_type": "Other", + "engine": "InnoDB", + "field_order": [ + "section_break0", + "posting_date", + "payroll_frequency", + "company", + "column_break1", + "status", + "currency", + "exchange_rate", + "payroll_payable_account", + "section_break_8", + "branch", + "department", + "column_break_10", + "designation", + "number_of_employees", + "sec_break20", + "employees", + "section_break_13", + "validate_attendance", + "attendance_detail_html", + "section_break_12", + "salary_slip_based_on_timesheet", + "select_payroll_period", + "start_date", + "end_date", + "column_break_11", + "deduct_tax_for_unclaimed_employee_benefits", + "deduct_tax_for_unsubmitted_tax_exemption_proof", + "accounting_dimensions_section", + "project", + "dimension_col_break", + "cost_center", + "account", + "payment_account", + "column_break_33", + "bank_account", + "salary_slips_created", + "salary_slips_submitted", + "failure_details_section", + "error_message", + "section_break_41", + "amended_from" + ], + "fields": [ + { + "fieldname": "section_break0", + "fieldtype": "Section Break", + "label": "Select Employees" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 + }, + { + "depends_on": "eval:doc.salary_slip_based_on_timesheet == 0", + "fieldname": "payroll_frequency", + "fieldtype": "Select", + "label": "Payroll Frequency", + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", + "reqd": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Employees" + }, + { + "fieldname": "branch", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Branch", + "options": "Branch" + }, + { + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation" + }, + { + "fieldname": "number_of_employees", + "fieldtype": "Int", + "label": "Number Of Employees", + "read_only": 1 + }, + { + "fieldname": "sec_break20", + "fieldtype": "Section Break" + }, + { + "fieldname": "employees", + "fieldtype": "Table", + "label": "Employee Details", + "options": "Payroll Employee Detail" + }, + { + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "validate_attendance", + "fieldtype": "Check", + "label": "Validate Attendance" + }, + { + "fieldname": "attendance_detail_html", + "fieldtype": "HTML" + }, + { + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "salary_slip_based_on_timesheet", + "fieldtype": "Check", + "label": "Salary Slip Based on Timesheet" + }, + { + "fieldname": "select_payroll_period", + "fieldtype": "Section Break", + "label": "Select Payroll Period" + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "reqd": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "deduct_tax_for_unclaimed_employee_benefits", + "fieldtype": "Check", + "label": "Deduct Tax For Unclaimed Employee Benefits" + }, + { + "default": "0", + "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", + "fieldtype": "Check", + "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" + }, + { + "default": ":Company", + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center", + "reqd": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "account", + "fieldtype": "Section Break", + "label": "Payment Entry" + }, + { + "allow_on_submit": 1, + "description": "Select Payment Account to make Bank Entry", + "fetch_from": "bank_account.account", + "fieldname": "payment_account", + "fieldtype": "Link", + "label": "Payment Account", + "options": "Account" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Payroll Entry", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "salary_slips_created", + "fieldtype": "Check", + "hidden": 1, + "label": "Salary Slips Created", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "salary_slips_submitted", + "fieldtype": "Check", + "hidden": 1, + "label": "Salary Slips Submitted", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account" + }, + { + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, + { + "depends_on": "company", + "fieldname": "currency", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, + { + "depends_on": "company", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9", + "reqd": 1 + }, + { + "depends_on": "company", + "fieldname": "payroll_payable_account", + "fieldtype": "Link", + "label": "Payroll Payable Account", + "options": "Account", + "reqd": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "error_message", + "depends_on": "eval:doc.status=='Failed';", + "fieldname": "failure_details_section", + "fieldtype": "Section Break", + "label": "Failure Details" + }, + { + "depends_on": "eval:doc.status=='Failed';", + "fieldname": "error_message", + "fieldtype": "Small Text", + "label": "Error Message", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_41", + "fieldtype": "Section Break" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Draft\nSubmitted\nCancelled\nQueued\nFailed", + "print_hide": 1, + "read_only": 1 + } + ], + "icon": "fa fa-cog", + "is_submittable": 1, + "links": [], + "modified": "2022-03-16 12:45:21.662765", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Entry", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/hrms/payroll/doctype/payroll_entry/payroll_entry.py b/hrms/payroll/doctype/payroll_entry/payroll_entry.py new file mode 100644 index 0000000..c7c2c8b --- /dev/null +++ b/hrms/payroll/doctype/payroll_entry/payroll_entry.py @@ -0,0 +1,1063 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import json + +from dateutil.relativedelta import relativedelta + +import frappe +from frappe import _ +from frappe.desk.reportview import get_filters_cond, get_match_cond +from frappe.model.document import Document +from frappe.query_builder.functions import Coalesce +from frappe.utils import ( + DATE_FORMAT, + add_days, + add_to_date, + cint, + comma_and, + date_diff, + flt, + get_link_to_form, + getdate, +) + +import erpnext +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) +from erpnext.accounts.utils import get_fiscal_year +from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee + + +class PayrollEntry(Document): + def onload(self): + if not self.docstatus == 1 or self.salary_slips_submitted: + return + + # check if salary slips were manually submitted + entries = frappe.db.count("Salary Slip", {"payroll_entry": self.name, "docstatus": 1}, ["name"]) + if cint(entries) == len(self.employees): + self.set_onload("submitted_ss", True) + + def validate(self): + self.number_of_employees = len(self.employees) + self.set_status() + + def on_submit(self): + self.set_status(update=True, status="Submitted") + self.create_salary_slips() + + def before_submit(self): + self.validate_employee_details() + self.validate_payroll_payable_account() + if self.validate_attendance: + if self.validate_employee_attendance(): + frappe.throw(_("Cannot Submit, Employees left to mark attendance")) + + def set_status(self, status=None, update=False): + if not status: + status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0] + + if update: + self.db_set("status", status) + else: + self.status = status + + def validate_employee_details(self): + emp_with_sal_slip = [] + for employee_details in self.employees: + if frappe.db.exists( + "Salary Slip", + { + "employee": employee_details.employee, + "start_date": self.start_date, + "end_date": self.end_date, + "docstatus": 1, + }, + ): + emp_with_sal_slip.append(employee_details.employee) + + if len(emp_with_sal_slip): + frappe.throw(_("Salary Slip already exists for {0}").format(comma_and(emp_with_sal_slip))) + + def validate_payroll_payable_account(self): + if frappe.db.get_value("Account", self.payroll_payable_account, "account_type"): + frappe.throw( + _( + "Account type cannot be set for payroll payable account {0}, please remove and try again" + ).format(frappe.bold(get_link_to_form("Account", self.payroll_payable_account))) + ) + + def on_cancel(self): + frappe.delete_doc( + "Salary Slip", + frappe.db.sql_list( + """select name from `tabSalary Slip` + where payroll_entry=%s """, + (self.name), + ), + ) + self.db_set("salary_slips_created", 0) + self.db_set("salary_slips_submitted", 0) + self.set_status(update=True, status="Cancelled") + self.db_set("error_message", "") + + def get_emp_list(self): + """ + Returns list of active employees based on selected criteria + and for which salary structure exists + """ + self.check_mandatory() + filters = self.make_filters() + cond = get_filter_condition(filters) + cond += get_joining_relieving_condition(self.start_date, self.end_date) + + condition = "" + if self.payroll_frequency: + condition = """and payroll_frequency = '%(payroll_frequency)s'""" % { + "payroll_frequency": self.payroll_frequency + } + + sal_struct = get_sal_struct( + self.company, self.currency, self.salary_slip_based_on_timesheet, condition + ) + if sal_struct: + cond += "and t2.salary_structure IN %(sal_struct)s " + cond += "and t2.payroll_payable_account = %(payroll_payable_account)s " + cond += "and %(from_date)s >= t2.from_date" + emp_list = get_emp_list(sal_struct, cond, self.end_date, self.payroll_payable_account) + emp_list = remove_payrolled_employees(emp_list, self.start_date, self.end_date) + return emp_list + + def make_filters(self): + filters = frappe._dict() + filters["company"] = self.company + filters["branch"] = self.branch + filters["department"] = self.department + filters["designation"] = self.designation + + return filters + + @frappe.whitelist() + def fill_employee_details(self): + self.set("employees", []) + employees = self.get_emp_list() + if not employees: + error_msg = _( + "No employees found for the mentioned criteria:
Company: {0}
Currency: {1}
Payroll Payable Account: {2}" + ).format( + frappe.bold(self.company), + frappe.bold(self.currency), + frappe.bold(self.payroll_payable_account), + ) + if self.branch: + error_msg += "
" + _("Branch: {0}").format(frappe.bold(self.branch)) + if self.department: + error_msg += "
" + _("Department: {0}").format(frappe.bold(self.department)) + if self.designation: + error_msg += "
" + _("Designation: {0}").format(frappe.bold(self.designation)) + if self.start_date: + error_msg += "
" + _("Start date: {0}").format(frappe.bold(self.start_date)) + if self.end_date: + error_msg += "
" + _("End date: {0}").format(frappe.bold(self.end_date)) + frappe.throw(error_msg, title=_("No employees found")) + + for d in employees: + self.append("employees", d) + + self.number_of_employees = len(self.employees) + if self.validate_attendance: + return self.validate_employee_attendance() + + def check_mandatory(self): + for fieldname in ["company", "start_date", "end_date"]: + if not self.get(fieldname): + frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname))) + + @frappe.whitelist() + def create_salary_slips(self): + """ + Creates salary slip for selected employees if already not created + """ + self.check_permission("write") + employees = [emp.employee for emp in self.employees] + if employees: + args = frappe._dict( + { + "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet, + "payroll_frequency": self.payroll_frequency, + "start_date": self.start_date, + "end_date": self.end_date, + "company": self.company, + "posting_date": self.posting_date, + "deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits, + "deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof, + "payroll_entry": self.name, + "exchange_rate": self.exchange_rate, + "currency": self.currency, + } + ) + if len(employees) > 30 or frappe.flags.enqueue_payroll_entry: + self.db_set("status", "Queued") + frappe.enqueue( + create_salary_slips_for_employees, + timeout=600, + employees=employees, + args=args, + publish_progress=False, + ) + frappe.msgprint( + _("Salary Slip creation is queued. It may take a few minutes"), + alert=True, + indicator="blue", + ) + else: + create_salary_slips_for_employees(employees, args, publish_progress=False) + # since this method is called via frm.call this doc needs to be updated manually + self.reload() + + def get_sal_slip_list(self, ss_status, as_dict=False): + """ + Returns list of salary slips based on selected criteria + """ + + ss = frappe.qb.DocType("Salary Slip") + ss_list = ( + frappe.qb.from_(ss) + .select(ss.name, ss.salary_structure) + .where( + (ss.docstatus == ss_status) + & (ss.start_date >= self.start_date) + & (ss.end_date <= self.end_date) + & (ss.payroll_entry == self.name) + & ((ss.journal_entry.isnull()) | (ss.journal_entry == "")) + & (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet) + ) + ).run(as_dict=as_dict) + + return ss_list + + @frappe.whitelist() + def submit_salary_slips(self): + self.check_permission("write") + salary_slips = self.get_sal_slip_list(ss_status=0) + if len(salary_slips) > 30 or frappe.flags.enqueue_payroll_entry: + self.db_set("status", "Queued") + frappe.enqueue( + submit_salary_slips_for_employees, + timeout=600, + payroll_entry=self, + salary_slips=salary_slips, + publish_progress=False, + ) + frappe.msgprint( + _("Salary Slip submission is queued. It may take a few minutes"), + alert=True, + indicator="blue", + ) + else: + submit_salary_slips_for_employees(self, salary_slips, publish_progress=False) + + def email_salary_slip(self, submitted_ss): + if frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee"): + for ss in submitted_ss: + ss.email_salary_slip() + + def get_salary_component_account(self, salary_component): + account = frappe.db.get_value( + "Salary Component Account", {"parent": salary_component, "company": self.company}, "account" + ) + + if not account: + frappe.throw( + _("Please set account in Salary Component {0}").format( + get_link_to_form("Salary Component", salary_component) + ) + ) + + return account + + def get_salary_components(self, component_type): + salary_slips = self.get_sal_slip_list(ss_status=1, as_dict=True) + + if salary_slips: + ss = frappe.qb.DocType("Salary Slip") + ssd = frappe.qb.DocType("Salary Detail") + salary_components = ( + frappe.qb.from_(ss) + .join(ssd) + .on(ss.name == ssd.parent) + .select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee) + .where( + (ssd.parentfield == component_type) & (ss.name.isin(tuple([d.name for d in salary_slips]))) + ) + ).run(as_dict=True) + + return salary_components + + def get_salary_component_total(self, component_type=None): + salary_components = self.get_salary_components(component_type) + if salary_components: + component_dict = {} + self.employee_cost_centers = {} + for item in salary_components: + employee_cost_centers = self.get_payroll_cost_centers_for_employee( + item.employee, item.salary_structure + ) + + add_component_to_accrual_jv_entry = True + if component_type == "earnings": + is_flexible_benefit, only_tax_impact = frappe.get_cached_value( + "Salary Component", item["salary_component"], ["is_flexible_benefit", "only_tax_impact"] + ) + if is_flexible_benefit == 1 and only_tax_impact == 1: + add_component_to_accrual_jv_entry = False + + if add_component_to_accrual_jv_entry: + for cost_center, percentage in employee_cost_centers.items(): + amount_against_cost_center = flt(item.amount) * percentage / 100 + component_dict[(item.salary_component, cost_center)] = ( + component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center + ) + + account_details = self.get_account(component_dict=component_dict) + return account_details + + def get_payroll_cost_centers_for_employee(self, employee, salary_structure): + if not self.employee_cost_centers.get(employee): + ss_assignment_name = frappe.db.get_value( + "Salary Structure Assignment", + {"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, + "name", + ) + + if ss_assignment_name: + cost_centers = dict( + frappe.get_all( + "Employee Cost Center", + {"parent": ss_assignment_name}, + ["cost_center", "percentage"], + as_list=1, + ) + ) + if not cost_centers: + default_cost_center, department = frappe.get_cached_value( + "Employee", employee, ["payroll_cost_center", "department"] + ) + if not default_cost_center and department: + default_cost_center = frappe.get_cached_value( + "Department", department, "payroll_cost_center" + ) + if not default_cost_center: + default_cost_center = self.cost_center + + cost_centers = {default_cost_center: 100} + + self.employee_cost_centers.setdefault(employee, cost_centers) + + return self.employee_cost_centers.get(employee, {}) + + def get_account(self, component_dict=None): + account_dict = {} + for key, amount in component_dict.items(): + account = self.get_salary_component_account(key[0]) + account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount + return account_dict + + def make_accrual_jv_entry(self): + self.check_permission("write") + earnings = self.get_salary_component_total(component_type="earnings") or {} + deductions = self.get_salary_component_total(component_type="deductions") or {} + payroll_payable_account = self.payroll_payable_account + jv_name = "" + precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") + + if earnings or deductions: + journal_entry = frappe.new_doc("Journal Entry") + journal_entry.voucher_type = "Journal Entry" + journal_entry.user_remark = _("Accrual Journal Entry for salaries from {0} to {1}").format( + self.start_date, self.end_date + ) + journal_entry.company = self.company + journal_entry.posting_date = self.posting_date + accounting_dimensions = get_accounting_dimensions() or [] + + accounts = [] + currencies = [] + payable_amount = 0 + multi_currency = 0 + company_currency = erpnext.get_company_currency(self.company) + + # Earnings + for acc_cc, amount in earnings.items(): + exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry( + acc_cc[0], amount, company_currency, currencies + ) + payable_amount += flt(amount, precision) + accounts.append( + self.update_accounting_dimensions( + { + "account": acc_cc[0], + "debit_in_account_currency": flt(amt, precision), + "exchange_rate": flt(exchange_rate), + "cost_center": acc_cc[1] or self.cost_center, + "project": self.project, + }, + accounting_dimensions, + ) + ) + + # Deductions + for acc_cc, amount in deductions.items(): + exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry( + acc_cc[0], amount, company_currency, currencies + ) + payable_amount -= flt(amount, precision) + accounts.append( + self.update_accounting_dimensions( + { + "account": acc_cc[0], + "credit_in_account_currency": flt(amt, precision), + "exchange_rate": flt(exchange_rate), + "cost_center": acc_cc[1] or self.cost_center, + "project": self.project, + }, + accounting_dimensions, + ) + ) + + # Payable amount + exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry( + payroll_payable_account, payable_amount, company_currency, currencies + ) + accounts.append( + self.update_accounting_dimensions( + { + "account": payroll_payable_account, + "credit_in_account_currency": flt(payable_amt, precision), + "exchange_rate": flt(exchange_rate), + "cost_center": self.cost_center, + }, + accounting_dimensions, + ) + ) + + journal_entry.set("accounts", accounts) + if len(currencies) > 1: + multi_currency = 1 + journal_entry.multi_currency = multi_currency + journal_entry.title = payroll_payable_account + journal_entry.save() + + try: + journal_entry.submit() + jv_name = journal_entry.name + self.update_salary_slip_status(jv_name=jv_name) + except Exception as e: + if type(e) in (str, list, tuple): + frappe.msgprint(e) + raise + + return jv_name + + def update_accounting_dimensions(self, row, accounting_dimensions): + for dimension in accounting_dimensions: + row.update({dimension: self.get(dimension)}) + + return row + + def get_amount_and_exchange_rate_for_journal_entry( + self, account, amount, company_currency, currencies + ): + conversion_rate = 1 + exchange_rate = self.exchange_rate + account_currency = frappe.db.get_value("Account", account, "account_currency") + if account_currency not in currencies: + currencies.append(account_currency) + if account_currency == company_currency: + conversion_rate = self.exchange_rate + exchange_rate = 1 + amount = flt(amount) * flt(conversion_rate) + return exchange_rate, amount + + @frappe.whitelist() + def make_payment_entry(self): + self.check_permission("write") + + salary_slip_name_list = frappe.db.sql( + """ select t1.name from `tabSalary Slip` t1 + where t1.docstatus = 1 and start_date >= %s and end_date <= %s and t1.payroll_entry = %s + """, + (self.start_date, self.end_date, self.name), + as_list=True, + ) + + if salary_slip_name_list and len(salary_slip_name_list) > 0: + salary_slip_total = 0 + for salary_slip_name in salary_slip_name_list: + salary_slip = frappe.get_doc("Salary Slip", salary_slip_name[0]) + for sal_detail in salary_slip.earnings: + ( + is_flexible_benefit, + only_tax_impact, + creat_separate_je, + statistical_component, + ) = frappe.db.get_value( + "Salary Component", + sal_detail.salary_component, + [ + "is_flexible_benefit", + "only_tax_impact", + "create_separate_payment_entry_against_benefit_claim", + "statistical_component", + ], + ) + if only_tax_impact != 1 and statistical_component != 1: + if is_flexible_benefit == 1 and creat_separate_je == 1: + self.create_journal_entry(sal_detail.amount, sal_detail.salary_component) + else: + salary_slip_total += sal_detail.amount + for sal_detail in salary_slip.deductions: + statistical_component = frappe.db.get_value( + "Salary Component", sal_detail.salary_component, "statistical_component" + ) + if statistical_component != 1: + salary_slip_total -= sal_detail.amount + if salary_slip_total > 0: + self.create_journal_entry(salary_slip_total, "salary") + + def create_journal_entry(self, je_payment_amount, user_remark): + payroll_payable_account = self.payroll_payable_account + precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") + + accounts = [] + currencies = [] + multi_currency = 0 + company_currency = erpnext.get_company_currency(self.company) + accounting_dimensions = get_accounting_dimensions() or [] + + exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry( + self.payment_account, je_payment_amount, company_currency, currencies + ) + accounts.append( + self.update_accounting_dimensions( + { + "account": self.payment_account, + "bank_account": self.bank_account, + "credit_in_account_currency": flt(amount, precision), + "exchange_rate": flt(exchange_rate), + }, + accounting_dimensions, + ) + ) + + exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry( + payroll_payable_account, je_payment_amount, company_currency, currencies + ) + accounts.append( + self.update_accounting_dimensions( + { + "account": payroll_payable_account, + "debit_in_account_currency": flt(amount, precision), + "exchange_rate": flt(exchange_rate), + "reference_type": self.doctype, + "reference_name": self.name, + }, + accounting_dimensions, + ) + ) + + if len(currencies) > 1: + multi_currency = 1 + + journal_entry = frappe.new_doc("Journal Entry") + journal_entry.voucher_type = "Bank Entry" + journal_entry.user_remark = _("Payment of {0} from {1} to {2}").format( + user_remark, self.start_date, self.end_date + ) + journal_entry.company = self.company + journal_entry.posting_date = self.posting_date + journal_entry.multi_currency = multi_currency + + journal_entry.set("accounts", accounts) + journal_entry.save(ignore_permissions=True) + + def update_salary_slip_status(self, jv_name=None): + ss_list = self.get_sal_slip_list(ss_status=1) + for ss in ss_list: + ss_obj = frappe.get_doc("Salary Slip", ss[0]) + frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name) + + def set_start_end_dates(self): + self.update( + get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date, self.company) + ) + + @frappe.whitelist() + def validate_employee_attendance(self): + employees_to_mark_attendance = [] + days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0 + for employee_detail in self.employees: + employee_joining_date = frappe.db.get_value( + "Employee", employee_detail.employee, "date_of_joining" + ) + start_date = self.start_date + if employee_joining_date > getdate(self.start_date): + start_date = employee_joining_date + days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date) + days_attendance_marked = self.get_count_employee_attendance( + employee_detail.employee, start_date + ) + days_in_payroll = date_diff(self.end_date, start_date) + 1 + if days_in_payroll > days_holiday + days_attendance_marked: + employees_to_mark_attendance.append( + {"employee": employee_detail.employee, "employee_name": employee_detail.employee_name} + ) + return employees_to_mark_attendance + + def get_count_holidays_of_employee(self, employee, start_date): + holiday_list = get_holiday_list_for_employee(employee) + holidays = 0 + if holiday_list: + days = frappe.db.sql( + """select count(*) from tabHoliday where + parent=%s and holiday_date between %s and %s""", + (holiday_list, start_date, self.end_date), + ) + if days and days[0][0]: + holidays = days[0][0] + return holidays + + def get_count_employee_attendance(self, employee, start_date): + marked_days = 0 + attendances = frappe.get_all( + "Attendance", + fields=["count(*)"], + filters={"employee": employee, "attendance_date": ("between", [start_date, self.end_date])}, + as_list=1, + ) + if attendances and attendances[0][0]: + marked_days = attendances[0][0] + return marked_days + + +def get_sal_struct( + company: str, currency: str, salary_slip_based_on_timesheet: int, condition: str +): + return frappe.db.sql_list( + """ + select + name from `tabSalary Structure` + where + docstatus = 1 and + is_active = 'Yes' + and company = %(company)s + and currency = %(currency)s and + ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s + {condition}""".format( + condition=condition + ), + { + "company": company, + "currency": currency, + "salary_slip_based_on_timesheet": salary_slip_based_on_timesheet, + }, + ) + + +def get_filter_condition(filters): + cond = "" + for f in ["company", "branch", "department", "designation"]: + if filters.get(f): + cond += " and t1." + f + " = " + frappe.db.escape(filters.get(f)) + + return cond + + +def get_joining_relieving_condition(start_date, end_date): + cond = """ + and ifnull(t1.date_of_joining, '1900-01-01') <= '%(end_date)s' + and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s' + """ % { + "start_date": start_date, + "end_date": end_date, + } + return cond + + +def get_emp_list(sal_struct, cond, end_date, payroll_payable_account): + return frappe.db.sql( + """ + select + distinct t1.name as employee, t1.employee_name, t1.department, t1.designation + from + `tabEmployee` t1, `tabSalary Structure Assignment` t2 + where + t1.name = t2.employee + and t2.docstatus = 1 + and t1.status != 'Inactive' + %s order by t2.from_date desc + """ + % cond, + { + "sal_struct": tuple(sal_struct), + "from_date": end_date, + "payroll_payable_account": payroll_payable_account, + }, + as_dict=True, + ) + + +def remove_payrolled_employees(emp_list, start_date, end_date): + new_emp_list = [] + for employee_details in emp_list: + if not frappe.db.exists( + "Salary Slip", + { + "employee": employee_details.employee, + "start_date": start_date, + "end_date": end_date, + "docstatus": 1, + }, + ): + new_emp_list.append(employee_details) + + return new_emp_list + + +@frappe.whitelist() +def get_start_end_dates(payroll_frequency, start_date=None, company=None): + """Returns dict of start and end dates for given payroll frequency based on start_date""" + + if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly" or payroll_frequency == "": + fiscal_year = get_fiscal_year(start_date, company=company)[0] + month = "%02d" % getdate(start_date).month + m = get_month_details(fiscal_year, month) + if payroll_frequency == "Bimonthly": + if getdate(start_date).day <= 15: + start_date = m["month_start_date"] + end_date = m["month_mid_end_date"] + else: + start_date = m["month_mid_start_date"] + end_date = m["month_end_date"] + else: + start_date = m["month_start_date"] + end_date = m["month_end_date"] + + if payroll_frequency == "Weekly": + end_date = add_days(start_date, 6) + + if payroll_frequency == "Fortnightly": + end_date = add_days(start_date, 13) + + if payroll_frequency == "Daily": + end_date = start_date + + return frappe._dict({"start_date": start_date, "end_date": end_date}) + + +def get_frequency_kwargs(frequency_name): + frequency_dict = { + "monthly": {"months": 1}, + "fortnightly": {"days": 14}, + "weekly": {"days": 7}, + "daily": {"days": 1}, + } + return frequency_dict.get(frequency_name) + + +@frappe.whitelist() +def get_end_date(start_date, frequency): + start_date = getdate(start_date) + frequency = frequency.lower() if frequency else "monthly" + kwargs = ( + get_frequency_kwargs(frequency) if frequency != "bimonthly" else get_frequency_kwargs("monthly") + ) + + # weekly, fortnightly and daily intervals have fixed days so no problems + end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1) + if frequency != "bimonthly": + return dict(end_date=end_date.strftime(DATE_FORMAT)) + + else: + return dict(end_date="") + + +def get_month_details(year, month): + ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date") + if ysd: + import calendar + import datetime + + diff_mnt = cint(month) - cint(ysd.month) + if diff_mnt < 0: + diff_mnt = 12 - int(ysd.month) + cint(month) + msd = ysd + relativedelta(months=diff_mnt) # month start date + month_days = cint(calendar.monthrange(cint(msd.year), cint(month))[1]) # days in month + mid_start = datetime.date(msd.year, cint(month), 16) # month mid start date + mid_end = datetime.date(msd.year, cint(month), 15) # month mid end date + med = datetime.date(msd.year, cint(month), month_days) # month end date + return frappe._dict( + { + "year": msd.year, + "month_start_date": msd, + "month_end_date": med, + "month_mid_start_date": mid_start, + "month_mid_end_date": mid_end, + "month_days": month_days, + } + ) + else: + frappe.throw(_("Fiscal Year {0} not found").format(year)) + + +def get_payroll_entry_bank_entries(payroll_entry_name): + journal_entries = frappe.db.sql( + "select name from `tabJournal Entry Account` " + 'where reference_type="Payroll Entry" ' + "and reference_name=%s and docstatus=1", + payroll_entry_name, + as_dict=1, + ) + + return journal_entries + + +@frappe.whitelist() +def payroll_entry_has_bank_entries(name): + response = {} + bank_entries = get_payroll_entry_bank_entries(name) + response["submitted"] = 1 if bank_entries else 0 + + return response + + +def log_payroll_failure(process, payroll_entry, error): + error_log = frappe.log_error( + title=_("Salary Slip {0} failed for Payroll Entry {1}").format(process, payroll_entry.name) + ) + message_log = frappe.message_log.pop() if frappe.message_log else str(error) + + try: + error_message = json.loads(message_log).get("message") + except Exception: + error_message = message_log + + error_message += "\n" + _("Check Error Log {0} for more details.").format( + get_link_to_form("Error Log", error_log.name) + ) + + payroll_entry.db_set({"error_message": error_message, "status": "Failed"}) + + +def create_salary_slips_for_employees(employees, args, publish_progress=True): + try: + payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry) + salary_slips_exist_for = get_existing_salary_slips(employees, args) + count = 0 + + for emp in employees: + if emp not in salary_slips_exist_for: + args.update({"doctype": "Salary Slip", "employee": emp}) + frappe.get_doc(args).insert() + + count += 1 + if publish_progress: + frappe.publish_progress( + count * 100 / len(set(employees) - set(salary_slips_exist_for)), + title=_("Creating Salary Slips..."), + ) + + payroll_entry.db_set({"status": "Submitted", "salary_slips_created": 1, "error_message": ""}) + + if salary_slips_exist_for: + frappe.msgprint( + _( + "Salary Slips already exist for employees {}, and will not be processed by this payroll." + ).format(frappe.bold(", ".join(emp for emp in salary_slips_exist_for))), + title=_("Message"), + indicator="orange", + ) + + except Exception as e: + frappe.db.rollback() + log_payroll_failure("creation", payroll_entry, e) + + finally: + frappe.db.commit() # nosemgrep + frappe.publish_realtime("completed_salary_slip_creation") + + +def show_payroll_submission_status(submitted, unsubmitted, payroll_entry): + if not submitted and not unsubmitted: + frappe.msgprint( + _( + "No salary slip found to submit for the above selected criteria OR salary slip already submitted" + ) + ) + elif submitted and not unsubmitted: + frappe.msgprint( + _("Salary Slips submitted for period from {0} to {1}").format( + payroll_entry.start_date, payroll_entry.end_date + ) + ) + elif unsubmitted: + frappe.msgprint( + _("Could not submit some Salary Slips: {}").format( + ", ".join(get_link_to_form("Salary Slip", entry) for entry in unsubmitted) + ) + ) + + +def get_existing_salary_slips(employees, args): + return frappe.db.sql_list( + """ + select distinct employee from `tabSalary Slip` + where docstatus!= 2 and company = %s and payroll_entry = %s + and start_date >= %s and end_date <= %s + and employee in (%s) + """ + % ("%s", "%s", "%s", "%s", ", ".join(["%s"] * len(employees))), + [args.company, args.payroll_entry, args.start_date, args.end_date] + employees, + ) + + +def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True): + try: + submitted = [] + unsubmitted = [] + frappe.flags.via_payroll_entry = True + count = 0 + + for entry in salary_slips: + salary_slip = frappe.get_doc("Salary Slip", entry[0]) + if salary_slip.net_pay < 0: + unsubmitted.append(entry[0]) + else: + try: + salary_slip.submit() + submitted.append(salary_slip) + except frappe.ValidationError: + unsubmitted.append(entry[0]) + + count += 1 + if publish_progress: + frappe.publish_progress(count * 100 / len(salary_slips), title=_("Submitting Salary Slips...")) + + if submitted: + payroll_entry.make_accrual_jv_entry() + payroll_entry.email_salary_slip(submitted) + payroll_entry.db_set({"salary_slips_submitted": 1, "status": "Submitted", "error_message": ""}) + + show_payroll_submission_status(submitted, unsubmitted, payroll_entry) + + except Exception as e: + frappe.db.rollback() + log_payroll_failure("submission", payroll_entry, e) + + finally: + frappe.db.commit() # nosemgrep + frappe.publish_realtime("completed_salary_slip_submission") + + frappe.flags.via_payroll_entry = False + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters): + return frappe.db.sql( + """ + select name from `tabPayroll Entry` + where `{key}` LIKE %(txt)s + and name not in + (select reference_name from `tabJournal Entry Account` + where reference_type="Payroll Entry") + order by name limit %(start)s, %(page_len)s""".format( + key=searchfield + ), + {"txt": "%%%s%%" % txt, "start": start, "page_len": page_len}, + ) + + +def get_employee_list(filters: frappe._dict) -> list[str]: + condition = f"and payroll_frequency = '{filters.payroll_frequency}'" + + sal_struct = get_sal_struct( + filters.company, filters.currency, filters.salary_slip_based_on_timesheet, condition + ) + + if not sal_struct: + return [] + + cond = ( + get_filter_condition(filters) + + get_joining_relieving_condition(filters.start_date, filters.end_date) + + ( + "and t2.salary_structure IN %(sal_struct)s " + "and t2.payroll_payable_account = %(payroll_payable_account)s " + "and %(from_date)s >= t2.from_date" + ) + ) + emp_list = get_emp_list(sal_struct, cond, filters.end_date, filters.payroll_payable_account) + return remove_payrolled_employees(emp_list, filters.start_date, filters.end_date) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def employee_query(doctype, txt, searchfield, start, page_len, filters): + filters = frappe._dict(filters) + conditions = [] + include_employees = [] + emp_cond = "" + + if not filters.payroll_frequency: + frappe.throw(_("Select Payroll Frequency.")) + + if filters.start_date and filters.end_date: + employee_list = get_employee_list(filters) + emp = filters.get("employees") or [] + include_employees = [ + employee.employee for employee in employee_list if employee.employee not in emp + ] + filters.pop("start_date") + filters.pop("end_date") + filters.pop("salary_slip_based_on_timesheet") + filters.pop("payroll_frequency") + filters.pop("payroll_payable_account") + filters.pop("currency") + if filters.employees is not None: + filters.pop("employees") + + if include_employees: + emp_cond += "and employee in %(include_employees)s" + + return frappe.db.sql( + """select name, employee_name from `tabEmployee` + where status = 'Active' + and docstatus < 2 + and ({key} like %(txt)s + or employee_name like %(txt)s) + {emp_cond} + {fcond} {mcond} + order by + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), + if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999), + idx desc, + name, employee_name + limit %(start)s, %(page_len)s""".format( + **{ + "key": searchfield, + "fcond": get_filters_cond(doctype, filters, conditions), + "mcond": get_match_cond(doctype), + "emp_cond": emp_cond, + } + ), + { + "txt": "%%%s%%" % txt, + "_txt": txt.replace("%", ""), + "start": start, + "page_len": page_len, + "include_employees": include_employees, + }, + ) diff --git a/hrms/payroll/doctype/payroll_entry/payroll_entry_dashboard.py b/hrms/payroll/doctype/payroll_entry/payroll_entry_dashboard.py new file mode 100644 index 0000000..eb93d68 --- /dev/null +++ b/hrms/payroll/doctype/payroll_entry/payroll_entry_dashboard.py @@ -0,0 +1,9 @@ +def get_data(): + return { + "fieldname": "payroll_entry", + "non_standard_fieldnames": { + "Journal Entry": "reference_name", + "Payment Entry": "reference_name", + }, + "transactions": [{"items": ["Salary Slip", "Journal Entry"]}], + } diff --git a/hrms/payroll/doctype/payroll_entry/payroll_entry_list.js b/hrms/payroll/doctype/payroll_entry/payroll_entry_list.js new file mode 100644 index 0000000..56390b7 --- /dev/null +++ b/hrms/payroll/doctype/payroll_entry/payroll_entry_list.js @@ -0,0 +1,18 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +// render +frappe.listview_settings['Payroll Entry'] = { + has_indicator_for_draft: 1, + get_indicator: function(doc) { + var status_color = { + 'Draft': 'red', + 'Submitted': 'blue', + 'Queued': 'orange', + 'Failed': 'red', + 'Cancelled': 'red' + + }; + return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status]; + } +}; diff --git a/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py b/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py new file mode 100644 index 0000000..a50a395 --- /dev/null +++ b/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -0,0 +1,490 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import unittest + +from dateutil.relativedelta import relativedelta + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_months + +import erpnext +from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate +from erpnext.loan_management.doctype.loan.test_loan import ( + create_loan, + create_loan_accounts, + create_loan_type, + make_loan_disbursement_entry, +) +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( + process_loan_interest_accrual_for_term_loans, +) +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.payroll.doctype.payroll_entry.payroll_entry import ( + PayrollEntry, + get_end_date, + get_start_end_dates, +) +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + create_account, + make_deduction_salary_component, + make_earning_salary_component, + set_salary_component_account, +) +from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + make_salary_structure, +) + +test_dependencies = ["Holiday List"] + + +class TestPayrollEntry(FrappeTestCase): + def setUp(self): + for dt in [ + "Salary Slip", + "Salary Component", + "Salary Component Account", + "Payroll Entry", + "Salary Structure", + "Salary Structure Assignment", + "Payroll Employee Detail", + "Additional Salary", + "Loan", + ]: + frappe.db.delete(dt) + + make_earning_salary_component(setup=True, company_list=["_Test Company"]) + make_deduction_salary_component(setup=True, test_tax=False, company_list=["_Test Company"]) + + frappe.db.set_value("Company", "_Test Company", "default_holiday_list", "_Test Holiday List") + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) + + # set default payable account + default_account = frappe.db.get_value( + "Company", "_Test Company", "default_payroll_payable_account" + ) + if not default_account or default_account != "_Test Payroll Payable - _TC": + create_account( + account_name="_Test Payroll Payable", + company="_Test Company", + parent_account="Current Liabilities - _TC", + account_type="Payable", + ) + frappe.db.set_value( + "Company", "_Test Company", "default_payroll_payable_account", "_Test Payroll Payable - _TC" + ) + + def test_payroll_entry(self): + company = frappe.get_doc("Company", "_Test Company") + employee = frappe.db.get_value("Employee", {"company": "_Test Company"}) + setup_salary_structure(employee, company) + + dates = get_start_end_dates("Monthly", nowdate()) + make_payroll_entry( + start_date=dates.start_date, + end_date=dates.end_date, + payable_account=company.default_payroll_payable_account, + currency=company.default_currency, + company=company.name, + ) + + def test_multi_currency_payroll_entry(self): + company = frappe.get_doc("Company", "_Test Company") + employee = make_employee( + "test_muti_currency_employee@payroll.com", company=company.name, department="Accounts - _TC" + ) + salary_structure = "_Test Multi Currency Salary Structure" + setup_salary_structure(employee, company, "USD", salary_structure) + + dates = get_start_end_dates("Monthly", nowdate()) + payroll_entry = make_payroll_entry( + start_date=dates.start_date, + end_date=dates.end_date, + payable_account=company.default_payroll_payable_account, + currency="USD", + exchange_rate=70, + company=company.name, + cost_center="Main - _TC", + ) + payroll_entry.make_payment_entry() + + salary_slip = frappe.db.get_value("Salary Slip", {"payroll_entry": payroll_entry.name}, "name") + salary_slip = frappe.get_doc("Salary Slip", salary_slip) + + payroll_entry.reload() + payroll_je = salary_slip.journal_entry + if payroll_je: + payroll_je_doc = frappe.get_doc("Journal Entry", payroll_je) + self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit) + self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit) + + payment_entry = frappe.db.sql( + """ + Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea + Where je.name = jea.parent + And jea.reference_name = %s + """, + (payroll_entry.name), + as_dict=1, + ) + self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_debit) + self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_credit) + + def test_payroll_entry_with_employee_cost_center(self): + if not frappe.db.exists("Department", "cc - _TC"): + frappe.get_doc( + {"doctype": "Department", "department_name": "cc", "company": "_Test Company"} + ).insert() + + employee1 = make_employee( + "test_employee1@example.com", + payroll_cost_center="_Test Cost Center - _TC", + department="cc - _TC", + company="_Test Company", + ) + employee2 = make_employee( + "test_employee2@example.com", department="cc - _TC", company="_Test Company" + ) + + company = frappe.get_doc("Company", "_Test Company") + setup_salary_structure(employee1, company) + + ss = make_salary_structure( + "_Test Salary Structure 2", + "Monthly", + employee2, + company="_Test Company", + currency=company.default_currency, + test_tax=False, + ) + + # update cost centers in salary structure assignment for employee2 + ssa = frappe.db.get_value( + "Salary Structure Assignment", + {"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, + "name", + ) + + ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa) + ssa_doc.payroll_cost_centers = [] + ssa_doc.append( + "payroll_cost_centers", {"cost_center": "_Test Cost Center - _TC", "percentage": 60} + ) + ssa_doc.append( + "payroll_cost_centers", {"cost_center": "_Test Cost Center 2 - _TC", "percentage": 40} + ) + ssa_doc.save() + + dates = get_start_end_dates("Monthly", nowdate()) + pe = make_payroll_entry( + start_date=dates.start_date, + end_date=dates.end_date, + payable_account="_Test Payroll Payable - _TC", + currency=frappe.db.get_value("Company", "_Test Company", "default_currency"), + department="cc - _TC", + company="_Test Company", + payment_account="Cash - _TC", + cost_center="Main - _TC", + ) + je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry") + je_entries = frappe.db.sql( + """ + select account, cost_center, debit, credit + from `tabJournal Entry Account` + where parent=%s + order by account, cost_center + """, + je, + ) + expected_je = ( + ("_Test Payroll Payable - _TC", "Main - _TC", 0.0, 155600.0), + ("Salary - _TC", "_Test Cost Center - _TC", 124800.0, 0.0), + ("Salary - _TC", "_Test Cost Center 2 - _TC", 31200.0, 0.0), + ("Salary Deductions - _TC", "_Test Cost Center - _TC", 0.0, 320.0), + ("Salary Deductions - _TC", "_Test Cost Center 2 - _TC", 0.0, 80.0), + ) + + self.assertEqual(je_entries, expected_je) + + def test_get_end_date(self): + self.assertEqual(get_end_date("2017-01-01", "monthly"), {"end_date": "2017-01-31"}) + self.assertEqual(get_end_date("2017-02-01", "monthly"), {"end_date": "2017-02-28"}) + self.assertEqual(get_end_date("2017-02-01", "fortnightly"), {"end_date": "2017-02-14"}) + self.assertEqual(get_end_date("2017-02-01", "bimonthly"), {"end_date": ""}) + self.assertEqual(get_end_date("2017-01-01", "bimonthly"), {"end_date": ""}) + self.assertEqual(get_end_date("2020-02-15", "bimonthly"), {"end_date": ""}) + self.assertEqual(get_end_date("2017-02-15", "monthly"), {"end_date": "2017-03-14"}) + self.assertEqual(get_end_date("2017-02-15", "daily"), {"end_date": "2017-02-15"}) + + def test_loan(self): + company = "_Test Company" + branch = "Test Employee Branch" + + if not frappe.db.exists("Branch", branch): + frappe.get_doc({"doctype": "Branch", "branch": branch}).insert() + holiday_list = make_holiday("test holiday for loan") + + applicant = make_employee( + "test_employee@loan.com", company="_Test Company", branch=branch, holiday_list=holiday_list + ) + company_doc = frappe.get_doc("Company", company) + + make_salary_structure( + "Test Salary Structure for Loan", + "Monthly", + employee=applicant, + company="_Test Company", + currency=company_doc.default_currency, + ) + + if not frappe.db.exists("Loan Type", "Car Loan"): + create_loan_accounts() + create_loan_type( + "Car Loan", + 500000, + 8.4, + is_term_loan=1, + mode_of_payment="Cash", + disbursement_account="Disbursement Account - _TC", + payment_account="Payment Account - _TC", + loan_account="Loan Account - _TC", + interest_income_account="Interest Income Account - _TC", + penalty_income_account="Penalty Income Account - _TC", + ) + + loan = create_loan( + applicant, + "Car Loan", + 280000, + "Repay Over Number of Periods", + 20, + posting_date=add_months(nowdate(), -1), + ) + loan.repay_from_salary = 1 + loan.submit() + + make_loan_disbursement_entry( + loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1) + ) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) + + dates = get_start_end_dates("Monthly", nowdate()) + make_payroll_entry( + company="_Test Company", + start_date=dates.start_date, + payable_account=company_doc.default_payroll_payable_account, + currency=company_doc.default_currency, + end_date=dates.end_date, + branch=branch, + cost_center="Main - _TC", + payment_account="Cash - _TC", + ) + + name = frappe.db.get_value( + "Salary Slip", {"posting_date": nowdate(), "employee": applicant}, "name" + ) + + salary_slip = frappe.get_doc("Salary Slip", name) + for row in salary_slip.loans: + if row.loan == loan.name: + interest_amount = (280000 * 8.4) / (12 * 100) + principal_amount = loan.monthly_repayment_amount - interest_amount + self.assertEqual(row.interest_amount, interest_amount) + self.assertEqual(row.principal_amount, principal_amount) + self.assertEqual(row.total_payment, interest_amount + principal_amount) + + def test_salary_slip_operation_queueing(self): + company = "_Test Company" + company_doc = frappe.get_doc("Company", company) + employee = make_employee("test_employee@payroll.com", company=company) + setup_salary_structure(employee, company_doc) + + # enqueue salary slip creation via payroll entry + # Payroll Entry status should change to Queued + dates = get_start_end_dates("Monthly", nowdate()) + payroll_entry = get_payroll_entry( + start_date=dates.start_date, + end_date=dates.end_date, + payable_account=company_doc.default_payroll_payable_account, + currency=company_doc.default_currency, + company=company_doc.name, + cost_center="Main - _TC", + ) + frappe.flags.enqueue_payroll_entry = True + payroll_entry.submit() + payroll_entry.reload() + + self.assertEqual(payroll_entry.status, "Queued") + frappe.flags.enqueue_payroll_entry = False + + def test_salary_slip_operation_failure(self): + company = "_Test Company" + company_doc = frappe.get_doc("Company", company) + employee = make_employee("test_employee@payroll.com", company=company) + + salary_structure = make_salary_structure( + "_Test Salary Structure", + "Monthly", + employee, + company=company, + currency=company_doc.default_currency, + ) + + # reset account in component to test submission failure + component = frappe.get_doc("Salary Component", salary_structure.earnings[0].salary_component) + component.accounts = [] + component.save() + + # salary slip submission via payroll entry + # Payroll Entry status should change to Failed because of the missing account setup + dates = get_start_end_dates("Monthly", nowdate()) + payroll_entry = get_payroll_entry( + start_date=dates.start_date, + end_date=dates.end_date, + payable_account=company_doc.default_payroll_payable_account, + currency=company_doc.default_currency, + company=company_doc.name, + cost_center="Main - _TC", + ) + + # set employee as Inactive to check creation failure + frappe.db.set_value("Employee", employee, "status", "Inactive") + payroll_entry.submit() + payroll_entry.reload() + self.assertEqual(payroll_entry.status, "Failed") + self.assertIsNotNone(payroll_entry.error_message) + + frappe.db.set_value("Employee", employee, "status", "Active") + payroll_entry.submit() + payroll_entry.submit_salary_slips() + + payroll_entry.reload() + self.assertEqual(payroll_entry.status, "Failed") + self.assertIsNotNone(payroll_entry.error_message) + + # set accounts + for data in frappe.get_all("Salary Component", pluck="name"): + set_salary_component_account(data, company_list=[company]) + + # Payroll Entry successful, status should change to Submitted + payroll_entry.submit_salary_slips() + payroll_entry.reload() + + self.assertEqual(payroll_entry.status, "Submitted") + self.assertEqual(payroll_entry.error_message, "") + + def test_payroll_entry_status(self): + company = "_Test Company" + company_doc = frappe.get_doc("Company", company) + employee = make_employee("test_employee@payroll.com", company=company) + + setup_salary_structure(employee, company_doc) + + dates = get_start_end_dates("Monthly", nowdate()) + payroll_entry = get_payroll_entry( + start_date=dates.start_date, + end_date=dates.end_date, + payable_account=company_doc.default_payroll_payable_account, + currency=company_doc.default_currency, + company=company_doc.name, + cost_center="Main - _TC", + ) + payroll_entry.submit() + self.assertEqual(payroll_entry.status, "Submitted") + + payroll_entry.cancel() + self.assertEqual(payroll_entry.status, "Cancelled") + + +def get_payroll_entry(**args): + args = frappe._dict(args) + + payroll_entry: PayrollEntry = frappe.new_doc("Payroll Entry") + payroll_entry.company = args.company or erpnext.get_default_company() + payroll_entry.start_date = args.start_date or "2016-11-01" + payroll_entry.end_date = args.end_date or "2016-11-30" + payroll_entry.payment_account = get_payment_account() + payroll_entry.posting_date = nowdate() + payroll_entry.payroll_frequency = "Monthly" + payroll_entry.branch = args.branch or None + payroll_entry.department = args.department or None + payroll_entry.payroll_payable_account = args.payable_account + payroll_entry.currency = args.currency + payroll_entry.exchange_rate = args.exchange_rate or 1 + + if args.cost_center: + payroll_entry.cost_center = args.cost_center + + if args.payment_account: + payroll_entry.payment_account = args.payment_account + + payroll_entry.fill_employee_details() + payroll_entry.insert() + + # Commit so that the first salary slip creation failure does not rollback the Payroll Entry insert. + frappe.db.commit() # nosemgrep + + return payroll_entry + + +def make_payroll_entry(**args): + payroll_entry = get_payroll_entry(**args) + payroll_entry.submit() + payroll_entry.submit_salary_slips() + if payroll_entry.get_sal_slip_list(ss_status=1): + payroll_entry.make_payment_entry() + + return payroll_entry + + +def get_payment_account(): + return frappe.get_value( + "Account", + {"account_type": "Cash", "company": erpnext.get_default_company(), "is_group": 0}, + "name", + ) + + +def make_holiday(holiday_list_name): + if not frappe.db.exists("Holiday List", holiday_list_name): + current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True) + dt = getdate(nowdate()) + + new_year = dt + relativedelta(month=1, day=1, year=dt.year) + republic_day = dt + relativedelta(month=1, day=26, year=dt.year) + test_holiday = dt + relativedelta(month=2, day=2, year=dt.year) + + frappe.get_doc( + { + "doctype": "Holiday List", + "from_date": current_fiscal_year.year_start_date, + "to_date": current_fiscal_year.year_end_date, + "holiday_list_name": holiday_list_name, + "holidays": [ + {"holiday_date": new_year, "description": "New Year"}, + {"holiday_date": republic_day, "description": "Republic Day"}, + {"holiday_date": test_holiday, "description": "Test Holiday"}, + ], + } + ).insert() + + return holiday_list_name + + +def setup_salary_structure(employee, company_doc, currency=None, salary_structure=None): + for data in frappe.get_all("Salary Component", pluck="name"): + if not frappe.db.get_value( + "Salary Component Account", {"parent": data, "company": company_doc.name}, "name" + ): + set_salary_component_account(data) + + make_salary_structure( + salary_structure or "_Test Salary Structure", + "Monthly", + employee, + company=company_doc.name, + currency=(currency or company_doc.default_currency), + ) diff --git a/hrms/payroll/doctype/payroll_period/__init__.py b/hrms/payroll/doctype/payroll_period/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/payroll_period/payroll_period.js b/hrms/payroll/doctype/payroll_period/payroll_period.js new file mode 100644 index 0000000..67caf9d --- /dev/null +++ b/hrms/payroll/doctype/payroll_period/payroll_period.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Payroll Period', { + refresh: function(frm) { + + } +}); diff --git a/hrms/payroll/doctype/payroll_period/payroll_period.json b/hrms/payroll/doctype/payroll_period/payroll_period.json new file mode 100644 index 0000000..0e09484 --- /dev/null +++ b/hrms/payroll/doctype/payroll_period/payroll_period.json @@ -0,0 +1,103 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "Prompt", + "creation": "2018-04-13 15:18:53.698553", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "column_break_2", + "start_date", + "end_date", + "section_break_5", + "periods" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "reqd": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Payroll Periods" + }, + { + "fieldname": "periods", + "fieldtype": "Table", + "label": "Payroll Periods", + "options": "Payroll Period Date" + } + ], + "links": [], + "modified": "2020-06-29 17:17:12.689089", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Period", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/payroll_period/payroll_period.py b/hrms/payroll/doctype/payroll_period/payroll_period.py new file mode 100644 index 0000000..dab3a02 --- /dev/null +++ b/hrms/payroll/doctype/payroll_period/payroll_period.py @@ -0,0 +1,130 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import add_months, cint, date_diff, flt, formatdate, getdate, month_diff + +from hrms.hr.utils import get_holiday_dates_for_employee + + +class PayrollPeriod(Document): + def validate(self): + self.validate_dates() + self.validate_overlap() + + def validate_dates(self): + if getdate(self.start_date) > getdate(self.end_date): + frappe.throw(_("End date can not be less than start date")) + + def validate_overlap(self): + query = """ + select name + from `tab{0}` + where name != %(name)s + and company = %(company)s and (start_date between %(start_date)s and %(end_date)s \ + or end_date between %(start_date)s and %(end_date)s \ + or (start_date < %(start_date)s and end_date > %(end_date)s)) + """ + if not self.name: + # hack! if name is null, it could cause problems with != + self.name = "New " + self.doctype + + overlap_doc = frappe.db.sql( + query.format(self.doctype), + { + "start_date": self.start_date, + "end_date": self.end_date, + "name": self.name, + "company": self.company, + }, + as_dict=1, + ) + + if overlap_doc: + msg = ( + _("A {0} exists between {1} and {2} (").format( + self.doctype, formatdate(self.start_date), formatdate(self.end_date) + ) + + """ {1}""".format(self.doctype, overlap_doc[0].name) + + _(") for {0}").format(self.company) + ) + frappe.throw(msg) + + +def get_payroll_period_days(start_date, end_date, employee, company=None): + if not company: + company = frappe.db.get_value("Employee", employee, "company") + payroll_period = frappe.db.sql( + """ + select name, start_date, end_date + from `tabPayroll Period` + where + company=%(company)s + and %(start_date)s between start_date and end_date + and %(end_date)s between start_date and end_date + """, + {"company": company, "start_date": start_date, "end_date": end_date}, + ) + + if len(payroll_period) > 0: + actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 + working_days = actual_no_of_days + if not cint( + frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days") + ): + holidays = get_holiday_dates_for_employee( + employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2]) + ) + working_days -= len(holidays) + return payroll_period[0][0], working_days, actual_no_of_days + return False, False, False + + +def get_payroll_period(from_date, to_date, company): + payroll_period = frappe.db.sql( + """ + select name, start_date, end_date + from `tabPayroll Period` + where start_date<=%s and end_date>= %s and company=%s + """, + (from_date, to_date, company), + as_dict=1, + ) + + return payroll_period[0] if payroll_period else None + + +def get_period_factor( + employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0 +): + # TODO if both deduct checked update the factor to make tax consistent + period_start, period_end = payroll_period.start_date, payroll_period.end_date + joining_date, relieving_date = frappe.db.get_value( + "Employee", employee, ["date_of_joining", "relieving_date"] + ) + + if getdate(joining_date) > getdate(period_start): + period_start = joining_date + if relieving_date and getdate(relieving_date) < getdate(period_end): + period_end = relieving_date + if month_diff(period_end, start_date) > 1: + start_date = add_months(start_date, -(month_diff(period_end, start_date) + 1)) + + total_sub_periods, remaining_sub_periods = 0.0, 0.0 + + if payroll_frequency == "Monthly" and not depends_on_payment_days: + total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date) + remaining_sub_periods = month_diff(period_end, start_date) + else: + salary_days = date_diff(end_date, start_date) + 1 + + days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1 + total_sub_periods = flt(days_in_payroll_period) / flt(salary_days) + + remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1 + remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days) + + return total_sub_periods, remaining_sub_periods diff --git a/hrms/payroll/doctype/payroll_period/payroll_period_dashboard.py b/hrms/payroll/doctype/payroll_period/payroll_period_dashboard.py new file mode 100644 index 0000000..96632c5 --- /dev/null +++ b/hrms/payroll/doctype/payroll_period/payroll_period_dashboard.py @@ -0,0 +1,7 @@ +def get_data(): + return { + "fieldname": "payroll_period", + "transactions": [ + {"items": ["Employee Tax Exemption Proof Submission", "Employee Tax Exemption Declaration"]}, + ], + } diff --git a/hrms/payroll/doctype/payroll_period/test_payroll_period.py b/hrms/payroll/doctype/payroll_period/test_payroll_period.py new file mode 100644 index 0000000..61967c0 --- /dev/null +++ b/hrms/payroll/doctype/payroll_period/test_payroll_period.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestPayrollPeriod(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/payroll_period_date/__init__.py b/hrms/payroll/doctype/payroll_period_date/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/payroll_period_date/payroll_period_date.json b/hrms/payroll/doctype/payroll_period_date/payroll_period_date.json new file mode 100644 index 0000000..4a2f383 --- /dev/null +++ b/hrms/payroll/doctype/payroll_period_date/payroll_period_date.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2018-04-13 15:17:30.513630", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "start_date", + "end_date" + ], + "fields": [ + { + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "End Date", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:30:15.943356", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Period Date", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/payroll_period_date/payroll_period_date.py b/hrms/payroll/doctype/payroll_period_date/payroll_period_date.py new file mode 100644 index 0000000..c90a76a --- /dev/null +++ b/hrms/payroll/doctype/payroll_period_date/payroll_period_date.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class PayrollPeriodDate(Document): + pass diff --git a/hrms/payroll/doctype/payroll_settings/__init__.py b/hrms/payroll/doctype/payroll_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/payroll_settings/payroll_settings.js b/hrms/payroll/doctype/payroll_settings/payroll_settings.js new file mode 100644 index 0000000..941464d --- /dev/null +++ b/hrms/payroll/doctype/payroll_settings/payroll_settings.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Payroll Settings', { + encrypt_salary_slips_in_emails: function(frm) { + let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; + frm.set_df_property('password_policy', 'reqd', encrypt_state); + }, + + validate: function(frm) { + let policy = frm.doc.password_policy; + if (policy) { + if (policy.includes(' ') || policy.includes('--')) { + frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); + } + frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); + } + }, +}); diff --git a/hrms/payroll/doctype/payroll_settings/payroll_settings.json b/hrms/payroll/doctype/payroll_settings/payroll_settings.json new file mode 100644 index 0000000..54377e9 --- /dev/null +++ b/hrms/payroll/doctype/payroll_settings/payroll_settings.json @@ -0,0 +1,119 @@ +{ + "actions": [], + "creation": "2020-06-04 15:13:33.589685", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "payroll_based_on", + "consider_unmarked_attendance_as", + "max_working_hours_against_timesheet", + "include_holidays_in_total_working_days", + "disable_rounded_total", + "column_break_11", + "daily_wages_fraction_for_half_day", + "email_salary_slip_to_employee", + "encrypt_salary_slips_in_emails", + "show_leave_balances_in_salary_slip", + "password_policy" + ], + "fields": [ + { + "default": "Leave", + "fieldname": "payroll_based_on", + "fieldtype": "Select", + "label": "Calculate Payroll Working Days Based On", + "options": "Leave\nAttendance" + }, + { + "fieldname": "max_working_hours_against_timesheet", + "fieldtype": "Float", + "label": "Max working hours against Timesheet" + }, + { + "default": "0", + "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", + "fieldname": "include_holidays_in_total_working_days", + "fieldtype": "Check", + "label": "Include holidays in Total no. of Working Days" + }, + { + "default": "0", + "description": "If checked, hides and disables Rounded Total field in Salary Slips", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "default": "0.5", + "description": "The fraction of daily wages to be paid for half-day attendance", + "fieldname": "daily_wages_fraction_for_half_day", + "fieldtype": "Float", + "label": "Fraction of Daily Salary for Half Day" + }, + { + "default": "1", + "description": "Emails salary slip to employee based on preferred email selected in Employee", + "fieldname": "email_salary_slip_to_employee", + "fieldtype": "Check", + "label": "Email Salary Slip to Employee" + }, + { + "default": "0", + "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", + "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", + "fieldname": "encrypt_salary_slips_in_emails", + "fieldtype": "Check", + "label": "Encrypt Salary Slips in Emails" + }, + { + "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", + "description": "Example: SAL-{first_name}-{date_of_birth.year}
This will generate a password like SAL-Jane-1972", + "fieldname": "password_policy", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Password Policy" + }, + { + "depends_on": "eval:doc.payroll_based_on == 'Attendance'", + "fieldname": "consider_unmarked_attendance_as", + "fieldtype": "Select", + "label": "Consider Unmarked Attendance As", + "options": "Present\nAbsent" + }, + { + "default": "0", + "fieldname": "show_leave_balances_in_salary_slip", + "fieldtype": "Check", + "label": "Show Leave Balances in Salary Slip" + } + ], + "icon": "fa fa-cog", + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2021-03-03 17:49:59.579723", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/payroll_settings/payroll_settings.py b/hrms/payroll/doctype/payroll_settings/payroll_settings.py new file mode 100644 index 0000000..33614e9 --- /dev/null +++ b/hrms/payroll/doctype/payroll_settings/payroll_settings.py @@ -0,0 +1,45 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe.model.document import Document +from frappe.utils import cint + + +class PayrollSettings(Document): + def validate(self): + self.validate_password_policy() + + if not self.daily_wages_fraction_for_half_day: + self.daily_wages_fraction_for_half_day = 0.5 + + def validate_password_policy(self): + if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: + if not self.password_policy: + frappe.throw(_("Password policy for Salary Slips is not set")) + + def on_update(self): + self.toggle_rounded_total() + frappe.clear_cache() + + def toggle_rounded_total(self): + self.disable_rounded_total = cint(self.disable_rounded_total) + make_property_setter( + "Salary Slip", + "rounded_total", + "hidden", + self.disable_rounded_total, + "Check", + validate_fields_for_doctype=False, + ) + make_property_setter( + "Salary Slip", + "rounded_total", + "print_hide", + self.disable_rounded_total, + "Check", + validate_fields_for_doctype=False, + ) diff --git a/hrms/payroll/doctype/payroll_settings/test_payroll_settings.py b/hrms/payroll/doctype/payroll_settings/test_payroll_settings.py new file mode 100644 index 0000000..3b96db6 --- /dev/null +++ b/hrms/payroll/doctype/payroll_settings/test_payroll_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestPayrollSettings(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/retention_bonus/__init__.py b/hrms/payroll/doctype/retention_bonus/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/retention_bonus/retention_bonus.js b/hrms/payroll/doctype/retention_bonus/retention_bonus.js new file mode 100644 index 0000000..99a21f6 --- /dev/null +++ b/hrms/payroll/doctype/retention_bonus/retention_bonus.js @@ -0,0 +1,43 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Retention Bonus', { + setup: function(frm) { + frm.set_query("employee", function() { + if (!frm.doc.company) { + frappe.msgprint(__("Please Select Company First")); + } + return { + filters: { + "status": "Active", + "company": frm.doc.company + } + }; + }); + + frm.set_query("salary_component", function() { + return { + filters: { + "type": "Earning" + } + }; + }); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + } + } +}); diff --git a/hrms/payroll/doctype/retention_bonus/retention_bonus.json b/hrms/payroll/doctype/retention_bonus/retention_bonus.json new file mode 100644 index 0000000..f8d8bb4 --- /dev/null +++ b/hrms/payroll/doctype/retention_bonus/retention_bonus.json @@ -0,0 +1,173 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-RTB-.YYYY.-.#####", + "creation": "2018-05-13 14:59:42.038964", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "employee", + "bonus_payment_date", + "bonus_amount", + "salary_component", + "amended_from", + "column_break_6", + "employee_name", + "department", + "date_of_joining", + "currency" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "bonus_payment_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Bonus Payment Date", + "reqd": 1 + }, + { + "fieldname": "bonus_amount", + "fieldtype": "Currency", + "label": "Bonus Amount", + "options": "currency", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Retention Bonus", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.date_of_joining", + "fieldname": "date_of_joining", + "fieldtype": "Data", + "label": "Date of Joining", + "read_only": 1 + }, + { + "fieldname": "salary_component", + "fieldtype": "Link", + "label": "Salary Component", + "options": "Salary Component", + "reqd": 1 + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-01-19 12:57:37.898953", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Retention Bonus", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + } + ], + "search_fields": "employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/retention_bonus/retention_bonus.py b/hrms/payroll/doctype/retention_bonus/retention_bonus.py new file mode 100644 index 0000000..b814059 --- /dev/null +++ b/hrms/payroll/doctype/retention_bonus/retention_bonus.py @@ -0,0 +1,69 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate + +from hrms.hr.utils import validate_active_employee + + +class RetentionBonus(Document): + def validate(self): + validate_active_employee(self.employee) + if getdate(self.bonus_payment_date) < getdate(): + frappe.throw(_("Bonus Payment Date cannot be a past date")) + + def on_submit(self): + company = frappe.db.get_value("Employee", self.employee, "company") + additional_salary = self.get_additional_salary() + + if not additional_salary: + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.employee = self.employee + additional_salary.salary_component = self.salary_component + additional_salary.amount = self.bonus_amount + additional_salary.payroll_date = self.bonus_payment_date + additional_salary.company = company + additional_salary.overwrite_salary_structure_amount = 0 + additional_salary.ref_doctype = self.doctype + additional_salary.ref_docname = self.name + additional_salary.submit() + # self.db_set('additional_salary', additional_salary.name) + + else: + bonus_added = ( + frappe.db.get_value("Additional Salary", additional_salary, "amount") + self.bonus_amount + ) + frappe.db.set_value("Additional Salary", additional_salary, "amount", bonus_added) + self.db_set("additional_salary", additional_salary) + + def on_cancel(self): + + additional_salary = self.get_additional_salary() + if self.additional_salary: + bonus_removed = ( + frappe.db.get_value("Additional Salary", self.additional_salary, "amount") - self.bonus_amount + ) + if bonus_removed == 0: + frappe.get_doc("Additional Salary", self.additional_salary).cancel() + else: + frappe.db.set_value("Additional Salary", self.additional_salary, "amount", bonus_removed) + + # self.db_set('additional_salary', '') + + def get_additional_salary(self): + return frappe.db.exists( + "Additional Salary", + { + "employee": self.employee, + "salary_component": self.salary_component, + "payroll_date": self.bonus_payment_date, + "company": self.company, + "docstatus": 1, + "ref_doctype": self.doctype, + "ref_docname": self.name, + }, + ) diff --git a/hrms/payroll/doctype/retention_bonus/test_retention_bonus.py b/hrms/payroll/doctype/retention_bonus/test_retention_bonus.py new file mode 100644 index 0000000..c86bf33 --- /dev/null +++ b/hrms/payroll/doctype/retention_bonus/test_retention_bonus.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestRetentionBonus(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/salary_component/README.md b/hrms/payroll/doctype/salary_component/README.md new file mode 100644 index 0000000..9644192 --- /dev/null +++ b/hrms/payroll/doctype/salary_component/README.md @@ -0,0 +1 @@ +Type of earning and deductions that is a part of the salary. diff --git a/hrms/payroll/doctype/salary_component/__init__.py b/hrms/payroll/doctype/salary_component/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js new file mode 100644 index 0000000..dbf7514 --- /dev/null +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -0,0 +1,69 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Salary Component', { + setup: function(frm) { + frm.set_query("account", "accounts", function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "is_group": 0, + "company": d.company + } + }; + }); + frm.set_query("earning_component_group", function() { + return { + filters: { + "is_group": 1, + "is_flexible_benefit": 1 + } + }; + }); + }, + is_flexible_benefit: function(frm) { + if(frm.doc.is_flexible_benefit){ + set_value_for_condition_and_formula(frm); + frm.set_value("formula", ''); + frm.set_value("amount", 0); + } + }, + type: function(frm) { + if(frm.doc.type=="Earning"){ + frm.set_value("is_tax_applicable", 1); + frm.set_value("variable_based_on_taxable_salary", 0); + } + if(frm.doc.type=="Deduction"){ + frm.set_value("is_tax_applicable", 0); + frm.set_value("is_flexible_benefit", 0); + } + }, + variable_based_on_taxable_salary: function(frm) { + if(frm.doc.variable_based_on_taxable_salary){ + set_value_for_condition_and_formula(frm); + } + }, + create_separate_payment_entry_against_benefit_claim: function(frm) { + if(frm.doc.create_separate_payment_entry_against_benefit_claim){ + frm.set_df_property("accounts", "reqd", 1); + frm.set_value("only_tax_impact", 0); + } + else{ + frm.set_df_property("accounts", "reqd", 0); + } + }, + only_tax_impact: function(frm) { + if(frm.only_tax_impact){ + frm.set_value("create_separate_payment_entry_against_benefit_claim", 0); + } + } +}); + +var set_value_for_condition_and_formula = function(frm) { + frm.set_value("formula", null); + frm.set_value("condition", null); + frm.set_value("amount_based_on_formula", 0); + frm.set_value("statistical_component", 0); + frm.set_value("do_not_include_in_total", 0); + frm.set_value("depends_on_payment_days", 0); +}; diff --git a/hrms/payroll/doctype/salary_component/salary_component.json b/hrms/payroll/doctype/salary_component/salary_component.json new file mode 100644 index 0000000..c97e45c --- /dev/null +++ b/hrms/payroll/doctype/salary_component/salary_component.json @@ -0,0 +1,272 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:salary_component", + "creation": "2016-06-30 15:42:43.631931", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "salary_component_abbr", + "type", + "description", + "column_break_4", + "depends_on_payment_days", + "is_tax_applicable", + "is_income_tax_component", + "deduct_full_tax_on_selected_payroll_date", + "variable_based_on_taxable_salary", + "exempted_from_income_tax", + "round_to_the_nearest_integer", + "statistical_component", + "do_not_include_in_total", + "disabled", + "flexible_benefits", + "is_flexible_benefit", + "max_benefit_amount", + "column_break_9", + "pay_against_benefit_claim", + "only_tax_impact", + "create_separate_payment_entry_against_benefit_claim", + "section_break_5", + "accounts", + "condition_and_formula", + "condition", + "amount", + "amount_based_on_formula", + "formula", + "column_break_28", + "help" + ], + "fields": [ + { + "fieldname": "salary_component", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "salary_component_abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_width": "120px", + "reqd": 1, + "width": "120px" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Type", + "options": "Earning\nDeduction", + "reqd": 1 + }, + { + "default": "1", + "depends_on": "eval:doc.type == \"Earning\"", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable" + }, + { + "default": "1", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do Not Include in Total" + }, + { + "default": "0", + "depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description" + }, + { + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fieldname": "statistical_component", + "fieldtype": "Check", + "label": "Statistical Component" + }, + { + "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1", + "fieldname": "flexible_benefits", + "fieldtype": "Section Break", + "label": "Flexible Benefits" + }, + { + "default": "0", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit" + }, + { + "depends_on": "is_flexible_benefit", + "fieldname": "max_benefit_amount", + "fieldtype": "Currency", + "label": "Max Benefit Amount (Yearly)" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "is_flexible_benefit", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "label": "Pay Against Benefit Claim" + }, + { + "default": "0", + "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1", + "fieldname": "only_tax_impact", + "fieldtype": "Check", + "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)" + }, + { + "default": "0", + "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1", + "fieldname": "create_separate_payment_entry_against_benefit_claim", + "fieldtype": "Check", + "label": "Create Separate Payment Entry Against Benefit Claim" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\"", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary" + }, + { + "depends_on": "eval:doc.statistical_component != 1", + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Accounts" + }, + { + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Salary Component Account" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", + "fieldname": "condition_and_formula", + "fieldtype": "Section Break", + "label": "Condition and Formula" + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, + { + "default": "0", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, + { + "depends_on": "amount_based_on_formula", + "fieldname": "formula", + "fieldtype": "Code", + "label": "Formula" + }, + { + "depends_on": "eval:doc.amount_based_on_formula!==1", + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "

Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condition. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" + }, + { + "default": "0", + "fieldname": "round_to_the_nearest_integer", + "fieldtype": "Check", + "label": "Round to the Nearest Integer" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary", + "description": "If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission.", + "fieldname": "exempted_from_income_tax", + "fieldtype": "Check", + "label": "Exempted from Income Tax" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\"", + "fieldname": "is_income_tax_component", + "fieldtype": "Check", + "label": "Is Income Tax Component" + } + ], + "icon": "fa fa-flag", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-10-07 20:38:33.795853", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Component", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py new file mode 100644 index 0000000..409c4a1 --- /dev/null +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -0,0 +1,24 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document +from frappe.model.naming import append_number_if_name_exists + + +class SalaryComponent(Document): + def validate(self): + self.validate_abbr() + + def validate_abbr(self): + if not self.salary_component_abbr: + self.salary_component_abbr = "".join([c[0] for c in self.salary_component.split()]).upper() + + self.salary_component_abbr = self.salary_component_abbr.strip() + self.salary_component_abbr = append_number_if_name_exists( + "Salary Component", + self.salary_component_abbr, + "salary_component_abbr", + separator="_", + filters={"name": ["!=", self.name]}, + ) diff --git a/hrms/payroll/doctype/salary_component/test_records.json b/hrms/payroll/doctype/salary_component/test_records.json new file mode 100644 index 0000000..104b44f --- /dev/null +++ b/hrms/payroll/doctype/salary_component/test_records.json @@ -0,0 +1,36 @@ +[ + { + "doctype": "Salary Component", + "salary_component": "_Test Basic Salary", + "type": "Earning", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "_Test Allowance", + "type": "Earning", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "_Test Professional Tax", + "type": "Deduction" + }, + { + "doctype": "Salary Component", + "salary_component": "_Test TDS", + "type": "Deduction" + }, + { + "doctype": "Salary Component", + "salary_component": "Basic", + "type": "Earning", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "Leave Encashment", + "type": "Earning", + "is_tax_applicable": 1 + } +] \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_component/test_salary_component.py b/hrms/payroll/doctype/salary_component/test_salary_component.py new file mode 100644 index 0000000..cd729e8 --- /dev/null +++ b/hrms/payroll/doctype/salary_component/test_salary_component.py @@ -0,0 +1,24 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + +import frappe + +# test_records = frappe.get_test_records('Salary Component') + + +class TestSalaryComponent(unittest.TestCase): + pass + + +def create_salary_component(component_name, **args): + if not frappe.db.exists("Salary Component", component_name): + frappe.get_doc( + { + "doctype": "Salary Component", + "salary_component": component_name, + "type": args.get("type") or "Earning", + "is_tax_applicable": args.get("is_tax_applicable") or 1, + } + ).insert() diff --git a/hrms/payroll/doctype/salary_component_account/__init__.py b/hrms/payroll/doctype/salary_component_account/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_component_account/salary_component_account.json b/hrms/payroll/doctype/salary_component_account/salary_component_account.json new file mode 100644 index 0000000..1b68880 --- /dev/null +++ b/hrms/payroll/doctype/salary_component_account/salary_component_account.json @@ -0,0 +1,38 @@ +{ + "actions": [], + "creation": "2016-07-27 17:24:24.956896", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "account" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company" + }, + { + "description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.", + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account" + } + ], + "istable": 1, + "links": [], + "modified": "2022-07-13 13:00:00.110257", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Component Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_component_account/salary_component_account.py b/hrms/payroll/doctype/salary_component_account/salary_component_account.py new file mode 100644 index 0000000..b70179a --- /dev/null +++ b/hrms/payroll/doctype/salary_component_account/salary_component_account.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class SalaryComponentAccount(Document): + pass diff --git a/hrms/payroll/doctype/salary_detail/__init__.py b/hrms/payroll/doctype/salary_detail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_detail/salary_detail.json b/hrms/payroll/doctype/salary_detail/salary_detail.json new file mode 100644 index 0000000..665f0a8 --- /dev/null +++ b/hrms/payroll/doctype/salary_detail/salary_detail.json @@ -0,0 +1,260 @@ +{ + "actions": [], + "creation": "2016-06-30 15:32:36.385111", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "abbr", + "column_break_3", + "amount", + "year_to_date", + "section_break_5", + "additional_salary", + "is_recurring_additional_salary", + "statistical_component", + "depends_on_payment_days", + "exempted_from_income_tax", + "is_tax_applicable", + "column_break_11", + "is_flexible_benefit", + "variable_based_on_taxable_salary", + "do_not_include_in_total", + "deduct_full_tax_on_selected_payroll_date", + "section_break_2", + "condition", + "column_break_18", + "amount_based_on_formula", + "formula", + "section_break_19", + "default_amount", + "additional_amount", + "column_break_24", + "tax_on_flexible_benefit", + "tax_on_additional_salary" + ], + "fields": [ + { + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Component", + "options": "Salary Component", + "reqd": 1 + }, + { + "columns": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fetch_from": "salary_component.salary_component_abbr", + "fieldname": "abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fetch_from": "salary_component.statistical_component", + "fieldname": "statistical_component", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Statistical Component" + }, + { + "default": "0", + "depends_on": "eval:doc.parentfield=='earnings'", + "fetch_from": "salary_component.is_tax_applicable", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.parentfield=='earnings'", + "fetch_from": "salary_component.is_flexible_benefit", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.parentfield=='deductions'", + "fetch_from": "salary_component.variable_based_on_taxable_salary", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "salary_component.depends_on_payment_days", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "depends_on": "eval:doc.is_flexible_benefit != 1", + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "label": "Condition and formula" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, + { + "default": "0", + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", + "fieldname": "formula", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Formula" + }, + { + "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency" + }, + { + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do not include in total" + }, + { + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "default_amount", + "fieldtype": "Currency", + "label": "Default Amount", + "options": "currency", + "print_hide": 1 + }, + { + "fieldname": "additional_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Additional Amount", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_flexible_benefit", + "fieldtype": "Currency", + "label": "Tax on flexible benefit", + "options": "currency", + "read_only": 1 + }, + { + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_additional_salary", + "fieldtype": "Currency", + "label": "Tax on additional salary", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "additional_salary", + "fieldtype": "Link", + "label": "Additional Salary ", + "options": "Additional Salary", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.parentfield=='deductions'", + "fetch_from": "salary_component.exempted_from_income_tax", + "fieldname": "exempted_from_income_tax", + "fieldtype": "Check", + "label": "Exempted from Income Tax", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Component properties and references " + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_19", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "description": "Total salary booked against this component for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.", + "fieldname": "year_to_date", + "fieldtype": "Currency", + "label": "Year To Date", + "options": "currency", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.additional_salary", + "fieldname": "is_recurring_additional_salary", + "fieldtype": "Check", + "label": "Is Recurring Additional Salary", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2021-08-30 13:39:15.847158", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_detail/salary_detail.py b/hrms/payroll/doctype/salary_detail/salary_detail.py new file mode 100644 index 0000000..c74bd54 --- /dev/null +++ b/hrms/payroll/doctype/salary_detail/salary_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +from frappe.model.document import Document + + +class SalaryDetail(Document): + pass diff --git a/hrms/payroll/doctype/salary_slip/README.md b/hrms/payroll/doctype/salary_slip/README.md new file mode 100644 index 0000000..736550e --- /dev/null +++ b/hrms/payroll/doctype/salary_slip/README.md @@ -0,0 +1 @@ +Details of monthly salary paid for an Employee. \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_slip/__init__.py b/hrms/payroll/doctype/salary_slip/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_slip/salary_slip.js b/hrms/payroll/doctype/salary_slip/salary_slip.js new file mode 100644 index 0000000..2cf727c --- /dev/null +++ b/hrms/payroll/doctype/salary_slip/salary_slip.js @@ -0,0 +1,290 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on("Salary Slip", { + setup: function(frm) { + $.each(["earnings", "deductions"], function(i, table_fieldname) { + frm.get_field(table_fieldname).grid.editable_fields = [ + {fieldname: 'salary_component', columns: 6}, + {fieldname: 'amount', columns: 4} + ]; + }); + + frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function() { + return { + filters: { + employee: frm.doc.employee + } + }; + }; + + frm.set_query("salary_component", "earnings", function() { + return { + filters: { + type: "earning" + } + }; + }); + + frm.set_query("salary_component", "deductions", function() { + return { + filters: { + type: "deduction" + } + }; + }); + + frm.set_query("employee", function() { + return { + query: "erpnext.controllers.queries.employee_query", + filters: { + company: frm.doc.company + } + }; + }); + }, + + start_date: function(frm) { + if (frm.doc.start_date) { + frm.trigger("set_end_date"); + } + }, + + end_date: function(frm) { + frm.events.get_emp_and_working_day_details(frm); + }, + + set_end_date: function(frm) { + frappe.call({ + method: 'hrms.payroll.doctype.payroll_entry.payroll_entry.get_end_date', + args: { + frequency: frm.doc.payroll_frequency, + start_date: frm.doc.start_date + }, + callback: function (r) { + if (r.message) { + frm.set_value('end_date', r.message.end_date); + } + } + }); + }, + + company: function(frm) { + var company = locals[':Company'][frm.doc.company]; + if (!frm.doc.letter_head && company.default_letter_head) { + frm.set_value('letter_head', company.default_letter_head); + } + }, + + currency: function(frm) { + frm.trigger("set_dynamic_labels"); + }, + + set_dynamic_labels: function(frm) { + var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency"); + if (frm.doc.employee && frm.doc.currency) { + frappe.run_serially([ + () => frm.events.set_exchange_rate(frm, company_currency), + () => frm.events.change_form_labels(frm, company_currency), + () => frm.events.change_grid_labels(frm), + () => frm.refresh_fields() + ]); + } + }, + + set_exchange_rate: function(frm, company_currency) { + if (frm.doc.docstatus === 0) { + if (frm.doc.currency) { + var from_currency = frm.doc.currency; + if (from_currency != company_currency) { + frm.events.hide_loan_section(frm); + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: from_currency, + to_currency: company_currency, + }, + callback: function(r) { + if (r.message) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + } + } + }); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", "" ); + } + } + } + }, + + exchange_rate: function(frm) { + set_totals(frm); + }, + + hide_loan_section: function(frm) { + frm.set_df_property('section_break_43', 'hidden', 1); + }, + + change_form_labels: function(frm, company_currency) { + frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction", + "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "gross_base_year_to_date"], + company_currency); + + frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date", "gross_year_to_date"], + frm.doc.currency); + + // toggle fields + frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction", + "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "base_gross_year_to_date"], + frm.doc.currency != company_currency); + }, + + change_grid_labels: function(frm) { + let fields = ["amount", "year_to_date", "default_amount", "additional_amount", "tax_on_flexible_benefit", + "tax_on_additional_salary"]; + + frm.set_currency_labels(fields, frm.doc.currency, "earnings"); + frm.set_currency_labels(fields, frm.doc.currency, "deductions"); + }, + + refresh: function(frm) { + frm.trigger("toggle_fields"); + + var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"]; + frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false); + frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false); + frm.trigger("set_dynamic_labels"); + }, + + salary_slip_based_on_timesheet: function(frm) { + frm.trigger("toggle_fields"); + frm.events.get_emp_and_working_day_details(frm); + }, + + payroll_frequency: function(frm) { + frm.trigger("toggle_fields"); + frm.set_value('end_date', ''); + }, + + employee: function(frm) { + frm.events.get_emp_and_working_day_details(frm); + }, + + leave_without_pay: function(frm) { + if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) { + return frappe.call({ + method: 'process_salary_based_on_working_days', + doc: frm.doc, + callback: function() { + frm.refresh(); + } + }); + } + }, + + toggle_fields: function(frm) { + frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1); + + frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'], + frm.doc.payroll_frequency != ""); + }, + + get_emp_and_working_day_details: function(frm) { + if (frm.doc.employee) { + return frappe.call({ + method: 'get_emp_and_working_day_details', + doc: frm.doc, + callback: function(r) { + if (r.message[1] !== "Leave" && r.message[0]) { + frm.fields_dict.absent_days.set_description(__("Unmarked Days is treated as {0}. You can can change this in {1}", [r.message, frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)])); + } + frm.refresh(); + } + }); + } + } +}); + +frappe.ui.form.on('Salary Slip Timesheet', { + time_sheet: function(frm) { + set_totals(frm); + }, + timesheets_remove: function(frm) { + set_totals(frm); + } +}); + +var set_totals = function(frm) { + if (frm.doc.docstatus === 0 && frm.doc.doctype === "Salary Slip") { + if (frm.doc.earnings || frm.doc.deductions) { + frappe.call({ + method: "set_totals", + doc: frm.doc, + callback: function() { + frm.refresh_fields(); + } + }); + } + } +}; + +frappe.ui.form.on('Salary Detail', { + amount: function(frm) { + set_totals(frm); + }, + + earnings_remove: function(frm) { + set_totals(frm); + }, + + deductions_remove: function(frm) { + set_totals(frm); + }, + + salary_component: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if (child.salary_component) { + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Salary Component", + name: child.salary_component + }, + callback: function(data) { + if (data.message) { + var result = data.message; + frappe.model.set_value(cdt, cdn, 'condition', result.condition); + frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); + if (result.amount_based_on_formula === 1) { + frappe.model.set_value(cdt, cdn, 'formula', result.formula); + } else { + frappe.model.set_value(cdt, cdn, 'amount', result.amount); + } + frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); + frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days); + frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); + frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); + frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); + frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); + refresh_field("earnings"); + refresh_field("deductions"); + } + } + }); + } + }, + + amount_based_on_formula: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if (child.amount_based_on_formula === 1) { + frappe.model.set_value(cdt, cdn, 'amount', null); + } else { + frappe.model.set_value(cdt, cdn, 'formula', null); + } + } +}); diff --git a/hrms/payroll/doctype/salary_slip/salary_slip.json b/hrms/payroll/doctype/salary_slip/salary_slip.json new file mode 100644 index 0000000..510cbb6 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip/salary_slip.json @@ -0,0 +1,685 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2013-01-10 16:34:15", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "posting_date", + "employee", + "employee_name", + "department", + "designation", + "branch", + "column_break1", + "status", + "journal_entry", + "payroll_entry", + "company", + "currency", + "exchange_rate", + "letter_head", + "section_break_10", + "start_date", + "end_date", + "salary_structure", + "column_break_18", + "salary_slip_based_on_timesheet", + "payroll_frequency", + "section_break_20", + "total_working_days", + "unmarked_days", + "leave_without_pay", + "column_break_24", + "absent_days", + "payment_days", + "hourly_wages", + "timesheets", + "column_break_20", + "total_working_hours", + "hour_rate", + "base_hour_rate", + "section_break_26", + "bank_name", + "bank_account_no", + "mode_of_payment", + "section_break_32", + "deduct_tax_for_unclaimed_employee_benefits", + "deduct_tax_for_unsubmitted_tax_exemption_proof", + "earning_deduction", + "earning", + "earnings", + "deduction", + "deductions", + "totals", + "gross_pay", + "base_gross_pay", + "gross_year_to_date", + "base_gross_year_to_date", + "column_break_25", + "total_deduction", + "base_total_deduction", + "loan_repayment", + "loans", + "section_break_43", + "total_principal_amount", + "total_interest_amount", + "column_break_45", + "total_loan_repayment", + "net_pay_info", + "net_pay", + "base_net_pay", + "year_to_date", + "base_year_to_date", + "column_break_53", + "rounded_total", + "base_rounded_total", + "month_to_date", + "base_month_to_date", + "section_break_55", + "total_in_words", + "column_break_69", + "base_total_in_words", + "leave_details_section", + "leave_details", + "section_break_75", + "amended_from" + ], + "fields": [ + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "in_global_search": 1, + "in_list_view": 1, + "label": "Employee Name", + "oldfieldname": "employee_name", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Department", + "oldfieldname": "department", + "oldfieldtype": "Link", + "options": "Department", + "read_only": 1 + }, + { + "depends_on": "eval:doc.designation", + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "oldfieldname": "designation", + "oldfieldtype": "Link", + "options": "Designation", + "read_only": 1 + }, + { + "fetch_from": "employee.branch", + "fieldname": "branch", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Branch", + "oldfieldname": "branch", + "oldfieldtype": "Link", + "options": "Branch", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Draft\nSubmitted\nCancelled", + "read_only": 1 + }, + { + "fieldname": "journal_entry", + "fieldtype": "Link", + "label": "Journal Entry", + "options": "Journal Entry", + "read_only": 1 + }, + { + "fieldname": "payroll_entry", + "fieldtype": "Link", + "label": "Payroll Entry", + "options": "Payroll Entry", + "read_only": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "salary_slip_based_on_timesheet", + "fieldtype": "Check", + "label": "Salary Slip Based on Timesheet", + "read_only": 1 + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date" + }, + { + "fieldname": "salary_structure", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Salary Structure", + "options": "Salary Structure", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", + "fieldname": "payroll_frequency", + "fieldtype": "Select", + "label": "Payroll Frequency", + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + }, + { + "fieldname": "total_working_days", + "fieldtype": "Float", + "label": "Working Days", + "oldfieldname": "total_days_in_month", + "oldfieldtype": "Int", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "leave_without_pay", + "fieldtype": "Float", + "label": "Leave Without Pay", + "oldfieldname": "leave_without_pay", + "oldfieldtype": "Currency" + }, + { + "fieldname": "payment_days", + "fieldtype": "Float", + "label": "Payment Days", + "oldfieldname": "payment_days", + "oldfieldtype": "Float", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "hourly_wages", + "fieldtype": "Section Break" + }, + { + "fieldname": "timesheets", + "fieldtype": "Table", + "label": "Salary Slip Timesheet", + "options": "Salary Slip Timesheet" + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, + { + "fetch_from": "timesheet.total_hours", + "fieldname": "total_working_hours", + "fieldtype": "Float", + "label": "Total Working Hours", + "print_hide_if_no_value": 1 + }, + { + "fieldname": "hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate", + "options": "currency", + "print_hide_if_no_value": 1 + }, + { + "fieldname": "section_break_26", + "fieldtype": "Section Break" + }, + { + "fieldname": "bank_name", + "fieldtype": "Data", + "label": "Bank Name", + "oldfieldname": "bank_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "bank_account_no", + "fieldtype": "Data", + "label": "Bank Account No.", + "oldfieldname": "bank_account_no", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "section_break_32", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "deduct_tax_for_unclaimed_employee_benefits", + "fieldtype": "Check", + "label": "Deduct Tax For Unclaimed Employee Benefits" + }, + { + "default": "0", + "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", + "fieldtype": "Check", + "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" + }, + { + "fieldname": "earning_deduction", + "fieldtype": "Section Break", + "label": "Earnings & Deductions", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "earning", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "earnings", + "fieldtype": "Table", + "label": "Earnings", + "oldfieldname": "earning_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, + { + "fieldname": "deduction", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "deductions", + "fieldtype": "Table", + "label": "Deductions", + "oldfieldname": "deduction_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "gross_pay", + "fieldtype": "Currency", + "label": "Gross Pay", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "depends_on": "total_loan_repayment", + "fieldname": "loan_repayment", + "fieldtype": "Section Break", + "label": "Loan Repayment" + }, + { + "fieldname": "loans", + "fieldtype": "Table", + "label": "Employee Loan", + "options": "Salary Slip Loan", + "print_hide": 1 + }, + { + "depends_on": "eval:doc.docstatus != 0", + "fieldname": "section_break_43", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "total_principal_amount", + "fieldtype": "Currency", + "label": "Total Principal Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "total_interest_amount", + "fieldtype": "Currency", + "label": "Total Interest Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "total_loan_repayment", + "fieldtype": "Currency", + "label": "Total Loan Repayment", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "net_pay_info", + "fieldtype": "Section Break", + "label": "Net Pay Info" + }, + { + "fieldname": "net_pay", + "fieldtype": "Currency", + "label": "Net Pay", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "section_break_55", + "fieldtype": "Section Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Salary Slip", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Select", + "label": "Mode Of Payment", + "read_only": 1 + }, + { + "fieldname": "absent_days", + "fieldtype": "Float", + "label": "Absent Days", + "read_only": 1 + }, + { + "fieldname": "unmarked_days", + "fieldtype": "Float", + "hidden": 1, + "label": "Unmarked days" + }, + { + "fieldname": "section_break_20", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", + "fetch_from": "salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "total_deduction", + "fieldtype": "Currency", + "label": "Total Deduction", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "total_in_words", + "fieldtype": "Data", + "label": "Total in words", + "length": 240, + "read_only": 1 + }, + { + "fieldname": "section_break_75", + "fieldtype": "Section Break" + }, + { + "fieldname": "base_hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate (Company Currency)", + "options": "Company:company:default_currency", + "print_hide_if_no_value": 1 + }, + { + "fieldname": "base_gross_pay", + "fieldtype": "Currency", + "label": "Gross Pay (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "default": "1.0", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Exchange Rate", + "print_hide": 1, + "reqd": 1 + }, + { + "fieldname": "base_total_deduction", + "fieldtype": "Currency", + "label": "Total Deduction (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "base_net_pay", + "fieldtype": "Currency", + "label": "Net Pay (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "bold": 1, + "fieldname": "base_rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "base_total_in_words", + "fieldtype": "Data", + "label": "Total in words (Company Currency)", + "length": 240, + "read_only": 1 + }, + { + "fieldname": "column_break_69", + "fieldtype": "Column Break" + }, + { + "description": "Total salary booked for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.", + "fieldname": "year_to_date", + "fieldtype": "Currency", + "label": "Year To Date", + "options": "currency", + "read_only": 1 + }, + { + "description": "Total salary booked for this employee from the beginning of the month up to the current salary slip's end date.", + "fieldname": "month_to_date", + "fieldtype": "Currency", + "label": "Month To Date", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "base_year_to_date", + "fieldtype": "Currency", + "label": "Year To Date(Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "base_month_to_date", + "fieldtype": "Currency", + "label": "Month To Date(Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "leave_details_section", + "fieldtype": "Section Break", + "label": "Leave Details" + }, + { + "fieldname": "leave_details", + "fieldtype": "Table", + "label": "Leave Details", + "options": "Salary Slip Leave", + "read_only": 1 + }, + { + "fieldname": "gross_year_to_date", + "fieldtype": "Currency", + "label": "Gross Year To Date", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "base_gross_year_to_date", + "fieldtype": "Currency", + "label": "Gross Year To Date(Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + } + ], + "icon": "fa fa-file-text", + "idx": 9, + "is_submittable": 1, + "links": [], + "modified": "2022-06-15 18:36:54.999345", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "search_fields": "employee_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "timeline_field": "employee", + "title_field": "employee_name" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_slip/salary_slip.py b/hrms/payroll/doctype/salary_slip/salary_slip.py new file mode 100644 index 0000000..93db426 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip/salary_slip.py @@ -0,0 +1,1827 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import datetime +import math + +import frappe +from frappe import _, msgprint +from frappe.model.naming import make_autoname +from frappe.utils import ( + add_days, + cint, + cstr, + date_diff, + flt, + formatdate, + get_first_day, + getdate, + money_in_words, + rounded, +) +from frappe.utils.background_jobs import enqueue + +import erpnext +from erpnext.accounts.utils import get_fiscal_year +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + calculate_amounts, + create_repayment_entry, +) +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( + process_loan_interest_accrual_for_term_loans, +) +from erpnext.utilities.transaction_base import TransactionBase + +from hrms.hr.utils import get_holiday_dates_for_employee, validate_active_employee +from hrms.payroll.doctype.additional_salary.additional_salary import get_additional_salaries +from hrms.payroll.doctype.employee_benefit_application.employee_benefit_application import ( + get_benefit_component_amount, +) +from hrms.payroll.doctype.employee_benefit_claim.employee_benefit_claim import ( + get_benefit_claim_amount, + get_last_payroll_period_benefits, +) +from hrms.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates +from hrms.payroll.doctype.payroll_period.payroll_period import ( + get_payroll_period, + get_period_factor, +) + + +class SalarySlip(TransactionBase): + def __init__(self, *args, **kwargs): + super(SalarySlip, self).__init__(*args, **kwargs) + self.series = "Sal Slip/{0}/.#####".format(self.employee) + self.whitelisted_globals = { + "int": int, + "float": float, + "long": int, + "round": round, + "date": datetime.date, + "getdate": getdate, + } + + def autoname(self): + self.name = make_autoname(self.series) + + def validate(self): + self.status = self.get_status() + validate_active_employee(self.employee) + self.validate_dates() + self.check_existing() + if not self.salary_slip_based_on_timesheet: + self.get_date_details() + + if not (len(self.get("earnings")) or len(self.get("deductions"))): + # get details from salary structure + self.get_emp_and_working_day_details() + else: + self.get_working_days_details(lwp=self.leave_without_pay) + + self.calculate_net_pay() + self.compute_year_to_date() + self.compute_month_to_date() + self.compute_component_wise_year_to_date() + self.add_leave_balances() + + if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"): + max_working_hours = frappe.db.get_single_value( + "Payroll Settings", "max_working_hours_against_timesheet" + ) + if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)): + frappe.msgprint( + _("Total working hours should not be greater than max working hours {0}").format( + max_working_hours + ), + alert=True, + ) + + def set_net_total_in_words(self): + doc_currency = self.currency + company_currency = erpnext.get_company_currency(self.company) + total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total + base_total = self.base_net_pay if self.is_rounding_total_disabled() else self.base_rounded_total + self.total_in_words = money_in_words(total, doc_currency) + self.base_total_in_words = money_in_words(base_total, company_currency) + + def on_submit(self): + if self.net_pay < 0: + frappe.throw(_("Net Pay cannot be less than 0")) + else: + self.set_status() + self.update_status(self.name) + self.make_loan_repayment_entry() + if ( + frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee") + ) and not frappe.flags.via_payroll_entry: + self.email_salary_slip() + + self.update_payment_status_for_gratuity() + + def update_payment_status_for_gratuity(self): + additional_salary = frappe.db.get_all( + "Additional Salary", + filters={ + "payroll_date": ("between", [self.start_date, self.end_date]), + "employee": self.employee, + "ref_doctype": "Gratuity", + "docstatus": 1, + }, + fields=["ref_docname", "name"], + limit=1, + ) + + if additional_salary: + status = "Paid" if self.docstatus == 1 else "Unpaid" + if additional_salary[0].name in [entry.additional_salary for entry in self.earnings]: + frappe.db.set_value("Gratuity", additional_salary[0].ref_docname, "status", status) + + def on_cancel(self): + self.set_status() + self.update_status() + self.update_payment_status_for_gratuity() + self.cancel_loan_repayment_entry() + + def on_trash(self): + from frappe.model.naming import revert_series_if_last + + revert_series_if_last(self.series, self.name) + + def get_status(self): + if self.docstatus == 0: + status = "Draft" + elif self.docstatus == 1: + status = "Submitted" + elif self.docstatus == 2: + status = "Cancelled" + return status + + def validate_dates(self, joining_date=None, relieving_date=None): + if date_diff(self.end_date, self.start_date) < 0: + frappe.throw(_("To date cannot be before From date")) + + if not joining_date: + joining_date, relieving_date = frappe.get_cached_value( + "Employee", self.employee, ("date_of_joining", "relieving_date") + ) + + if date_diff(self.end_date, joining_date) < 0: + frappe.throw(_("Cannot create Salary Slip for Employee joining after Payroll Period")) + + if relieving_date and date_diff(relieving_date, self.start_date) < 0: + frappe.throw(_("Cannot create Salary Slip for Employee who has left before Payroll Period")) + + def is_rounding_total_disabled(self): + return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total")) + + def check_existing(self): + if not self.salary_slip_based_on_timesheet: + cond = "" + if self.payroll_entry: + cond += "and payroll_entry = '{0}'".format(self.payroll_entry) + ret_exist = frappe.db.sql( + """select name from `tabSalary Slip` + where start_date = %s and end_date = %s and docstatus != 2 + and employee = %s and name != %s {0}""".format( + cond + ), + (self.start_date, self.end_date, self.employee, self.name), + ) + if ret_exist: + frappe.throw( + _("Salary Slip of employee {0} already created for this period").format(self.employee) + ) + else: + for data in self.timesheets: + if frappe.db.get_value("Timesheet", data.time_sheet, "status") == "Payrolled": + frappe.throw( + _("Salary Slip of employee {0} already created for time sheet {1}").format( + self.employee, data.time_sheet + ) + ) + + def get_date_details(self): + if not self.end_date: + date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date) + self.start_date = date_details.start_date + self.end_date = date_details.end_date + + @frappe.whitelist() + def get_emp_and_working_day_details(self): + """First time, load all the components from salary structure""" + if self.employee: + self.set("earnings", []) + self.set("deductions", []) + + if not self.salary_slip_based_on_timesheet: + self.get_date_details() + + joining_date, relieving_date = frappe.get_cached_value( + "Employee", self.employee, ("date_of_joining", "relieving_date") + ) + + self.validate_dates(joining_date, relieving_date) + + # getin leave details + self.get_working_days_details(joining_date, relieving_date) + struct = self.check_sal_struct(joining_date, relieving_date) + + if struct: + self._salary_structure_doc = frappe.get_doc("Salary Structure", struct) + self.salary_slip_based_on_timesheet = ( + self._salary_structure_doc.salary_slip_based_on_timesheet or 0 + ) + self.set_time_sheet() + self.pull_sal_struct() + ps = frappe.db.get_value( + "Payroll Settings", None, ["payroll_based_on", "consider_unmarked_attendance_as"], as_dict=1 + ) + return [ps.payroll_based_on, ps.consider_unmarked_attendance_as] + + def set_time_sheet(self): + if self.salary_slip_based_on_timesheet: + self.set("timesheets", []) + timesheets = frappe.db.sql( + """ select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or + status = 'Billed')""", + {"employee": self.employee, "start_date": self.start_date, "end_date": self.end_date}, + as_dict=1, + ) + + for data in timesheets: + self.append("timesheets", {"time_sheet": data.name, "working_hours": data.total_hours}) + + def check_sal_struct(self, joining_date, relieving_date): + cond = """and sa.employee=%(employee)s and (sa.from_date <= %(start_date)s or + sa.from_date <= %(end_date)s or sa.from_date <= %(joining_date)s)""" + if self.payroll_frequency: + cond += """and ss.payroll_frequency = '%(payroll_frequency)s'""" % { + "payroll_frequency": self.payroll_frequency + } + + st_name = frappe.db.sql( + """ + select sa.salary_structure + from `tabSalary Structure Assignment` sa join `tabSalary Structure` ss + where sa.salary_structure=ss.name + and sa.docstatus = 1 and ss.docstatus = 1 and ss.is_active ='Yes' %s + order by sa.from_date desc + limit 1 + """ + % cond, + { + "employee": self.employee, + "start_date": self.start_date, + "end_date": self.end_date, + "joining_date": joining_date, + }, + ) + + if st_name: + self.salary_structure = st_name[0][0] + return self.salary_structure + + else: + self.salary_structure = None + frappe.msgprint( + _("No active or default Salary Structure found for employee {0} for the given dates").format( + self.employee + ), + title=_("Salary Structure Missing"), + ) + + def pull_sal_struct(self): + from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip + + if self.salary_slip_based_on_timesheet: + self.salary_structure = self._salary_structure_doc.name + self.hour_rate = self._salary_structure_doc.hour_rate + self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate) + self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0 + wages_amount = self.hour_rate * self.total_working_hours + + self.add_earning_for_hourly_wages( + self, self._salary_structure_doc.salary_component, wages_amount + ) + + make_salary_slip(self._salary_structure_doc.name, self) + + def get_working_days_details( + self, joining_date=None, relieving_date=None, lwp=None, for_preview=0 + ): + payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") + include_holidays_in_total_working_days = frappe.db.get_single_value( + "Payroll Settings", "include_holidays_in_total_working_days" + ) + + working_days = date_diff(self.end_date, self.start_date) + 1 + if for_preview: + self.total_working_days = working_days + self.payment_days = working_days + return + + holidays = self.get_holidays_for_employee(self.start_date, self.end_date) + + if not cint(include_holidays_in_total_working_days): + working_days -= len(holidays) + if working_days < 0: + frappe.throw(_("There are more holidays than working days this month.")) + + if not payroll_based_on: + frappe.throw(_("Please set Payroll based on in Payroll settings")) + + if payroll_based_on == "Attendance": + actual_lwp, absent = self.calculate_lwp_ppl_and_absent_days_based_on_attendance(holidays) + self.absent_days = absent + else: + actual_lwp = self.calculate_lwp_or_ppl_based_on_leave_application(holidays, working_days) + + if not lwp: + lwp = actual_lwp + elif lwp != actual_lwp: + frappe.msgprint( + _("Leave Without Pay does not match with approved {} records").format(payroll_based_on) + ) + + self.leave_without_pay = lwp + self.total_working_days = working_days + + payment_days = self.get_payment_days( + joining_date, relieving_date, include_holidays_in_total_working_days + ) + + if flt(payment_days) > flt(lwp): + self.payment_days = flt(payment_days) - flt(lwp) + + if payroll_based_on == "Attendance": + self.payment_days -= flt(absent) + + consider_unmarked_attendance_as = ( + frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" + ) + + if payroll_based_on == "Attendance" and consider_unmarked_attendance_as == "Absent": + unmarked_days = self.get_unmarked_days(include_holidays_in_total_working_days) + self.absent_days += unmarked_days # will be treated as absent + self.payment_days -= unmarked_days + else: + self.payment_days = 0 + + def get_unmarked_days(self, include_holidays_in_total_working_days): + unmarked_days = self.total_working_days + joining_date, relieving_date = frappe.get_cached_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) + start_date = self.start_date + end_date = self.end_date + + if joining_date and (getdate(self.start_date) < joining_date <= getdate(self.end_date)): + start_date = joining_date + unmarked_days = self.get_unmarked_days_based_on_doj_or_relieving( + unmarked_days, + include_holidays_in_total_working_days, + self.start_date, + add_days(joining_date, -1), + ) + + if relieving_date and (getdate(self.start_date) <= relieving_date < getdate(self.end_date)): + end_date = relieving_date + unmarked_days = self.get_unmarked_days_based_on_doj_or_relieving( + unmarked_days, + include_holidays_in_total_working_days, + add_days(relieving_date, 1), + self.end_date, + ) + + # exclude days for which attendance has been marked + unmarked_days -= frappe.get_all( + "Attendance", + filters={ + "attendance_date": ["between", [start_date, end_date]], + "employee": self.employee, + "docstatus": 1, + }, + fields=["COUNT(*) as marked_days"], + )[0].marked_days + + return unmarked_days + + def get_unmarked_days_based_on_doj_or_relieving( + self, unmarked_days, include_holidays_in_total_working_days, start_date, end_date + ): + """ + Exclude days before DOJ or after + Relieving Date from unmarked days + """ + from erpnext.setup.doctype.employee.employee import is_holiday + + if include_holidays_in_total_working_days: + unmarked_days -= date_diff(end_date, start_date) + 1 + else: + # exclude only if not holidays + for days in range(date_diff(end_date, start_date) + 1): + date = add_days(end_date, -days) + if not is_holiday(self.employee, date): + unmarked_days -= 1 + + return unmarked_days + + def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days): + if not joining_date: + joining_date, relieving_date = frappe.get_cached_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) + + start_date = getdate(self.start_date) + if joining_date: + if getdate(self.start_date) <= joining_date <= getdate(self.end_date): + start_date = joining_date + elif joining_date > getdate(self.end_date): + return + + end_date = getdate(self.end_date) + if relieving_date: + if getdate(self.start_date) <= relieving_date <= getdate(self.end_date): + end_date = relieving_date + elif relieving_date < getdate(self.start_date): + frappe.throw(_("Employee relieved on {0} must be set as 'Left'").format(relieving_date)) + + payment_days = date_diff(end_date, start_date) + 1 + + if not cint(include_holidays_in_total_working_days): + holidays = self.get_holidays_for_employee(start_date, end_date) + payment_days -= len(holidays) + + return payment_days + + def get_holidays_for_employee(self, start_date, end_date): + return get_holiday_dates_for_employee(self.employee, start_date, end_date) + + def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days): + lwp = 0 + holidays = "','".join(holidays) + daily_wages_fraction_for_half_day = ( + flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 + ) + + for d in range(working_days): + date = add_days(cstr(getdate(self.start_date)), d) + leave = get_lwp_or_ppl_for_date(date, self.employee, holidays) + + if leave: + equivalent_lwp_count = 0 + is_half_day_leave = cint(leave[0].is_half_day) + is_partially_paid_leave = cint(leave[0].is_ppl) + fraction_of_daily_salary_per_leave = flt(leave[0].fraction_of_daily_salary_per_leave) + + equivalent_lwp_count = (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1 + + if is_partially_paid_leave: + equivalent_lwp_count *= ( + fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 + ) + + lwp += equivalent_lwp_count + + return lwp + + def calculate_lwp_ppl_and_absent_days_based_on_attendance(self, holidays): + lwp = 0 + absent = 0 + + daily_wages_fraction_for_half_day = ( + flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 + ) + + leave_types = frappe.get_all( + "Leave Type", + or_filters=[["is_ppl", "=", 1], ["is_lwp", "=", 1]], + fields=["name", "is_lwp", "is_ppl", "fraction_of_daily_salary_per_leave", "include_holiday"], + ) + + leave_type_map = {} + for leave_type in leave_types: + leave_type_map[leave_type.name] = leave_type + + attendances = frappe.db.sql( + """ + SELECT attendance_date, status, leave_type + FROM `tabAttendance` + WHERE + status in ('Absent', 'Half Day', 'On leave') + AND employee = %s + AND docstatus = 1 + AND attendance_date between %s and %s + """, + values=(self.employee, self.start_date, self.end_date), + as_dict=1, + ) + + for d in attendances: + if ( + d.status in ("Half Day", "On Leave") + and d.leave_type + and d.leave_type not in leave_type_map.keys() + ): + continue + + if formatdate(d.attendance_date, "yyyy-mm-dd") in holidays: + if d.status == "Absent" or ( + d.leave_type + and d.leave_type in leave_type_map.keys() + and not leave_type_map[d.leave_type]["include_holiday"] + ): + continue + + if d.leave_type: + fraction_of_daily_salary_per_leave = leave_type_map[d.leave_type][ + "fraction_of_daily_salary_per_leave" + ] + + if d.status == "Half Day": + equivalent_lwp = 1 - daily_wages_fraction_for_half_day + + if d.leave_type in leave_type_map.keys() and leave_type_map[d.leave_type]["is_ppl"]: + equivalent_lwp *= ( + fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 + ) + lwp += equivalent_lwp + elif d.status == "On Leave" and d.leave_type and d.leave_type in leave_type_map.keys(): + equivalent_lwp = 1 + if leave_type_map[d.leave_type]["is_ppl"]: + equivalent_lwp *= ( + fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 + ) + lwp += equivalent_lwp + elif d.status == "Absent": + absent += 1 + return lwp, absent + + def add_earning_for_hourly_wages(self, doc, salary_component, amount): + row_exists = False + for row in doc.earnings: + if row.salary_component == salary_component: + row.amount = amount + row_exists = True + break + + if not row_exists: + wages_row = { + "salary_component": salary_component, + "abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"), + "amount": self.hour_rate * self.total_working_hours, + "default_amount": 0.0, + "additional_amount": 0.0, + } + doc.append("earnings", wages_row) + + def calculate_net_pay(self): + if self.salary_structure: + self.calculate_component_amounts("earnings") + self.gross_pay = self.get_component_totals("earnings", depends_on_payment_days=1) + self.base_gross_pay = flt( + flt(self.gross_pay) * flt(self.exchange_rate), self.precision("base_gross_pay") + ) + + if self.salary_structure: + self.calculate_component_amounts("deductions") + + self.set_loan_repayment() + self.set_precision_for_component_amounts() + self.set_net_pay() + + def set_net_pay(self): + self.total_deduction = self.get_component_totals("deductions") + self.base_total_deduction = flt( + flt(self.total_deduction) * flt(self.exchange_rate), self.precision("base_total_deduction") + ) + self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) + self.rounded_total = rounded(self.net_pay) + self.base_net_pay = flt( + flt(self.net_pay) * flt(self.exchange_rate), self.precision("base_net_pay") + ) + self.base_rounded_total = flt(rounded(self.base_net_pay), self.precision("base_net_pay")) + if self.hour_rate: + self.base_hour_rate = flt( + flt(self.hour_rate) * flt(self.exchange_rate), self.precision("base_hour_rate") + ) + self.set_net_total_in_words() + + def calculate_component_amounts(self, component_type): + if not getattr(self, "_salary_structure_doc", None): + self._salary_structure_doc = frappe.get_doc("Salary Structure", self.salary_structure) + + payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) + + self.add_structure_components(component_type) + self.add_additional_salary_components(component_type) + if component_type == "earnings": + self.add_employee_benefits(payroll_period) + else: + self.add_tax_components(payroll_period) + + def add_structure_components(self, component_type): + data, default_data = self.get_data_for_eval() + timesheet_component = frappe.db.get_value( + "Salary Structure", self.salary_structure, "salary_component" + ) + + for struct_row in self._salary_structure_doc.get(component_type): + if self.salary_slip_based_on_timesheet and struct_row.salary_component == timesheet_component: + continue + + amount = self.eval_condition_and_formula(struct_row, data) + + if struct_row.statistical_component: + # update statitical component amount in reference data based on payment days + # since row for statistical component is not added to salary slip + if struct_row.depends_on_payment_days: + joining_date, relieving_date = self.get_joining_and_relieving_dates() + default_data[struct_row.abbr] = amount + data[struct_row.abbr] = flt( + (flt(amount) * flt(self.payment_days) / cint(self.total_working_days)), + struct_row.precision("amount"), + ) + + elif amount or struct_row.amount_based_on_formula and amount is not None: + default_amount = self.eval_condition_and_formula(struct_row, default_data) + self.update_component_row( + struct_row, amount, component_type, data=data, default_amount=default_amount + ) + + def get_data_for_eval(self): + """Returns data for evaluating formula""" + data = frappe._dict() + employee = frappe.get_doc("Employee", self.employee).as_dict() + + start_date = getdate(self.start_date) + date_to_validate = ( + employee.date_of_joining if employee.date_of_joining > start_date else start_date + ) + + salary_structure_assignment = frappe.get_value( + "Salary Structure Assignment", + { + "employee": self.employee, + "salary_structure": self.salary_structure, + "from_date": ("<=", date_to_validate), + "docstatus": 1, + }, + "*", + order_by="from_date desc", + as_dict=True, + ) + + if not salary_structure_assignment: + frappe.throw( + _( + "Please assign a Salary Structure for Employee {0} " "applicable from or before {1} first" + ).format( + frappe.bold(self.employee_name), + frappe.bold(formatdate(date_to_validate)), + ) + ) + + data.update(salary_structure_assignment) + data.update(employee) + data.update(self.as_dict()) + + # set values for components + salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"]) + for sc in salary_components: + data.setdefault(sc.salary_component_abbr, 0) + + # shallow copy of data to store default amounts (without payment days) for tax calculation + default_data = data.copy() + + for key in ("earnings", "deductions"): + for d in self.get(key): + default_data[d.abbr] = d.default_amount or 0 + data[d.abbr] = d.amount or 0 + + return data, default_data + + def eval_condition_and_formula(self, d, data): + try: + condition = d.condition.strip().replace("\n", " ") if d.condition else None + if condition: + if not frappe.safe_eval(condition, self.whitelisted_globals, data): + return None + amount = d.amount + if d.amount_based_on_formula: + formula = d.formula.strip().replace("\n", " ") if d.formula else None + if formula: + amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount")) + if amount: + data[d.abbr] = amount + + return amount + + except NameError as err: + frappe.throw( + _("{0}
This error can be due to missing or deleted field.").format(err), + title=_("Name error"), + ) + except SyntaxError as err: + frappe.throw(_("Syntax error in formula or condition: {0}").format(err)) + except Exception as e: + frappe.throw(_("Error in formula or condition: {0}").format(e)) + raise + + def add_employee_benefits(self, payroll_period): + for struct_row in self._salary_structure_doc.get("earnings"): + if struct_row.is_flexible_benefit == 1: + if ( + frappe.db.get_value( + "Salary Component", struct_row.salary_component, "pay_against_benefit_claim" + ) + != 1 + ): + benefit_component_amount = get_benefit_component_amount( + self.employee, + self.start_date, + self.end_date, + struct_row.salary_component, + self._salary_structure_doc, + self.payroll_frequency, + payroll_period, + ) + if benefit_component_amount: + self.update_component_row(struct_row, benefit_component_amount, "earnings") + else: + benefit_claim_amount = get_benefit_claim_amount( + self.employee, self.start_date, self.end_date, struct_row.salary_component + ) + if benefit_claim_amount: + self.update_component_row(struct_row, benefit_claim_amount, "earnings") + + self.adjust_benefits_in_last_payroll_period(payroll_period) + + def adjust_benefits_in_last_payroll_period(self, payroll_period): + if payroll_period: + if getdate(payroll_period.end_date) <= getdate(self.end_date): + last_benefits = get_last_payroll_period_benefits( + self.employee, self.start_date, self.end_date, payroll_period, self._salary_structure_doc + ) + if last_benefits: + for last_benefit in last_benefits: + last_benefit = frappe._dict(last_benefit) + amount = last_benefit.amount + self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") + + def add_additional_salary_components(self, component_type): + additional_salaries = get_additional_salaries( + self.employee, self.start_date, self.end_date, component_type + ) + + for additional_salary in additional_salaries: + self.update_component_row( + get_salary_component_data(additional_salary.component), + additional_salary.amount, + component_type, + additional_salary, + is_recurring=additional_salary.is_recurring, + ) + + def add_tax_components(self, payroll_period): + # Calculate variable_based_on_taxable_salary after all components updated in salary slip + tax_components, other_deduction_components = [], [] + for d in self._salary_structure_doc.get("deductions"): + if d.variable_based_on_taxable_salary == 1 and not d.formula and not flt(d.amount): + tax_components.append(d.salary_component) + else: + other_deduction_components.append(d.salary_component) + + if not tax_components: + tax_components = [ + d.name + for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1}) + if d.name not in other_deduction_components + ] + + for d in tax_components: + tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period) + tax_row = get_salary_component_data(d) + self.update_component_row(tax_row, tax_amount, "deductions") + + def update_component_row( + self, + component_data, + amount, + component_type, + additional_salary=None, + is_recurring=0, + data=None, + default_amount=None, + ): + component_row = None + for d in self.get(component_type): + if d.salary_component != component_data.salary_component: + continue + + if (not d.additional_salary and (not additional_salary or additional_salary.overwrite)) or ( + additional_salary and additional_salary.name == d.additional_salary + ): + component_row = d + break + + if additional_salary and additional_salary.overwrite: + # Additional Salary with overwrite checked, remove default rows of same component + self.set( + component_type, + [ + d + for d in self.get(component_type) + if d.salary_component != component_data.salary_component + or (d.additional_salary and additional_salary.name != d.additional_salary) + or d == component_row + ], + ) + + if not component_row: + if not amount: + return + + component_row = self.append(component_type) + for attr in ( + "depends_on_payment_days", + "salary_component", + "abbr", + "do_not_include_in_total", + "is_tax_applicable", + "is_flexible_benefit", + "variable_based_on_taxable_salary", + "exempted_from_income_tax", + ): + component_row.set(attr, component_data.get(attr)) + + if additional_salary: + if additional_salary.overwrite: + component_row.additional_amount = flt( + flt(amount) - flt(component_row.get("default_amount", 0)), + component_row.precision("additional_amount"), + ) + else: + component_row.default_amount = 0 + component_row.additional_amount = amount + + component_row.is_recurring_additional_salary = is_recurring + component_row.additional_salary = additional_salary.name + component_row.deduct_full_tax_on_selected_payroll_date = ( + additional_salary.deduct_full_tax_on_selected_payroll_date + ) + else: + component_row.default_amount = default_amount or amount + component_row.additional_amount = 0 + component_row.deduct_full_tax_on_selected_payroll_date = ( + component_data.deduct_full_tax_on_selected_payroll_date + ) + + component_row.amount = amount + + self.update_component_amount_based_on_payment_days(component_row) + + if data: + data[component_row.abbr] = component_row.amount + + def update_component_amount_based_on_payment_days(self, component_row): + joining_date, relieving_date = self.get_joining_and_relieving_dates() + component_row.amount = self.get_amount_based_on_payment_days( + component_row, joining_date, relieving_date + )[0] + + # remove 0 valued components that have been updated later + if component_row.amount == 0: + self.remove(component_row) + + def set_precision_for_component_amounts(self): + for component_type in ("earnings", "deductions"): + for component_row in self.get(component_type): + component_row.amount = flt(component_row.amount, component_row.precision("amount")) + + def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period): + if not payroll_period: + frappe.msgprint( + _("Start and end dates not in a valid Payroll Period, cannot calculate {0}.").format( + tax_component + ) + ) + return + + # Deduct taxes forcefully for unsubmitted tax exemption proof and unclaimed benefits in the last period + if payroll_period.end_date <= getdate(self.end_date): + self.deduct_tax_for_unsubmitted_tax_exemption_proof = 1 + self.deduct_tax_for_unclaimed_employee_benefits = 1 + + return self.calculate_variable_tax(payroll_period, tax_component) + + def calculate_variable_tax(self, payroll_period, tax_component): + # get Tax slab from salary structure assignment for the employee and payroll period + tax_slab = self.get_income_tax_slabs(payroll_period) + + # get remaining numbers of sub-period (period for which one salary is processed) + remaining_sub_periods = get_period_factor( + self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period + )[1] + # get taxable_earnings, paid_taxes for previous period + previous_taxable_earnings = self.get_taxable_earnings_for_prev_period( + payroll_period.start_date, self.start_date, tax_slab.allow_tax_exemption + ) + previous_total_paid_taxes = self.get_tax_paid_in_period( + payroll_period.start_date, self.start_date, tax_component + ) + + # get taxable_earnings for current period (all days) + current_taxable_earnings = self.get_taxable_earnings( + tax_slab.allow_tax_exemption, payroll_period=payroll_period + ) + future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * ( + math.ceil(remaining_sub_periods) - 1 + ) + + # get taxable_earnings, addition_earnings for current actual payment days + current_taxable_earnings_for_payment_days = self.get_taxable_earnings( + tax_slab.allow_tax_exemption, based_on_payment_days=1, payroll_period=payroll_period + ) + current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings + current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income + current_additional_earnings_with_full_tax = ( + current_taxable_earnings_for_payment_days.additional_income_with_full_tax + ) + + # Get taxable unclaimed benefits + unclaimed_taxable_benefits = 0 + if self.deduct_tax_for_unclaimed_employee_benefits: + unclaimed_taxable_benefits = self.calculate_unclaimed_taxable_benefits(payroll_period) + unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits + + # Total exemption amount based on tax exemption declaration + total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab) + + # Employee Other Incomes + other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0 + + # Total taxable earnings including additional and other incomes + total_taxable_earnings = ( + previous_taxable_earnings + + current_structured_taxable_earnings + + future_structured_taxable_earnings + + current_additional_earnings + + other_incomes + + unclaimed_taxable_benefits + - total_exemption_amount + ) + + # Total taxable earnings without additional earnings with full tax + total_taxable_earnings_without_full_tax_addl_components = ( + total_taxable_earnings - current_additional_earnings_with_full_tax + ) + + # Structured tax amount + eval_locals, default_data = self.get_data_for_eval() + total_structured_tax_amount = calculate_tax_by_tax_slab( + total_taxable_earnings_without_full_tax_addl_components, + tax_slab, + self.whitelisted_globals, + eval_locals, + ) + current_structured_tax_amount = ( + total_structured_tax_amount - previous_total_paid_taxes + ) / remaining_sub_periods + + # Total taxable earnings with additional earnings with full tax + full_tax_on_additional_earnings = 0.0 + if current_additional_earnings_with_full_tax: + total_tax_amount = calculate_tax_by_tax_slab( + total_taxable_earnings, tax_slab, self.whitelisted_globals, eval_locals + ) + full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount + + current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings + if flt(current_tax_amount) < 0: + current_tax_amount = 0 + + return current_tax_amount + + def get_income_tax_slabs(self, payroll_period): + income_tax_slab, ss_assignment_name = frappe.db.get_value( + "Salary Structure Assignment", + {"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, + ["income_tax_slab", "name"], + ) + + if not income_tax_slab: + frappe.throw( + _("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name) + ) + + income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab) + if income_tax_slab_doc.disabled: + frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab)) + + if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date): + frappe.throw( + _("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}").format( + payroll_period.start_date + ) + ) + + return income_tax_slab_doc + + def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False): + taxable_earnings = frappe.db.sql( + """ + select sum(sd.amount) + from + `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='earnings' + and sd.is_tax_applicable=1 + and is_flexible_benefit=0 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(from_date)s and %(to_date)s + and ss.end_date between %(from_date)s and %(to_date)s + """, + {"employee": self.employee, "from_date": start_date, "to_date": end_date}, + ) + taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0 + + exempted_amount = 0 + if allow_tax_exemption: + exempted_amount = frappe.db.sql( + """ + select sum(sd.amount) + from + `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='deductions' + and sd.exempted_from_income_tax=1 + and is_flexible_benefit=0 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(from_date)s and %(to_date)s + and ss.end_date between %(from_date)s and %(to_date)s + """, + {"employee": self.employee, "from_date": start_date, "to_date": end_date}, + ) + exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0 + + return taxable_earnings - exempted_amount + + def get_tax_paid_in_period(self, start_date, end_date, tax_component): + # find total_tax_paid, tax paid for benefit, additional_salary + total_tax_paid = flt( + frappe.db.sql( + """ + select + sum(sd.amount) + from + `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='deductions' + and sd.salary_component=%(salary_component)s + and sd.variable_based_on_taxable_salary=1 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(from_date)s and %(to_date)s + and ss.end_date between %(from_date)s and %(to_date)s + """, + { + "salary_component": tax_component, + "employee": self.employee, + "from_date": start_date, + "to_date": end_date, + }, + )[0][0] + ) + + return total_tax_paid + + def get_taxable_earnings( + self, allow_tax_exemption=False, based_on_payment_days=0, payroll_period=None + ): + joining_date, relieving_date = self.get_joining_and_relieving_dates() + + taxable_earnings = 0 + additional_income = 0 + additional_income_with_full_tax = 0 + flexi_benefits = 0 + + for earning in self.earnings: + if based_on_payment_days: + amount, additional_amount = self.get_amount_based_on_payment_days( + earning, joining_date, relieving_date + ) + else: + if earning.additional_amount: + amount, additional_amount = earning.amount, earning.additional_amount + else: + amount, additional_amount = earning.default_amount, earning.additional_amount + + if earning.is_tax_applicable: + if earning.is_flexible_benefit: + flexi_benefits += amount + else: + taxable_earnings += amount - additional_amount + additional_income += additional_amount + + # Get additional amount based on future recurring additional salary + if additional_amount and earning.is_recurring_additional_salary: + additional_income += self.get_future_recurring_additional_amount( + earning.additional_salary, earning.additional_amount, payroll_period + ) # Used earning.additional_amount to consider the amount for the full month + + if earning.deduct_full_tax_on_selected_payroll_date: + additional_income_with_full_tax += additional_amount + + if allow_tax_exemption: + for ded in self.deductions: + if ded.exempted_from_income_tax: + amount, additional_amount = ded.amount, ded.additional_amount + if based_on_payment_days: + amount, additional_amount = self.get_amount_based_on_payment_days( + ded, joining_date, relieving_date + ) + + taxable_earnings -= flt(amount - additional_amount) + additional_income -= additional_amount + + if additional_amount and ded.is_recurring_additional_salary: + additional_income -= self.get_future_recurring_additional_amount( + ded.additional_salary, ded.additional_amount, payroll_period + ) # Used ded.additional_amount to consider the amount for the full month + + return frappe._dict( + { + "taxable_earnings": taxable_earnings, + "additional_income": additional_income, + "additional_income_with_full_tax": additional_income_with_full_tax, + "flexi_benefits": flexi_benefits, + } + ) + + def get_future_recurring_additional_amount( + self, additional_salary, monthly_additional_amount, payroll_period + ): + future_recurring_additional_amount = 0 + to_date = frappe.db.get_value("Additional Salary", additional_salary, "to_date") + + # future month count excluding current + from_date, to_date = getdate(self.start_date), getdate(to_date) + + # If recurring period end date is beyond the payroll period, + # last day of payroll period should be considered for recurring period calculation + if getdate(to_date) > getdate(payroll_period.end_date): + to_date = getdate(payroll_period.end_date) + + future_recurring_period = ((to_date.year - from_date.year) * 12) + ( + to_date.month - from_date.month + ) + + if future_recurring_period > 0: + future_recurring_additional_amount = ( + monthly_additional_amount * future_recurring_period + ) # Used earning.additional_amount to consider the amount for the full month + return future_recurring_additional_amount + + def get_amount_based_on_payment_days(self, row, joining_date, relieving_date): + amount, additional_amount = row.amount, row.additional_amount + timesheet_component = frappe.db.get_value( + "Salary Structure", self.salary_structure, "salary_component" + ) + + if ( + self.salary_structure + and cint(row.depends_on_payment_days) + and cint(self.total_working_days) + and not ( + row.additional_salary and row.default_amount + ) # to identify overwritten additional salary + and ( + row.salary_component != timesheet_component + or getdate(self.start_date) < joining_date + or (relieving_date and getdate(self.end_date) > relieving_date) + ) + ): + additional_amount = flt( + (flt(row.additional_amount) * flt(self.payment_days) / cint(self.total_working_days)), + row.precision("additional_amount"), + ) + amount = ( + flt( + (flt(row.default_amount) * flt(self.payment_days) / cint(self.total_working_days)), + row.precision("amount"), + ) + + additional_amount + ) + + elif ( + not self.payment_days + and row.salary_component != timesheet_component + and cint(row.depends_on_payment_days) + ): + amount, additional_amount = 0, 0 + elif not row.amount: + amount = flt(row.default_amount) + flt(row.additional_amount) + + # apply rounding + if frappe.get_cached_value( + "Salary Component", row.salary_component, "round_to_the_nearest_integer" + ): + amount, additional_amount = rounded(amount or 0), rounded(additional_amount or 0) + + return amount, additional_amount + + def calculate_unclaimed_taxable_benefits(self, payroll_period): + # get total sum of benefits paid + total_benefits_paid = flt( + frappe.db.sql( + """ + select sum(sd.amount) + from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name + where + sd.parentfield='earnings' + and sd.is_tax_applicable=1 + and is_flexible_benefit=1 + and ss.docstatus=1 + and ss.employee=%(employee)s + and ss.start_date between %(start_date)s and %(end_date)s + and ss.end_date between %(start_date)s and %(end_date)s + """, + { + "employee": self.employee, + "start_date": payroll_period.start_date, + "end_date": self.start_date, + }, + )[0][0] + ) + + # get total benefits claimed + total_benefits_claimed = flt( + frappe.db.sql( + """ + select sum(claimed_amount) + from `tabEmployee Benefit Claim` + where + docstatus=1 + and employee=%s + and claim_date between %s and %s + """, + (self.employee, payroll_period.start_date, self.end_date), + )[0][0] + ) + + return total_benefits_paid - total_benefits_claimed + + def get_total_exemption_amount(self, payroll_period, tax_slab): + total_exemption_amount = 0 + if tax_slab.allow_tax_exemption: + if self.deduct_tax_for_unsubmitted_tax_exemption_proof: + exemption_proof = frappe.db.get_value( + "Employee Tax Exemption Proof Submission", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ["exemption_amount"], + ) + if exemption_proof: + total_exemption_amount = exemption_proof + else: + declaration = frappe.db.get_value( + "Employee Tax Exemption Declaration", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, + ["total_exemption_amount"], + ) + if declaration: + total_exemption_amount = declaration + + total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount) + + return total_exemption_amount + + def get_income_form_other_sources(self, payroll_period): + return frappe.get_all( + "Employee Other Income", + filters={ + "employee": self.employee, + "payroll_period": payroll_period.name, + "company": self.company, + "docstatus": 1, + }, + fields="SUM(amount) as total_amount", + )[0].total_amount + + def get_component_totals(self, component_type, depends_on_payment_days=0): + joining_date, relieving_date = frappe.get_cached_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) + + total = 0.0 + for d in self.get(component_type): + if not d.do_not_include_in_total: + if depends_on_payment_days: + amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] + else: + amount = flt(d.amount, d.precision("amount")) + total += amount + return total + + def get_joining_and_relieving_dates(self): + joining_date, relieving_date = frappe.get_cached_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) + + if not relieving_date: + relieving_date = getdate(self.end_date) + + if not joining_date: + frappe.throw( + _("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)) + ) + + return joining_date, relieving_date + + def set_loan_repayment(self): + self.total_loan_repayment = 0 + self.total_interest_amount = 0 + self.total_principal_amount = 0 + + if not self.get("loans"): + for loan in self.get_loan_details(): + + amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") + + if amounts["interest_amount"] or amounts["payable_principal_amount"]: + self.append( + "loans", + { + "loan": loan.name, + "total_payment": amounts["interest_amount"] + amounts["payable_principal_amount"], + "interest_amount": amounts["interest_amount"], + "principal_amount": amounts["payable_principal_amount"], + "loan_account": loan.loan_account, + "interest_income_account": loan.interest_income_account, + }, + ) + + for payment in self.get("loans"): + amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") + total_amount = amounts["interest_amount"] + amounts["payable_principal_amount"] + if payment.total_payment > total_amount: + frappe.throw( + _( + """Row {0}: Paid amount {1} is greater than pending accrued amount {2} against loan {3}""" + ).format( + payment.idx, + frappe.bold(payment.total_payment), + frappe.bold(total_amount), + frappe.bold(payment.loan), + ) + ) + + self.total_interest_amount += payment.interest_amount + self.total_principal_amount += payment.principal_amount + + self.total_loan_repayment += payment.total_payment + + def get_loan_details(self): + loan_details = frappe.get_all( + "Loan", + fields=["name", "interest_income_account", "loan_account", "loan_type", "is_term_loan"], + filters={ + "applicant": self.employee, + "docstatus": 1, + "repay_from_salary": 1, + "company": self.company, + }, + ) + + if loan_details: + for loan in loan_details: + if loan.is_term_loan: + process_loan_interest_accrual_for_term_loans( + posting_date=self.posting_date, loan_type=loan.loan_type, loan=loan.name + ) + + return loan_details + + def make_loan_repayment_entry(self): + payroll_payable_account = get_payroll_payable_account(self.company, self.payroll_entry) + for loan in self.loans: + if loan.total_payment: + repayment_entry = create_repayment_entry( + loan.loan, + self.employee, + self.company, + self.posting_date, + loan.loan_type, + "Regular Payment", + loan.interest_amount, + loan.principal_amount, + loan.total_payment, + payroll_payable_account=payroll_payable_account, + ) + + repayment_entry.save() + repayment_entry.submit() + + frappe.db.set_value( + "Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name + ) + + def cancel_loan_repayment_entry(self): + for loan in self.loans: + if loan.loan_repayment_entry: + repayment_entry = frappe.get_doc("Loan Repayment", loan.loan_repayment_entry) + repayment_entry.cancel() + + def email_salary_slip(self): + receiver = frappe.db.get_value("Employee", self.employee, "prefered_email") + payroll_settings = frappe.get_single("Payroll Settings") + message = "Please see attachment" + password = None + if payroll_settings.encrypt_salary_slips_in_emails: + password = generate_password_for_pdf(payroll_settings.password_policy, self.employee) + message += """
Note: Your salary slip is password protected, + the password to unlock the PDF is of the format {0}. """.format( + payroll_settings.password_policy + ) + + if receiver: + email_args = { + "recipients": [receiver], + "message": _(message), + "subject": "Salary Slip - from {0} to {1}".format(self.start_date, self.end_date), + "attachments": [ + frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password) + ], + "reference_doctype": self.doctype, + "reference_name": self.name, + } + if not frappe.flags.in_test: + enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args) + else: + frappe.sendmail(**email_args) + else: + msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name)) + + def update_status(self, salary_slip=None): + for data in self.timesheets: + if data.time_sheet: + timesheet = frappe.get_doc("Timesheet", data.time_sheet) + timesheet.salary_slip = salary_slip + timesheet.flags.ignore_validate_update_after_submit = True + timesheet.set_status() + timesheet.save() + + def set_status(self, status=None): + """Get and update status""" + if not status: + status = self.get_status() + self.db_set("status", status) + + def process_salary_structure(self, for_preview=0): + """Calculate salary after salary structure details have been updated""" + if not self.salary_slip_based_on_timesheet: + self.get_date_details() + self.pull_emp_details() + self.get_working_days_details(for_preview=for_preview) + self.calculate_net_pay() + + def pull_emp_details(self): + emp = frappe.db.get_value( + "Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1 + ) + if emp: + self.mode_of_payment = emp.salary_mode + self.bank_name = emp.bank_name + self.bank_account_no = emp.bank_ac_no + + @frappe.whitelist() + def process_salary_based_on_working_days(self): + self.get_working_days_details(lwp=self.leave_without_pay) + self.calculate_net_pay() + + @frappe.whitelist() + def set_totals(self): + self.gross_pay = 0.0 + if self.salary_slip_based_on_timesheet == 1: + self.calculate_total_for_salary_slip_based_on_timesheet() + else: + self.total_deduction = 0.0 + if hasattr(self, "earnings"): + for earning in self.earnings: + self.gross_pay += flt(earning.amount, earning.precision("amount")) + if hasattr(self, "deductions"): + for deduction in self.deductions: + self.total_deduction += flt(deduction.amount, deduction.precision("amount")) + self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment) + self.set_base_totals() + + def set_base_totals(self): + self.base_gross_pay = flt(self.gross_pay) * flt(self.exchange_rate) + self.base_total_deduction = flt(self.total_deduction) * flt(self.exchange_rate) + self.rounded_total = rounded(self.net_pay or 0) + self.base_net_pay = flt(self.net_pay) * flt(self.exchange_rate) + self.base_rounded_total = rounded(self.base_net_pay or 0) + self.set_net_total_in_words() + + # calculate total working hours, earnings based on hourly wages and totals + def calculate_total_for_salary_slip_based_on_timesheet(self): + if self.timesheets: + self.total_working_hours = 0 + for timesheet in self.timesheets: + if timesheet.working_hours: + self.total_working_hours += timesheet.working_hours + + wages_amount = self.total_working_hours * self.hour_rate + self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate) + salary_component = frappe.db.get_value( + "Salary Structure", {"name": self.salary_structure}, "salary_component" + ) + if self.earnings: + for i, earning in enumerate(self.earnings): + if earning.salary_component == salary_component: + self.earnings[i].amount = wages_amount + self.gross_pay += flt(self.earnings[i].amount, earning.precision("amount")) + self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) + + def compute_year_to_date(self): + year_to_date = 0 + period_start_date, period_end_date = self.get_year_to_date_period() + + salary_slip_sum = frappe.get_list( + "Salary Slip", + fields=["sum(net_pay) as net_sum", "sum(gross_pay) as gross_sum"], + filters={ + "employee": self.employee, + "start_date": [">=", period_start_date], + "end_date": ["<", period_end_date], + "name": ["!=", self.name], + "docstatus": 1, + }, + ) + + year_to_date = flt(salary_slip_sum[0].net_sum) if salary_slip_sum else 0.0 + gross_year_to_date = flt(salary_slip_sum[0].gross_sum) if salary_slip_sum else 0.0 + + year_to_date += self.net_pay + gross_year_to_date += self.gross_pay + self.year_to_date = year_to_date + self.gross_year_to_date = gross_year_to_date + + def compute_month_to_date(self): + month_to_date = 0 + first_day_of_the_month = get_first_day(self.start_date) + salary_slip_sum = frappe.get_list( + "Salary Slip", + fields=["sum(net_pay) as sum"], + filters={ + "employee": self.employee, + "start_date": [">=", first_day_of_the_month], + "end_date": ["<", self.start_date], + "name": ["!=", self.name], + "docstatus": 1, + }, + ) + + month_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0 + + month_to_date += self.net_pay + self.month_to_date = month_to_date + + def compute_component_wise_year_to_date(self): + period_start_date, period_end_date = self.get_year_to_date_period() + + for key in ("earnings", "deductions"): + for component in self.get(key): + year_to_date = 0 + component_sum = frappe.db.sql( + """ + SELECT sum(detail.amount) as sum + FROM `tabSalary Detail` as detail + INNER JOIN `tabSalary Slip` as salary_slip + ON detail.parent = salary_slip.name + WHERE + salary_slip.employee = %(employee)s + AND detail.salary_component = %(component)s + AND salary_slip.start_date >= %(period_start_date)s + AND salary_slip.end_date < %(period_end_date)s + AND salary_slip.name != %(docname)s + AND salary_slip.docstatus = 1""", + { + "employee": self.employee, + "component": component.salary_component, + "period_start_date": period_start_date, + "period_end_date": period_end_date, + "docname": self.name, + }, + ) + + year_to_date = flt(component_sum[0][0]) if component_sum else 0.0 + year_to_date += component.amount + component.year_to_date = year_to_date + + def get_year_to_date_period(self): + payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) + + if payroll_period: + period_start_date = payroll_period.start_date + period_end_date = payroll_period.end_date + else: + # get dates based on fiscal year if no payroll period exists + fiscal_year = get_fiscal_year(date=self.start_date, company=self.company, as_dict=1) + period_start_date = fiscal_year.year_start_date + period_end_date = fiscal_year.year_end_date + + return period_start_date, period_end_date + + def add_leave_balances(self): + self.set("leave_details", []) + + if frappe.db.get_single_value("Payroll Settings", "show_leave_balances_in_salary_slip"): + from hrms.hr.doctype.leave_application.leave_application import get_leave_details + + leave_details = get_leave_details(self.employee, self.end_date) + + for leave_type, leave_values in leave_details["leave_allocation"].items(): + self.append( + "leave_details", + { + "leave_type": leave_type, + "total_allocated_leaves": flt(leave_values.get("total_leaves")), + "expired_leaves": flt(leave_values.get("expired_leaves")), + "used_leaves": flt(leave_values.get("leaves_taken")), + "pending_leaves": flt(leave_values.get("leaves_pending_approval")), + "available_leaves": flt(leave_values.get("remaining_leaves")), + }, + ) + + +def unlink_ref_doc_from_salary_slip(doc, method=None): + """Unlinks accrual Journal Entry from Salary Slips on cancellation""" + linked_ss = frappe.db.sql_list( + """select name from `tabSalary Slip` + where journal_entry=%s and docstatus < 2""", + (doc.name), + ) + if linked_ss: + for ss in linked_ss: + ss_doc = frappe.get_doc("Salary Slip", ss) + frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "") + + +def generate_password_for_pdf(policy_template, employee): + employee = frappe.get_doc("Employee", employee) + return policy_template.format(**employee.as_dict()) + + +def get_salary_component_data(component): + return frappe.get_value( + "Salary Component", + component, + [ + "name as salary_component", + "depends_on_payment_days", + "salary_component_abbr as abbr", + "do_not_include_in_total", + "is_tax_applicable", + "is_flexible_benefit", + "variable_based_on_taxable_salary", + ], + as_dict=1, + ) + + +def get_payroll_payable_account(company, payroll_entry): + if payroll_entry: + payroll_payable_account = frappe.db.get_value( + "Payroll Entry", payroll_entry, "payroll_payable_account" + ) + else: + payroll_payable_account = frappe.db.get_value( + "Company", company, "default_payroll_payable_account" + ) + + return payroll_payable_account + + +def calculate_tax_by_tax_slab( + annual_taxable_earning, tax_slab, eval_globals=None, eval_locals=None +): + eval_locals.update({"annual_taxable_earning": annual_taxable_earning}) + tax_amount = 0 + for slab in tax_slab.slabs: + cond = cstr(slab.condition).strip() + if cond and not eval_tax_slab_condition(cond, eval_globals, eval_locals): + continue + if not slab.to_amount and annual_taxable_earning >= slab.from_amount: + tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction * 0.01 + continue + if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount: + tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction * 0.01 + elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount: + tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * 0.01 + + # other taxes and charges on income tax + for d in tax_slab.other_taxes_and_charges: + if flt(d.min_taxable_income) and flt(d.min_taxable_income) > annual_taxable_earning: + continue + + if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning: + continue + + tax_amount += tax_amount * flt(d.percent) / 100 + + return tax_amount + + +def eval_tax_slab_condition(condition, eval_globals=None, eval_locals=None): + if not eval_globals: + eval_globals = { + "int": int, + "float": float, + "long": int, + "round": round, + "date": datetime.date, + "getdate": getdate, + } + + try: + condition = condition.strip() + if condition: + return frappe.safe_eval(condition, eval_globals, eval_locals) + except NameError as err: + frappe.throw( + _("{0}
This error can be due to missing or deleted field.").format(err), + title=_("Name error"), + ) + except SyntaxError as err: + frappe.throw(_("Syntax error in condition: {0} in Income Tax Slab").format(err)) + except Exception as e: + frappe.throw(_("Error in formula or condition: {0} in Income Tax Slab").format(e)) + raise + + +def get_lwp_or_ppl_for_date(date, employee, holidays): + LeaveApplication = frappe.qb.DocType("Leave Application") + LeaveType = frappe.qb.DocType("Leave Type") + + is_half_day = ( + frappe.qb.terms.Case() + .when( + ( + (LeaveApplication.half_day_date == date) + | (LeaveApplication.from_date == LeaveApplication.to_date) + ), + LeaveApplication.half_day, + ) + .else_(0) + ).as_("is_half_day") + + query = ( + frappe.qb.from_(LeaveApplication) + .inner_join(LeaveType) + .on((LeaveType.name == LeaveApplication.leave_type)) + .select( + LeaveApplication.name, + LeaveType.is_ppl, + LeaveType.fraction_of_daily_salary_per_leave, + (is_half_day), + ) + .where( + (((LeaveType.is_lwp == 1) | (LeaveType.is_ppl == 1))) + & (LeaveApplication.docstatus == 1) + & (LeaveApplication.status == "Approved") + & (LeaveApplication.employee == employee) + & ((LeaveApplication.salary_slip.isnull()) | (LeaveApplication.salary_slip == "")) + & ((LeaveApplication.from_date <= date) & (date <= LeaveApplication.to_date)) + ) + ) + + # if it's a holiday only include if leave type has "include holiday" enabled + if date in holidays: + query = query.where((LeaveType.include_holiday == "1")) + + return query.run(as_dict=True) + + +@frappe.whitelist() +def make_salary_slip_from_timesheet(source_name, target_doc=None): + target = frappe.new_doc("Salary Slip") + set_missing_values(source_name, target) + target.run_method("get_emp_and_working_day_details") + + return target + + +def set_missing_values(time_sheet, target): + doc = frappe.get_doc("Timesheet", time_sheet) + target.employee = doc.employee + target.employee_name = doc.employee_name + target.salary_slip_based_on_timesheet = 1 + target.start_date = doc.start_date + target.end_date = doc.end_date + target.posting_date = doc.modified + target.total_working_hours = doc.total_hours + target.append("timesheets", {"time_sheet": doc.name, "working_hours": doc.total_hours}) diff --git a/hrms/payroll/doctype/salary_slip/salary_slip_list.js b/hrms/payroll/doctype/salary_slip/salary_slip_list.js new file mode 100644 index 0000000..33d5bd7 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip/salary_slip_list.js @@ -0,0 +1,3 @@ +frappe.listview_settings['Salary Slip'] = { + add_fields: ["employee", "employee_name"], +}; diff --git a/hrms/payroll/doctype/salary_slip/test_salary_slip.py b/hrms/payroll/doctype/salary_slip/test_salary_slip.py new file mode 100644 index 0000000..ba5cd88 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip/test_salary_slip.py @@ -0,0 +1,1715 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import calendar +import random +import unittest + +import frappe +from frappe.model.document import Document +from frappe.tests.utils import FrappeTestCase, change_settings +from frappe.utils import ( + add_days, + add_months, + cstr, + date_diff, + flt, + get_first_day, + get_last_day, + getdate, + nowdate, +) +from frappe.utils.make_random import get_random + +import erpnext +from erpnext.accounts.utils import get_fiscal_year +from erpnext.setup.doctype.employee.employee import InactiveEmployeeStatusError +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.hr.doctype.attendance.attendance import mark_attendance +from hrms.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation +from hrms.hr.doctype.leave_type.test_leave_type import create_leave_type +from hrms.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( + create_exemption_category, + create_payroll_period, +) +from hrms.payroll.doctype.payroll_entry.payroll_entry import get_month_details +from hrms.payroll.doctype.salary_slip.salary_slip import make_salary_slip_from_timesheet +from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from hrms.tests.test_utils import get_first_sunday + + +class TestSalarySlip(FrappeTestCase): + def setUp(self): + setup_test() + frappe.flags.pop("via_payroll_entry", None) + + def tearDown(self): + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) + frappe.set_user("Administrator") + + def test_employee_status_inactive(self): + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + employee = make_employee("test_employee_status@company.com") + employee_doc = frappe.get_doc("Employee", employee) + employee_doc.status = "Inactive" + employee_doc.save() + employee_doc.reload() + + make_holiday_list() + frappe.db.set_value( + "Company", employee_doc.company, "default_holiday_list", "Salary Slip Test Holiday List" + ) + + frappe.db.sql( + """delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""" + ) + salary_structure = make_salary_structure( + "Test Inactive Employee Salary Slip", + "Monthly", + employee=employee_doc.name, + company=employee_doc.company, + ) + salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name) + + self.assertRaises(InactiveEmployeeStatusError, salary_slip.save) + + @change_settings( + "Payroll Settings", {"payroll_based_on": "Attendance", "daily_wages_fraction_for_half_day": 0.75} + ) + def test_payment_days_based_on_attendance(self): + no_of_days = get_no_of_days() + + emp_id = make_employee("test_payment_days_based_on_attendance@salary.com") + frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) + + frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) + + first_sunday = get_first_sunday() + + mark_attendance(emp_id, first_sunday, "Absent", ignore_validate=True) # invalid lwp + mark_attendance( + emp_id, add_days(first_sunday, 1), "Absent", ignore_validate=True + ) # counted as absent + mark_attendance( + emp_id, + add_days(first_sunday, 2), + "Half Day", + leave_type="Leave Without Pay", + ignore_validate=True, + ) # valid 0.75 lwp + mark_attendance( + emp_id, + add_days(first_sunday, 3), + "On Leave", + leave_type="Leave Without Pay", + ignore_validate=True, + ) # valid lwp + mark_attendance( + emp_id, add_days(first_sunday, 4), "On Leave", leave_type="Casual Leave", ignore_validate=True + ) # invalid lwp + mark_attendance( + emp_id, + add_days(first_sunday, 7), + "On Leave", + leave_type="Leave Without Pay", + ignore_validate=True, + ) # invalid lwp + + ss = make_employee_salary_slip( + "test_payment_days_based_on_attendance@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + + self.assertEqual(ss.leave_without_pay, 1.25) + self.assertEqual(ss.absent_days, 1) + + days_in_month = no_of_days[0] + no_of_holidays = no_of_days[1] + + self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25) + + # Gross pay calculation based on attendances + gross_pay = 78000 - ( + (78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days) + ) + + self.assertEqual(ss.gross_pay, gross_pay) + + @change_settings( + "Payroll Settings", + { + "payroll_based_on": "Attendance", + "consider_unmarked_attendance_as": "Absent", + "include_holidays_in_total_working_days": True, + }, + ) + def test_payment_days_for_mid_joinee_including_holidays(self): + no_of_days = get_no_of_days() + month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) + + new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com") + joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5) + + for days in range(date_diff(month_end_date, month_start_date) + 1): + date = add_days(month_start_date, days) + mark_attendance(new_emp_id, date, "Present", ignore_validate=True) + + # Case 1: relieving in mid month + frappe.db.set_value( + "Employee", + new_emp_id, + {"date_of_joining": month_start_date, "relieving_date": relieving_date, "status": "Active"}, + ) + + new_ss = make_employee_salary_slip( + "test_payment_days_based_on_joining_date@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + self.assertEqual(new_ss.payment_days, no_of_days[0] - 5) + + # Case 2: joining in mid month + frappe.db.set_value( + "Employee", + new_emp_id, + {"date_of_joining": joining_date, "relieving_date": month_end_date, "status": "Active"}, + ) + + frappe.delete_doc("Salary Slip", new_ss.name, force=True) + new_ss = make_employee_salary_slip( + "test_payment_days_based_on_joining_date@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + self.assertEqual(new_ss.payment_days, no_of_days[0] - 3) + + # Case 3: joining and relieving in mid-month + frappe.db.set_value( + "Employee", + new_emp_id, + {"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"}, + ) + + frappe.delete_doc("Salary Slip", new_ss.name, force=True) + new_ss = make_employee_salary_slip( + "test_payment_days_based_on_joining_date@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + + self.assertEqual(new_ss.total_working_days, no_of_days[0]) + self.assertEqual(new_ss.payment_days, no_of_days[0] - 8) + + @change_settings( + "Payroll Settings", + { + "payroll_based_on": "Attendance", + "consider_unmarked_attendance_as": "Absent", + "include_holidays_in_total_working_days": True, + }, + ) + def test_payment_days_for_mid_joinee_including_holidays_and_unmarked_days(self): + # tests mid month joining and relieving along with unmarked days + from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday + + no_of_days = get_no_of_days() + month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) + + new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com") + joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5) + holidays = 0 + + for days in range(date_diff(relieving_date, joining_date) + 1): + date = add_days(joining_date, days) + if not is_holiday("Salary Slip Test Holiday List", date): + mark_attendance(new_emp_id, date, "Present", ignore_validate=True) + else: + holidays += 1 + + frappe.db.set_value( + "Employee", + new_emp_id, + {"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"}, + ) + + new_ss = make_employee_salary_slip( + "test_payment_days_based_on_joining_date@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + + self.assertEqual(new_ss.total_working_days, no_of_days[0]) + self.assertEqual(new_ss.payment_days, no_of_days[0] - holidays - 8) + + @change_settings( + "Payroll Settings", + { + "payroll_based_on": "Attendance", + "consider_unmarked_attendance_as": "Absent", + "include_holidays_in_total_working_days": False, + }, + ) + def test_payment_days_for_mid_joinee_excluding_holidays(self): + from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday + + no_of_days = get_no_of_days() + month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) + + new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com") + joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5) + frappe.db.set_value( + "Employee", + new_emp_id, + {"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"}, + ) + + holidays = 0 + + for days in range(date_diff(relieving_date, joining_date) + 1): + date = add_days(joining_date, days) + if not is_holiday("Salary Slip Test Holiday List", date): + mark_attendance(new_emp_id, date, "Present", ignore_validate=True) + else: + holidays += 1 + + new_ss = make_employee_salary_slip( + "test_payment_days_based_on_joining_date@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + + self.assertEqual(new_ss.total_working_days, no_of_days[0] - no_of_days[1]) + self.assertEqual(new_ss.payment_days, no_of_days[0] - holidays - 8) + + @change_settings("Payroll Settings", {"payroll_based_on": "Leave"}) + def test_payment_days_based_on_leave_application(self): + no_of_days = get_no_of_days() + + emp_id = make_employee("test_payment_days_based_on_leave_application@salary.com") + frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) + + frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) + + first_sunday = get_first_sunday() + + make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay") + + leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl=1) + leave_type_ppl.save() + + alloc = create_leave_allocation( + employee=emp_id, + from_date=add_days(first_sunday, 4), + to_date=add_days(first_sunday, 10), + new_leaves_allocated=3, + leave_type="Test Partially Paid Leave", + ) + alloc.save() + alloc.submit() + + # two day leave ppl with fraction_of_daily_salary_per_leave = 0.5 equivalent to single day lwp + make_leave_application( + emp_id, add_days(first_sunday, 4), add_days(first_sunday, 5), "Test Partially Paid Leave" + ) + + ss = make_employee_salary_slip( + "test_payment_days_based_on_leave_application@salary.com", + "Monthly", + "Test Payment Based On Leave Application", + ) + + self.assertEqual(ss.leave_without_pay, 4) + + days_in_month = no_of_days[0] + no_of_holidays = no_of_days[1] + + self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 4) + + @change_settings("Payroll Settings", {"payroll_based_on": "Attendance"}) + def test_payment_days_in_salary_slip_based_on_timesheet(self): + from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet + + from hrms.hr.doctype.attendance.attendance import mark_attendance + + emp = make_employee( + "test_employee_timesheet@salary.com", + company="_Test Company", + holiday_list="Salary Slip Test Holiday List", + ) + frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"}) + + # mark attendance + first_sunday = get_first_sunday() + + mark_attendance( + emp, add_days(first_sunday, 1), "Absent", ignore_validate=True + ) # counted as absent + + # salary structure based on timesheet + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, is_billable=1) + salary_slip = make_salary_slip_from_timesheet(timesheet.name) + salary_slip.start_date = get_first_day(nowdate()) + salary_slip.end_date = get_last_day(nowdate()) + salary_slip.save() + salary_slip.submit() + salary_slip.reload() + + no_of_days = get_no_of_days() + days_in_month = no_of_days[0] + no_of_holidays = no_of_days[1] + + self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays - 1) + + # component calculation based on attendance (payment days) + amount, precision = None, None + + for row in salary_slip.earnings: + if row.salary_component == "Basic Salary": + amount = row.amount + precision = row.precision("amount") + break + expected_amount = flt( + (50000 * salary_slip.payment_days / salary_slip.total_working_days), precision + ) + + self.assertEqual(amount, expected_amount) + + @change_settings("Payroll Settings", {"payroll_based_on": "Attendance"}) + def test_component_amount_dependent_on_another_payment_days_based_component(self): + from hrms.hr.doctype.attendance.attendance import mark_attendance + from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + ) + + salary_structure = make_salary_structure_for_payment_days_based_component_dependency() + employee = make_employee("test_payment_days_based_component@salary.com", company="_Test Company") + + # base = 50000 + create_salary_structure_assignment( + employee, salary_structure.name, company="_Test Company", currency="INR" + ) + + # mark employee absent for a day since this case works fine if payment days are equal to working days + first_sunday = get_first_sunday() + + mark_attendance( + employee, add_days(first_sunday, 1), "Absent", ignore_validate=True + ) # counted as absent + + # make salary slip and assert payment days + ss = make_salary_slip_for_payment_days_dependency_test( + "test_payment_days_based_component@salary.com", salary_structure.name + ) + self.assertEqual(ss.absent_days, 1) + + ss.reload() + payment_days_based_comp_amount = 0 + for component in ss.earnings: + if component.salary_component == "HRA - Payment Days": + payment_days_based_comp_amount = flt(component.amount, component.precision("amount")) + break + + # check if the dependent component is calculated using the amount updated after payment days + actual_amount = 0 + precision = 0 + for component in ss.deductions: + if component.salary_component == "P - Employee Provident Fund": + precision = component.precision("amount") + actual_amount = flt(component.amount, precision) + break + + expected_amount = flt((flt(ss.gross_pay) - payment_days_based_comp_amount) * 0.12, precision) + + self.assertEqual(actual_amount, expected_amount) + + @change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 1}) + def test_salary_slip_with_holidays_included(self): + no_of_days = get_no_of_days() + make_employee("test_salary_slip_with_holidays_included@salary.com") + frappe.db.set_value( + "Employee", + frappe.get_value( + "Employee", {"employee_name": "test_salary_slip_with_holidays_included@salary.com"}, "name" + ), + "relieving_date", + None, + ) + frappe.db.set_value( + "Employee", + frappe.get_value( + "Employee", {"employee_name": "test_salary_slip_with_holidays_included@salary.com"}, "name" + ), + "status", + "Active", + ) + ss = make_employee_salary_slip( + "test_salary_slip_with_holidays_included@salary.com", + "Monthly", + "Test Salary Slip With Holidays Included", + ) + + self.assertEqual(ss.total_working_days, no_of_days[0]) + self.assertEqual(ss.payment_days, no_of_days[0]) + self.assertEqual(ss.earnings[0].amount, 50000) + self.assertEqual(ss.earnings[1].amount, 3000) + self.assertEqual(ss.gross_pay, 78000) + + @change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 0}) + def test_salary_slip_with_holidays_excluded(self): + no_of_days = get_no_of_days() + make_employee("test_salary_slip_with_holidays_excluded@salary.com") + frappe.db.set_value( + "Employee", + frappe.get_value( + "Employee", {"employee_name": "test_salary_slip_with_holidays_excluded@salary.com"}, "name" + ), + "relieving_date", + None, + ) + frappe.db.set_value( + "Employee", + frappe.get_value( + "Employee", {"employee_name": "test_salary_slip_with_holidays_excluded@salary.com"}, "name" + ), + "status", + "Active", + ) + ss = make_employee_salary_slip( + "test_salary_slip_with_holidays_excluded@salary.com", + "Monthly", + "Test Salary Slip With Holidays Excluded", + ) + + self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1]) + self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1]) + self.assertEqual(ss.earnings[0].amount, 50000) + self.assertEqual(ss.earnings[0].default_amount, 50000) + self.assertEqual(ss.earnings[1].amount, 3000) + self.assertEqual(ss.gross_pay, 78000) + + @change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 1}) + def test_payment_days(self): + from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + ) + + no_of_days = get_no_of_days() + + # set joinng date in the same month + employee = make_employee("test_payment_days@salary.com") + if getdate(nowdate()).day >= 15: + relieving_date = getdate(add_days(nowdate(), -10)) + date_of_joining = getdate(add_days(nowdate(), -10)) + elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5: + date_of_joining = getdate(add_days(nowdate(), -3)) + relieving_date = getdate(add_days(nowdate(), -3)) + elif getdate(nowdate()).day < 5 and not getdate(nowdate()).day == 1: + date_of_joining = getdate(add_days(nowdate(), -1)) + relieving_date = getdate(add_days(nowdate(), -1)) + elif getdate(nowdate()).day == 1: + date_of_joining = getdate(nowdate()) + relieving_date = getdate(nowdate()) + + frappe.db.set_value( + "Employee", + employee, + {"date_of_joining": date_of_joining, "relieving_date": None, "status": "Active"}, + ) + + salary_structure = "Test Payment Days" + ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", salary_structure) + + self.assertEqual(ss.total_working_days, no_of_days[0]) + self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1)) + + # set relieving date in the same month + frappe.db.set_value( + "Employee", + employee, + { + "date_of_joining": add_days(nowdate(), -60), + "relieving_date": relieving_date, + "status": "Left", + }, + ) + + if date_of_joining.day > 1: + self.assertRaises(frappe.ValidationError, ss.save) + + create_salary_structure_assignment(employee, salary_structure) + ss.reload() + ss.save() + + self.assertEqual(ss.total_working_days, no_of_days[0]) + self.assertEqual(ss.payment_days, getdate(relieving_date).day) + + frappe.db.set_value( + "Employee", + frappe.get_value("Employee", {"employee_name": "test_payment_days@salary.com"}, "name"), + "relieving_date", + None, + ) + frappe.db.set_value( + "Employee", + frappe.get_value("Employee", {"employee_name": "test_payment_days@salary.com"}, "name"), + "status", + "Active", + ) + + def test_employee_salary_slip_read_permission(self): + make_employee("test_employee_salary_slip_read_permission@salary.com") + + salary_slip_test_employee = make_employee_salary_slip( + "test_employee_salary_slip_read_permission@salary.com", + "Monthly", + "Test Employee Salary Slip Read Permission", + ) + frappe.set_user("test_employee_salary_slip_read_permission@salary.com") + self.assertTrue(salary_slip_test_employee.has_permission("read")) + + @change_settings("Payroll Settings", {"email_salary_slip_to_employee": 1}) + def test_email_salary_slip(self): + frappe.db.delete("Email Queue") + + user_id = "test_email_salary_slip@salary.com" + + make_employee(user_id, company="_Test Company") + ss = make_employee_salary_slip(user_id, "Monthly", "Test Salary Slip Email") + ss.company = "_Test Company" + ss.save() + ss.submit() + + email_queue = frappe.db.a_row_exists("Email Queue") + self.assertTrue(email_queue) + + def test_loan_repayment_salary_slip(self): + from erpnext.loan_management.doctype.loan.test_loan import ( + create_loan, + create_loan_accounts, + create_loan_type, + make_loan_disbursement_entry, + ) + from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( + process_loan_interest_accrual_for_term_loans, + ) + + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + applicant = make_employee("test_loan_repayment_salary_slip@salary.com", company="_Test Company") + + create_loan_accounts() + + create_loan_type( + "Car Loan", + 500000, + 8.4, + is_term_loan=1, + mode_of_payment="Cash", + disbursement_account="Disbursement Account - _TC", + payment_account="Payment Account - _TC", + loan_account="Loan Account - _TC", + interest_income_account="Interest Income Account - _TC", + penalty_income_account="Penalty Income Account - _TC", + ) + + payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") + + make_salary_structure( + "Test Loan Repayment Salary Structure", + "Monthly", + employee=applicant, + currency="INR", + payroll_period=payroll_period, + ) + + frappe.db.sql( + "delete from tabLoan where applicant = 'test_loan_repayment_salary_slip@salary.com'" + ) + loan = create_loan( + applicant, + "Car Loan", + 11000, + "Repay Over Number of Periods", + 20, + posting_date=add_months(nowdate(), -1), + ) + loan.repay_from_salary = 1 + loan.submit() + + make_loan_disbursement_entry( + loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1) + ) + + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) + + ss = make_employee_salary_slip( + "test_loan_repayment_salary_slip@salary.com", "Monthly", "Test Loan Repayment Salary Structure" + ) + ss.submit() + + self.assertEqual(ss.total_loan_repayment, 592) + self.assertEqual( + ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment))) + ) + + def test_payroll_frequency(self): + fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())[0] + month = "%02d" % getdate(nowdate()).month + m = get_month_details(fiscal_year, month) + + for payroll_frequency in ["Monthly", "Bimonthly", "Fortnightly", "Weekly", "Daily"]: + make_employee(payroll_frequency + "_test_employee@salary.com") + ss = make_employee_salary_slip( + payroll_frequency + "_test_employee@salary.com", + payroll_frequency, + payroll_frequency + "_Test Payroll Frequency", + ) + if payroll_frequency == "Monthly": + self.assertEqual(ss.end_date, m["month_end_date"]) + elif payroll_frequency == "Bimonthly": + if getdate(ss.start_date).day <= 15: + self.assertEqual(ss.end_date, m["month_mid_end_date"]) + else: + self.assertEqual(ss.end_date, m["month_end_date"]) + elif payroll_frequency == "Fortnightly": + self.assertEqual(ss.end_date, add_days(nowdate(), 13)) + elif payroll_frequency == "Weekly": + self.assertEqual(ss.end_date, add_days(nowdate(), 6)) + elif payroll_frequency == "Daily": + self.assertEqual(ss.end_date, nowdate()) + + def test_multi_currency_salary_slip(self): + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company") + frappe.db.sql( + """delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""" + ) + salary_structure = make_salary_structure( + "Test Multi Currency Salary Slip", + "Monthly", + employee=applicant, + company="_Test Company", + currency="USD", + ) + salary_slip = make_salary_slip(salary_structure.name, employee=applicant) + salary_slip.exchange_rate = 70 + salary_slip.calculate_net_pay() + + self.assertEqual(salary_slip.gross_pay, 78000) + self.assertEqual(salary_slip.base_gross_pay, 78000 * 70) + + def test_year_to_date_computation(self): + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + applicant = make_employee("test_ytd@salary.com", company="_Test Company") + + payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") + + create_tax_slab( + payroll_period, + allow_tax_exemption=True, + currency="INR", + effective_date=getdate("2019-04-01"), + company="_Test Company", + ) + + salary_structure = make_salary_structure( + "Monthly Salary Structure Test for Salary Slip YTD", + "Monthly", + employee=applicant, + company="_Test Company", + currency="INR", + payroll_period=payroll_period, + ) + + # clear salary slip for this employee + frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'") + + create_salary_slips_for_payroll_period( + applicant, salary_structure.name, payroll_period, deduct_random=False, num=6 + ) + + salary_slips = frappe.get_all( + "Salary Slip", + fields=["year_to_date", "net_pay"], + filters={"employee_name": "test_ytd@salary.com"}, + order_by="posting_date", + ) + + year_to_date = 0 + for slip in salary_slips: + year_to_date += flt(slip.net_pay) + self.assertEqual(slip.year_to_date, year_to_date) + + def test_component_wise_year_to_date_computation(self): + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + employee_name = "test_component_wise_ytd@salary.com" + applicant = make_employee(employee_name, company="_Test Company") + + payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") + + create_tax_slab( + payroll_period, + allow_tax_exemption=True, + currency="INR", + effective_date=getdate("2019-04-01"), + company="_Test Company", + ) + + salary_structure = make_salary_structure( + "Monthly Salary Structure Test for Salary Slip YTD", + "Monthly", + employee=applicant, + company="_Test Company", + currency="INR", + payroll_period=payroll_period, + ) + + # clear salary slip for this employee + frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = '%s'" % employee_name) + + create_salary_slips_for_payroll_period( + applicant, salary_structure.name, payroll_period, deduct_random=False, num=3 + ) + + salary_slips = frappe.get_all( + "Salary Slip", + fields=["name"], + filters={"employee_name": employee_name}, + order_by="posting_date", + ) + + year_to_date = dict() + for slip in salary_slips: + doc = frappe.get_doc("Salary Slip", slip.name) + for entry in doc.get("earnings"): + if not year_to_date.get(entry.salary_component): + year_to_date[entry.salary_component] = 0 + + year_to_date[entry.salary_component] += entry.amount + self.assertEqual(year_to_date[entry.salary_component], entry.year_to_date) + + def test_tax_for_payroll_period(self): + data = {} + # test the impact of tax exemption declaration, tax exemption proof submission + # and deduct check boxes in annual tax calculation + # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 + frappe.db.sql("""delete from `tabPayroll Period`""") + frappe.db.sql("""delete from `tabSalary Component`""") + + payroll_period = create_payroll_period() + + create_tax_slab(payroll_period, allow_tax_exemption=True) + + employee = make_employee("test_tax@salary.slip") + delete_docs = [ + "Salary Slip", + "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Employee Benefit Claim", + "Salary Structure Assignment", + ] + for doc in delete_docs: + frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) + + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + salary_structure = make_salary_structure( + "Stucture to test tax", + "Monthly", + other_details={"max_benefits": 100000}, + test_tax=True, + include_flexi_benefits=True, + employee=employee, + payroll_period=payroll_period, + ) + + # create salary slip for whole period deducting tax only on last period + # to find the total tax amount paid + create_salary_slips_for_payroll_period( + employee, salary_structure.name, payroll_period, deduct_random=False + ) + tax_paid = get_tax_paid_in_period(employee) + + annual_tax = 113589.0 + try: + self.assertEqual(tax_paid, annual_tax) + except AssertionError: + print("\nSalary Slip - Annual tax calculation failed\n") + raise + frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + + # create exemption declaration so the tax amount varies + create_exemption_declaration(employee, payroll_period.name) + + # create for payroll deducting in random months + data["deducted_dates"] = create_salary_slips_for_payroll_period( + employee, salary_structure.name, payroll_period + ) + tax_paid = get_tax_paid_in_period(employee) + + # No proof, benefit claim sumitted, total tax paid, should not change + try: + self.assertEqual(tax_paid, annual_tax) + except AssertionError: + print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") + raise + + # Submit proof for total 120000 + data["proof"] = create_proof_submission(employee, payroll_period, 120000) + + # Submit benefit claim for total 50000 + data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance") + data["benefit-2"] = create_benefit_claim( + employee, payroll_period, 35000, "Leave Travel Allowance" + ) + + frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + data["deducted_dates"] = create_salary_slips_for_payroll_period( + employee, salary_structure.name, payroll_period + ) + tax_paid = get_tax_paid_in_period(employee) + + # total taxable income 416000, 166000 @ 5% ie. 8300 + try: + self.assertEqual(tax_paid, 82389.0) + except AssertionError: + print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") + raise + + # create additional salary of 150000 + frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + data["additional-1"] = create_additional_salary(employee, payroll_period, 150000) + data["deducted_dates"] = create_salary_slips_for_payroll_period( + employee, salary_structure.name, payroll_period + ) + + # total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200 + tax_paid = get_tax_paid_in_period(employee) + try: + self.assertEqual(tax_paid, annual_tax) + except AssertionError: + print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") + raise + frappe.db.sql("""delete from `tabAdditional Salary` where employee=%s""", (employee)) + + # undelete fixture data + frappe.db.rollback() + + @change_settings( + "Payroll Settings", + { + "payroll_based_on": "Attendance", + "consider_unmarked_attendance_as": "Present", + "include_holidays_in_total_working_days": True, + }, + ) + def test_default_amount(self): + # Special Allowance (SA) uses another component Basic (BS) in it's formula : BD * .5 + # Basic has "Depends on Payment Days" enabled + # Test default amount for SA is based on default amount for BS (irrespective of PD) + # Test amount for SA is based on amount for BS (based on PD) + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + month_start_date = get_first_day(nowdate()) + joining_date = add_days(month_start_date, 3) + employee = make_employee("test_tax_for_mid_joinee@salary.com", date_of_joining=joining_date) + + salary_structure = make_salary_structure( + "Stucture to test tax", + "Monthly", + test_tax=True, + from_date=joining_date, + employee=employee, + ) + + ss = make_salary_slip(salary_structure.name, employee=employee) + + # default amount for SA (special allowance = BS*0.5) should be based on default amount for basic + self.assertEqual(ss.earnings[2].default_amount, 25000) + self.assertEqual( + ss.earnings[2].amount, flt(ss.earnings[0].amount * 0.5, ss.earnings[0].precision("amount")) + ) + + def test_tax_for_recurring_additional_salary(self): + frappe.db.sql("""delete from `tabPayroll Period`""") + frappe.db.sql("""delete from `tabSalary Component`""") + + payroll_period = create_payroll_period() + + create_tax_slab(payroll_period, allow_tax_exemption=True) + + employee = make_employee("test_tax@salary.slip") + delete_docs = [ + "Salary Slip", + "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Employee Benefit Claim", + "Salary Structure Assignment", + ] + for doc in delete_docs: + frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) + + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + salary_structure = make_salary_structure( + "Stucture to test tax", + "Monthly", + other_details={"max_benefits": 100000}, + test_tax=True, + include_flexi_benefits=True, + employee=employee, + payroll_period=payroll_period, + ) + + create_salary_slips_for_payroll_period( + employee, salary_structure.name, payroll_period, deduct_random=False, num=3 + ) + + tax_paid = get_tax_paid_in_period(employee) + + annual_tax = 23196.0 + self.assertEqual(tax_paid, annual_tax) + + frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + + # ------------------------------------ + # Recurring additional salary + start_date = add_months(payroll_period.start_date, 3) + end_date = add_months(payroll_period.start_date, 5) + create_recurring_additional_salary(employee, "Performance Bonus", 20000, start_date, end_date) + + frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + + create_salary_slips_for_payroll_period( + employee, salary_structure.name, payroll_period, deduct_random=False, num=4 + ) + + tax_paid = get_tax_paid_in_period(employee) + + annual_tax = 32315.0 + self.assertEqual(tax_paid, annual_tax) + + frappe.db.rollback() + + def test_salary_slip_from_timesheet(self): + from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet + + emp = make_employee("test_employee_6@salary.com", company="_Test Company") + salary_structure = make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, is_billable=1) + salary_slip = make_salary_slip_from_timesheet(timesheet.name) + salary_slip.submit() + + self.assertEqual(salary_slip.total_working_hours, 2) + self.assertEqual(salary_slip.hour_rate, 50) + self.assertEqual(salary_slip.earnings[0].salary_component, "Timesheet Component") + self.assertEqual(salary_slip.earnings[0].amount, 100) + self.assertEqual(salary_slip.timesheets[0].time_sheet, timesheet.name) + self.assertEqual(salary_slip.timesheets[0].working_hours, 2) + + timesheet = frappe.get_doc("Timesheet", timesheet.name) + self.assertEqual(timesheet.status, "Payslip") + salary_slip.cancel() + + timesheet = frappe.get_doc("Timesheet", timesheet.name) + self.assertEqual(timesheet.status, "Submitted") + + def test_do_not_show_statistical_component_in_slip(self): + make_employee("test_statistical_component@salary.com") + new_ss = make_employee_salary_slip( + "test_statistical_component@salary.com", + "Monthly", + "Test Payment Based On Attendence", + ) + components = [row.salary_component for row in new_ss.get("earnings")] + self.assertNotIn("Statistical Component", components) + + @change_settings( + "Payroll Settings", + {"payroll_based_on": "Attendance", "consider_unmarked_attendance_as": "Present"}, + ) + def test_statistical_component_based_on_payment_days(self): + """ + Tests whether component using statistical component in the formula + gets the updated value based on payment days + """ + from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + ) + + emp = make_employee("test_statistical_component@salary.com") + first_sunday = get_first_sunday() + mark_attendance(emp, add_days(first_sunday, 1), "Absent", ignore_validate=True) + salary_structure = make_salary_structure_for_payment_days_based_component_dependency( + test_statistical_comp=True + ) + create_salary_structure_assignment( + emp, salary_structure.name, company="_Test Company", currency="INR" + ) + # make salary slip and assert payment days + ss = make_salary_slip_for_payment_days_dependency_test( + "test_statistical_component@salary.com", salary_structure.name + ) + + amount = precision = None + for entry in ss.earnings: + if entry.salary_component == "Dependency Component": + amount = entry.amount + precision = entry.precision("amount") + break + + self.assertEqual(amount, flt((1000 * ss.payment_days / ss.total_working_days) * 0.5, precision)) + + def make_activity_for_employee(self): + activity_type = frappe.get_doc("Activity Type", "_Test Activity Type") + activity_type.billing_rate = 50 + activity_type.costing_rate = 20 + activity_type.wage_rate = 25 + activity_type.save() + + +def get_no_of_days(): + no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year, getdate(nowdate()).month) + no_of_holidays_in_month = len( + [ + 1 + for i in calendar.monthcalendar(getdate(nowdate()).year, getdate(nowdate()).month) + if i[6] != 0 + ] + ) + + return [no_of_days_in_month[1], no_of_holidays_in_month] + + +def make_employee_salary_slip(user, payroll_frequency, salary_structure=None, posting_date=None): + from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + + if not salary_structure: + salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" + + employee = frappe.db.get_value( + "Employee", {"user_id": user}, ["name", "company", "employee_name"], as_dict=True + ) + + salary_structure_doc = make_salary_structure( + salary_structure, + payroll_frequency, + employee=employee.name, + company=employee.company, + from_date=posting_date, + ) + salary_slip_name = frappe.db.get_value( + "Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})} + ) + + if not salary_slip_name: + salary_slip = make_salary_slip(salary_structure_doc.name, employee=employee.name) + salary_slip.employee_name = employee.employee_name + salary_slip.payroll_frequency = payroll_frequency + salary_slip.posting_date = posting_date or nowdate() + salary_slip.insert() + else: + salary_slip = frappe.get_doc("Salary Slip", salary_slip_name) + + return salary_slip + + +def make_salary_component(salary_components, test_tax, company_list=None): + for salary_component in salary_components: + if frappe.db.exists("Salary Component", salary_component["salary_component"]): + frappe.delete_doc("Salary Component", salary_component["salary_component"], force=True) + + if test_tax: + if salary_component["type"] == "Earning": + salary_component["is_tax_applicable"] = 1 + elif salary_component["salary_component"] == "TDS": + salary_component["variable_based_on_taxable_salary"] = 1 + salary_component["amount_based_on_formula"] = 0 + salary_component["amount"] = 0 + salary_component["formula"] = "" + salary_component["condition"] = "" + + salary_component["salary_component_abbr"] = salary_component["abbr"] + doc = frappe.new_doc("Salary Component") + doc.update(salary_component) + doc.insert() + + set_salary_component_account(doc, company_list) + + +def set_salary_component_account(sal_comp, company_list=None): + company = erpnext.get_default_company() + + if company_list and company not in company_list: + company_list.append(company) + + if not isinstance(sal_comp, Document): + sal_comp = frappe.get_doc("Salary Component", sal_comp) + + if not sal_comp.get("accounts"): + for d in company_list: + company_abbr = frappe.get_cached_value("Company", d, "abbr") + + if sal_comp.type == "Earning": + account_name = "Salary" + parent_account = "Indirect Expenses - " + company_abbr + else: + account_name = "Salary Deductions" + parent_account = "Current Liabilities - " + company_abbr + + sal_comp.append( + "accounts", {"company": d, "account": create_account(account_name, d, parent_account)} + ) + sal_comp.save() + + +def create_account(account_name, company, parent_account, account_type=None): + company_abbr = frappe.get_cached_value("Company", company, "abbr") + account = frappe.db.get_value("Account", account_name + " - " + company_abbr) + if not account: + frappe.get_doc( + { + "doctype": "Account", + "account_name": account_name, + "parent_account": parent_account, + "company": company, + } + ).insert() + return account + + +def make_earning_salary_component( + setup=False, + test_tax=False, + company_list=None, + include_flexi_benefits=False, + test_statistical_comp=False, +): + data = [ + { + "salary_component": "Basic Salary", + "abbr": "BS", + "condition": "base > 10000", + "formula": "base", + "type": "Earning", + "amount_based_on_formula": 1, + }, + {"salary_component": "HRA", "abbr": "H", "amount": 3000, "type": "Earning"}, + { + "salary_component": "Special Allowance", + "abbr": "SA", + "condition": "H < 10000", + "formula": "BS*.5", + "type": "Earning", + "amount_based_on_formula": 1, + "depends_on_payment_days": 0, + }, + {"salary_component": "Leave Encashment", "abbr": "LE", "type": "Earning"}, + { + "salary_component": "Statistical Component", + "abbr": "SC", + "type": "Earning", + "statistical_component": 1, + "amount": 500, + }, + ] + if include_flexi_benefits: + data.extend( + [ + { + "salary_component": "Leave Travel Allowance", + "abbr": "B", + "is_flexible_benefit": 1, + "type": "Earning", + "pay_against_benefit_claim": 1, + "max_benefit_amount": 100000, + "depends_on_payment_days": 0, + }, + { + "salary_component": "Medical Allowance", + "abbr": "B", + "is_flexible_benefit": 1, + "pay_against_benefit_claim": 0, + "type": "Earning", + "max_benefit_amount": 15000, + "depends_on_payment_days": 1, + }, + ] + ) + if test_tax: + data.extend( + [ + {"salary_component": "Performance Bonus", "abbr": "B", "type": "Earning"}, + ] + ) + + if setup or test_tax: + make_salary_component(data, test_tax, company_list) + + data.append( + { + "salary_component": "Basic Salary", + "abbr": "BS", + "condition": "base < 10000", + "formula": "base*.2", + "type": "Earning", + "amount_based_on_formula": 1, + } + ) + return data + + +def make_deduction_salary_component(setup=False, test_tax=False, company_list=None): + data = [ + { + "salary_component": "Professional Tax", + "abbr": "PT", + "type": "Deduction", + "amount": 200, + "exempted_from_income_tax": 1, + } + ] + if not test_tax: + data.append( + { + "salary_component": "TDS", + "abbr": "T", + "condition": 'employment_type=="Intern"', + "type": "Deduction", + "round_to_the_nearest_integer": 1, + } + ) + else: + data.append( + { + "salary_component": "TDS", + "abbr": "T", + "type": "Deduction", + "depends_on_payment_days": 0, + "variable_based_on_taxable_salary": 1, + "round_to_the_nearest_integer": 1, + } + ) + if setup or test_tax: + make_salary_component(data, test_tax, company_list) + + return data + + +def get_tax_paid_in_period(employee): + tax_paid_amount = frappe.db.sql( + """select sum(sd.amount) from `tabSalary Detail` + sd join `tabSalary Slip` ss where ss.name=sd.parent and ss.employee=%s + and ss.docstatus=1 and sd.salary_component='TDS'""", + (employee), + ) + return tax_paid_amount[0][0] + + +def create_exemption_declaration(employee, payroll_period): + create_exemption_category() + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "payroll_period": payroll_period, + "company": erpnext.get_default_company(), + "currency": erpnext.get_default_currency(), + } + ) + declaration.append( + "declarations", + { + "exemption_sub_category": "_Test Sub Category", + "exemption_category": "_Test Category", + "amount": 100000, + }, + ) + declaration.submit() + + +def create_proof_submission(employee, payroll_period, amount): + submission_date = add_months(payroll_period.start_date, random.randint(0, 11)) + proof_submission = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "employee": employee, + "payroll_period": payroll_period.name, + "submission_date": submission_date, + "currency": erpnext.get_default_currency(), + } + ) + proof_submission.append( + "tax_exemption_proofs", + { + "exemption_sub_category": "_Test Sub Category", + "exemption_category": "_Test Category", + "type_of_proof": "Test", + "amount": amount, + }, + ) + proof_submission.submit() + return submission_date + + +def create_benefit_claim(employee, payroll_period, amount, component): + claim_date = add_months(payroll_period.start_date, random.randint(0, 11)) + frappe.get_doc( + { + "doctype": "Employee Benefit Claim", + "employee": employee, + "claimed_amount": amount, + "claim_date": claim_date, + "earning_component": component, + "currency": erpnext.get_default_currency(), + } + ).submit() + return claim_date + + +def create_tax_slab( + payroll_period, + effective_date=None, + allow_tax_exemption=False, + dont_submit=False, + currency=None, + company=None, +): + if not currency: + currency = erpnext.get_default_currency() + + if company: + currency = erpnext.get_company_currency(company) + + slabs = [ + { + "from_amount": 250000, + "to_amount": 500000, + "percent_deduction": 5, + "condition": "annual_taxable_earning > 500000", + }, + {"from_amount": 500001, "to_amount": 1000000, "percent_deduction": 20}, + {"from_amount": 1000001, "percent_deduction": 30}, + ] + + income_tax_slab_name = frappe.db.get_value("Income Tax Slab", {"currency": currency}) + if not income_tax_slab_name: + income_tax_slab = frappe.new_doc("Income Tax Slab") + income_tax_slab.name = "Tax Slab: " + payroll_period.name + " " + cstr(currency) + income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2) + income_tax_slab.company = company or "" + income_tax_slab.currency = currency + + if allow_tax_exemption: + income_tax_slab.allow_tax_exemption = 1 + income_tax_slab.standard_tax_exemption_amount = 50000 + + for item in slabs: + income_tax_slab.append("slabs", item) + + income_tax_slab.append("other_taxes_and_charges", {"description": "cess", "percent": 4}) + + income_tax_slab.save() + if not dont_submit: + income_tax_slab.submit() + + return income_tax_slab.name + else: + return income_tax_slab_name + + +def create_salary_slips_for_payroll_period( + employee, salary_structure, payroll_period, deduct_random=True, num=12 +): + deducted_dates = [] + i = 0 + while i < num: + slip = frappe.get_doc( + { + "doctype": "Salary Slip", + "employee": employee, + "salary_structure": salary_structure, + "frequency": "Monthly", + } + ) + if i == 0: + posting_date = add_days(payroll_period.start_date, 25) + else: + posting_date = add_months(posting_date, 1) + if i == 11: + slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1 + slip.deduct_tax_for_unclaimed_employee_benefits = 1 + if deduct_random and not random.randint(0, 2): + slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1 + deducted_dates.append(posting_date) + slip.posting_date = posting_date + slip.start_date = get_first_day(posting_date) + slip.end_date = get_last_day(posting_date) + doc = make_salary_slip(salary_structure, slip, employee) + doc.submit() + i += 1 + return deducted_dates + + +def create_additional_salary(employee, payroll_period, amount): + salary_date = add_months(payroll_period.start_date, random.randint(0, 11)) + frappe.get_doc( + { + "doctype": "Additional Salary", + "employee": employee, + "company": erpnext.get_default_company(), + "salary_component": "Performance Bonus", + "payroll_date": salary_date, + "amount": amount, + "type": "Earning", + "currency": erpnext.get_default_currency(), + } + ).submit() + return salary_date + + +def make_leave_application( + employee, + from_date, + to_date, + leave_type, + company=None, + half_day=False, + half_day_date=None, + submit=True, +): + leave_application = frappe.get_doc( + dict( + doctype="Leave Application", + employee=employee, + leave_type=leave_type, + from_date=from_date, + to_date=to_date, + half_day=half_day, + half_day_date=half_day_date, + company=company or erpnext.get_default_company() or "_Test Company", + status="Approved", + leave_approver="test@example.com", + ) + ).insert() + + if submit: + leave_application.submit() + + return leave_application + + +def setup_test(): + make_earning_salary_component(setup=True, company_list=["_Test Company"]) + make_deduction_salary_component(setup=True, company_list=["_Test Company"]) + + for dt in [ + "Leave Application", + "Leave Allocation", + "Salary Slip", + "Attendance", + "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Employee Benefit Claim", + "Salary Structure Assignment", + ]: + frappe.db.sql("delete from `tab%s`" % dt) + + make_holiday_list() + + frappe.db.set_value( + "Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List" + ) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) + frappe.db.set_value("HR Settings", None, "leave_status_notification_template", None) + frappe.db.set_value("HR Settings", None, "leave_approval_notification_template", None) + + +def make_holiday_list(list_name=None, from_date=None, to_date=None): + fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company()) + name = list_name or "Salary Slip Test Holiday List" + + frappe.delete_doc_if_exists("Holiday List", name, force=True) + + holiday_list = frappe.get_doc( + { + "doctype": "Holiday List", + "holiday_list_name": name, + "from_date": from_date or fiscal_year[1], + "to_date": to_date or fiscal_year[2], + "weekly_off": "Sunday", + } + ).insert() + holiday_list.get_weekly_off_dates() + holiday_list.save() + holiday_list = holiday_list.name + + return holiday_list + + +def make_salary_structure_for_payment_days_based_component_dependency(test_statistical_comp=False): + earnings = [ + { + "salary_component": "Basic Salary - Payment Days", + "abbr": "P_BS", + "type": "Earning", + "formula": "base", + "amount_based_on_formula": 1, + }, + { + "salary_component": "HRA - Payment Days", + "abbr": "P_HRA", + "type": "Earning", + "depends_on_payment_days": 1, + "amount_based_on_formula": 1, + "formula": "base * 0.20", + }, + ] + if test_statistical_comp: + earnings.extend( + [ + { + "salary_component": "Statistical Component", + "abbr": "SC", + "type": "Earning", + "statistical_component": 1, + "amount": 1000, + "depends_on_payment_days": 1, + }, + { + "salary_component": "Dependency Component", + "abbr": "DC", + "type": "Earning", + "amount_based_on_formula": 1, + "formula": "SC * 0.5", + "depends_on_payment_days": 0, + }, + ] + ) + + make_salary_component(earnings, False, company_list=["_Test Company"]) + + deductions = [ + { + "salary_component": "P - Professional Tax", + "abbr": "P_PT", + "type": "Deduction", + "depends_on_payment_days": 1, + "amount": 200.00, + }, + { + "salary_component": "P - Employee Provident Fund", + "abbr": "P_EPF", + "type": "Deduction", + "exempted_from_income_tax": 1, + "amount_based_on_formula": 1, + "depends_on_payment_days": 0, + "formula": "(gross_pay - P_HRA) * 0.12", + }, + ] + + make_salary_component(deductions, False, company_list=["_Test Company"]) + + salary_structure = "Salary Structure with PF" + if frappe.db.exists("Salary Structure", salary_structure): + frappe.db.delete("Salary Structure", salary_structure) + + details = { + "doctype": "Salary Structure", + "name": salary_structure, + "company": "_Test Company", + "payroll_frequency": "Monthly", + "payment_account": get_random("Account", filters={"account_currency": "INR"}), + "currency": "INR", + } + + salary_structure_doc = frappe.get_doc(details) + + for entry in earnings: + salary_structure_doc.append("earnings", entry) + + for entry in deductions: + salary_structure_doc.append("deductions", entry) + + salary_structure_doc.insert() + salary_structure_doc.submit() + + return salary_structure_doc + + +def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure): + employee = frappe.db.get_value( + "Employee", {"user_id": employee}, ["name", "company", "employee_name"], as_dict=True + ) + + salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name}) + + if not salary_slip_name: + salary_slip = make_salary_slip(salary_structure, employee=employee.name) + salary_slip.employee_name = employee.employee_name + salary_slip.payroll_frequency = "Monthly" + salary_slip.posting_date = nowdate() + salary_slip.insert() + else: + salary_slip = frappe.get_doc("Salary Slip", salary_slip_name) + + return salary_slip + + +def create_recurring_additional_salary( + employee, salary_component, amount, from_date, to_date, company=None +): + frappe.get_doc( + { + "doctype": "Additional Salary", + "employee": employee, + "company": company or erpnext.get_default_company(), + "salary_component": salary_component, + "is_recurring": 1, + "from_date": from_date, + "to_date": to_date, + "amount": amount, + "type": "Earning", + "currency": erpnext.get_default_currency(), + } + ).submit() + + +def make_salary_structure_for_timesheet(employee, company=None): + from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + make_salary_structure, + ) + + salary_structure_name = "Timesheet Salary Structure Test" + frequency = "Monthly" + + if not frappe.db.exists("Salary Component", "Timesheet Component"): + frappe.get_doc( + {"doctype": "Salary Component", "salary_component": "Timesheet Component"} + ).insert() + + salary_structure = make_salary_structure( + salary_structure_name, frequency, company=company, dont_submit=True + ) + salary_structure.salary_component = "Timesheet Component" + salary_structure.salary_slip_based_on_timesheet = 1 + salary_structure.hour_rate = 50.0 + salary_structure.save() + salary_structure.submit() + + if not frappe.db.get_value("Salary Structure Assignment", {"employee": employee, "docstatus": 1}): + frappe.db.set_value("Employee", employee, "date_of_joining", add_months(nowdate(), -5)) + create_salary_structure_assignment(employee, salary_structure.name) + + return salary_structure diff --git a/hrms/payroll/doctype/salary_slip_leave/__init__.py b/hrms/payroll/doctype/salary_slip_leave/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.json b/hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.json new file mode 100644 index 0000000..60ed453 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.json @@ -0,0 +1,79 @@ +{ + "actions": [], + "creation": "2021-02-19 11:45:18.173417", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "leave_type", + "total_allocated_leaves", + "expired_leaves", + "used_leaves", + "pending_leaves", + "available_leaves" + ], + "fields": [ + { + "fieldname": "leave_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Leave Type", + "no_copy": 1, + "options": "Leave Type", + "read_only": 1 + }, + { + "fieldname": "total_allocated_leaves", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Total Allocated Leave(s)", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "expired_leaves", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Expired Leave(s)", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "used_leaves", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Used Leave(s)", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "pending_leaves", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Leave(s) Pending Approval", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "available_leaves", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Available Leave(s)", + "no_copy": 1, + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2022-02-28 14:01:32.327204", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip Leave", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.py b/hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.py new file mode 100644 index 0000000..b29a60b --- /dev/null +++ b/hrms/payroll/doctype/salary_slip_leave/salary_slip_leave.py @@ -0,0 +1,10 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class SalarySlipLeave(Document): + pass diff --git a/hrms/payroll/doctype/salary_slip_loan/__init__.py b/hrms/payroll/doctype/salary_slip_loan/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.json b/hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.json new file mode 100644 index 0000000..b3475ef --- /dev/null +++ b/hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.json @@ -0,0 +1,101 @@ +{ + "actions": [], + "creation": "2019-08-29 18:11:36.829526", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan", + "loan_type", + "loan_account", + "interest_income_account", + "column_break_4", + "principal_amount", + "interest_amount", + "total_payment", + "loan_repayment_entry" + ], + "fields": [ + { + "fieldname": "loan", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan", + "options": "Loan", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "loan_account", + "fieldtype": "Link", + "label": "Loan Account", + "options": "Account", + "read_only": 1 + }, + { + "fieldname": "interest_income_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Interest Income Account", + "options": "Account", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "principal_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Principal Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "interest_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Interest Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "total_payment", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Payment", + "options": "Company:company:default_currency" + }, + { + "fieldname": "loan_repayment_entry", + "fieldtype": "Link", + "label": "Loan Repayment Entry", + "no_copy": 1, + "options": "Loan Repayment", + "read_only": 1 + }, + { + "fetch_from": "loan.loan_type", + "fieldname": "loan_type", + "fieldtype": "Link", + "label": "Loan Type", + "options": "Loan Type", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2022-06-21 14:50:14.823213", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip Loan", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.py b/hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.py new file mode 100644 index 0000000..91267b8 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip_loan/salary_slip_loan.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class SalarySlipLoan(Document): + pass diff --git a/hrms/payroll/doctype/salary_slip_timesheet/__init__.py b/hrms/payroll/doctype/salary_slip_timesheet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json new file mode 100644 index 0000000..9930c53 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2016-06-14 19:22:29.811658", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "time_sheet", + "working_hours" + ], + "fields": [ + { + "fieldname": "time_sheet", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Time Sheet", + "options": "Timesheet", + "reqd": 1 + }, + { + "fieldname": "working_hours", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Working Hours", + "no_copy": 1, + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:27:43.463532", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip Timesheet", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py new file mode 100644 index 0000000..022eba0 --- /dev/null +++ b/hrms/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class SalarySlipTimesheet(Document): + pass diff --git a/hrms/payroll/doctype/salary_structure/README.md b/hrms/payroll/doctype/salary_structure/README.md new file mode 100644 index 0000000..3795971 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/README.md @@ -0,0 +1 @@ +Salary Template for an Employee, basis of which monthly Salary is calculated. \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_structure/__init__.py b/hrms/payroll/doctype/salary_structure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_structure/condition_and_formula_help.html b/hrms/payroll/doctype/salary_structure/condition_and_formula_help.html new file mode 100644 index 0000000..0f6cc37 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/condition_and_formula_help.html @@ -0,0 +1,47 @@ +

Variables

+
    +
  • + Variables from Salary Structure Assignment:
    + base = Base, variable = Variable etc. +
  • +
  • + Variables from Employee:
    Employment Type = employment_type, Branch = branch etc. +
  • +
  • + Variables from Salary Slip:
    + Payment Days = payment_days, Leave without pay = leave_without_pay etc. +
  • +
  • + Abbreviation from Salary Component:
    + BS = Basic Salary etc. +
  • +
  • + Some additional variable:
    + gross_pay and annual_taxable_earning can also be used. +
  • +
  • Direct Amount can also be used
  • +
+ +

Examples for Conditions and formula

+
    +
  • + Calculating Basic Salary based on base +
    Condition: base < 10000
    +
    Formula: base * .2
    +
  • +
  • + Calculating HRA based on Basic SalaryBS +
    Condition: BS > 2000
    +
    Formula: BS * .1
    +
  • +
  • + Calculating TDS based on Employment Typeemployment_type +
    Condition: employment_type=="Intern"
    +
    Amount: 1000
    +
  • +
  • + Calculating Income Tax based on annual_taxable_earning +
    Condition: annual_taxable_earning > 20000000
    +
    Formula: annual_taxable_earning * 0.10 
    +
  • +
diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.js b/hrms/payroll/doctype/salary_structure/salary_structure.js new file mode 100644 index 0000000..669a6b2 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/salary_structure.js @@ -0,0 +1,349 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt +{% include "erpnext/public/js/controllers/accounts.js" %} + +cur_frm.add_fetch('company', 'default_letter_head', 'letter_head'); + + +cur_frm.cscript.onload = function(doc, dt, dn){ + var e_tbl = doc.earnings || []; + var d_tbl = doc.deductions || []; + if (e_tbl.length == 0 && d_tbl.length == 0) + return function(r, rt) { refresh_many(['earnings', 'deductions']);}; +} + +frappe.ui.form.on('Salary Structure', { + onload: function(frm) { + + let help_button = $(` + ${__("Condition and Formula Help")} + `).click(()=>{ + + let d = new frappe.ui.Dialog({ + title: __('Condition and Formula Help'), + fields: [ + { + fieldname: 'msg_wrapper', + fieldtype: 'HTML' + } + ] + }); + + let message_html = frappe.render_template("condition_and_formula_help") + + d.fields_dict.msg_wrapper.$wrapper.append(message_html) + + d.show() + }); + let help_button_wrapper = frm.get_field("conditions_and_formula_variable_and_example").$wrapper; + help_button_wrapper.empty(); + help_button_wrapper.append(frm.doc.filters_html).append(help_button) + + frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) + + frm.set_query("payment_account", function () { + var account_types = ["Bank", "Cash"]; + return { + filters: { + "account_type": ["in", account_types], + "is_group": 0, + "company": frm.doc.company + } + }; + }); + frm.trigger('set_earning_deduction_component'); + }, + + set_earning_deduction_component: function(frm) { + if(!frm.doc.company) return; + frm.set_query("salary_component", "earnings", function() { + return { + filters: {type: "earning", company: frm.doc.company} + }; + }); + frm.set_query("salary_component", "deductions", function() { + return { + filters: {type: "deduction", company: frm.doc.company} + }; + }); + }, + + company: function(frm) { + frm.trigger('set_earning_deduction_component'); + }, + + currency: function(frm) { + calculate_totals(frm.doc); + frm.trigger("set_dynamic_labels") + frm.refresh() + }, + + set_dynamic_labels: function(frm) { + frm.set_currency_labels(["net_pay","hour_rate", "leave_encashment_amount_per_day", "max_benefits", "total_earning", + "total_deduction"], frm.doc.currency); + + frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "earnings"); + + frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "deductions"); + + frm.refresh_fields(); + }, + + refresh: function(frm) { + frm.trigger("set_dynamic_labels") + frm.trigger("toggle_fields"); + frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); + frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); + + if(frm.doc.docstatus === 1) { + frm.add_custom_button(__("Preview Salary Slip"), function() { + frm.trigger('preview_salary_slip'); + }); + } + + if(frm.doc.docstatus==1) { + frm.add_custom_button(__("Assign Salary Structure"), function() { + var doc = frappe.model.get_new_doc('Salary Structure Assignment'); + doc.salary_structure = frm.doc.name; + doc.company = frm.doc.company; + frappe.set_route('Form', 'Salary Structure Assignment', doc.name); + }); + frm.add_custom_button(__("Assign to Employees"),function () { + frm.trigger('assign_to_employees') + }) + } + + // set columns read-only + let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"]; + fields_read_only.forEach(function(field) { + frm.fields_dict.earnings.grid.update_docfield_property( + field, 'read_only', 1 + ); + frm.fields_dict.deductions.grid.update_docfield_property( + field, 'read_only', 1 + ); + }); + frm.trigger('set_earning_deduction_component'); + }, + + assign_to_employees:function (frm) { + var d = new frappe.ui.Dialog({ + title: __("Assign to Employees"), + fields: [ + {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, + {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, + {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, + {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, + {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:"payroll_payable_account", fieldtype: "Link", options: "Account", filters: {"company": frm.doc.company, "root_type": "Liability", "is_group": 0, "account_currency": frm.doc.currency}, label: __("Payroll Payable Account")}, + {fieldname:'base_variable', fieldtype:'Section Break'}, + {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, + {fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'}, + {fieldname:'base_col_br', fieldtype:'Column Break'}, + {fieldname:'base', fieldtype:'Currency', label: __('Base')}, + {fieldname:'variable', fieldtype:'Currency', label: __('Variable')} + ], + primary_action: function() { + var data = d.get_values(); + delete data.company + delete data.currency + frappe.call({ + doc: frm.doc, + method: "assign_salary_structure", + args: data, + callback: function(r) { + if(!r.exc) { + d.hide(); + frm.reload_doc(); + } + } + }); + }, + primary_action_label: __('Assign') + }); + + d.fields_dict.grade.df.onchange = function() { + const grade = d.fields_dict.grade.value; + if (grade) { + frappe.db.get_value('Employee Grade', grade, 'default_base_pay') + .then(({ message }) => { + d.set_value('base', message.default_base_pay); + }); + } + }; + + d.show(); + }, + + salary_slip_based_on_timesheet: function(frm) { + frm.trigger("toggle_fields") + }, + + preview_salary_slip: function(frm) { + frappe.call({ + method: "hrms.payroll.doctype.salary_structure.salary_structure.get_employees", + args: { + salary_structure: frm.doc.name + }, + callback: function(r) { + var employees = r.message; + if(!employees) return; + if (employees.length == 1){ + frm.events.open_salary_slip(frm, employees[0]); + } else { + var d = new frappe.ui.Dialog({ + title: __("Preview Salary Slip"), + fields: [ + { + "label":__("Employee"), + "fieldname":"employee", + "fieldtype":"Select", + "reqd": true, + options: employees + }, { + fieldname:"fetch", + "label":__("Show Salary Slip"), + "fieldtype":"Button" + } + ] + }); + d.get_input("fetch").on("click", function() { + var values = d.get_values(); + if(!values) return; + frm.events.open_salary_slip(frm, values.employee) + + }); + d.show(); + } + } + }); + }, + + open_salary_slip: function(frm, employee){ + var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard"; + frappe.call({ + method: "hrms.payroll.doctype.salary_structure.salary_structure.make_salary_slip", + args: { + source_name: frm.doc.name, + employee: employee, + as_print: 1, + print_format: print_format, + for_preview: 1 + }, + callback: function(r) { + var new_window = window.open(); + new_window.document.write(r.message); + } + }); + }, + + toggle_fields: function(frm) { + frm.toggle_display(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet); + frm.toggle_reqd(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet); + frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); + } +}); + +var validate_date = function(frm, cdt, cdn) { + var doc = locals[cdt][cdn]; + if(doc.to_date && doc.from_date) { + var from_date = frappe.datetime.str_to_obj(doc.from_date); + var to_date = frappe.datetime.str_to_obj(doc.to_date); + + if(to_date < from_date) { + frappe.model.set_value(cdt, cdn, "to_date", ""); + frappe.throw(__("From Date cannot be greater than To Date")); + } + } +} + + +cur_frm.cscript.amount = function(doc, cdt, cdn){ + calculate_totals(doc, cdt, cdn); +}; + +var calculate_totals = function(doc) { + var tbl1 = doc.earnings || []; + var tbl2 = doc.deductions || []; + + var total_earn = 0; var total_ded = 0; + for(var i = 0; i < tbl1.length; i++){ + total_earn += flt(tbl1[i].amount); + } + for(var j = 0; j < tbl2.length; j++){ + total_ded += flt(tbl2[j].amount); + } + doc.total_earning = total_earn; + doc.total_deduction = total_ded; + doc.net_pay = 0.0 + if(doc.salary_slip_based_on_timesheet == 0){ + doc.net_pay = flt(total_earn) - flt(total_ded); + } + + refresh_many(['total_earning', 'total_deduction', 'net_pay']); +} + +cur_frm.cscript.validate = function(doc, cdt, cdn) { + calculate_totals(doc); +} + + +frappe.ui.form.on('Salary Detail', { + amount: function(frm) { + calculate_totals(frm.doc); + }, + + earnings_remove: function(frm) { + calculate_totals(frm.doc); + }, + + deductions_remove: function(frm) { + calculate_totals(frm.doc); + }, + + salary_component: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.salary_component){ + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Salary Component", + name: child.salary_component + }, + callback: function(data) { + if(data.message){ + var result = data.message; + frappe.model.set_value(cdt, cdn, 'condition', result.condition); + frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); + if(result.amount_based_on_formula == 1){ + frappe.model.set_value(cdt, cdn, 'formula', result.formula); + } + else{ + frappe.model.set_value(cdt, cdn, 'amount', result.amount); + } + frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); + frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days); + frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); + frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); + frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); + frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); + refresh_field("earnings"); + refresh_field("deductions"); + } + } + }); + } + }, + + amount_based_on_formula: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.amount_based_on_formula == 1){ + frappe.model.set_value(cdt, cdn, 'amount', null); + } + else{ + frappe.model.set_value(cdt, cdn, 'formula', null); + } + } +}) diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.json b/hrms/payroll/doctype/salary_structure/salary_structure.json new file mode 100644 index 0000000..8df9957 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/salary_structure.json @@ -0,0 +1,278 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2013-03-07 18:50:29", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "company", + "letter_head", + "column_break1", + "is_active", + "payroll_frequency", + "currency", + "is_default", + "time_sheet_earning_detail", + "salary_slip_based_on_timesheet", + "column_break_17", + "salary_component", + "hour_rate", + "leave_encashment_amount_per_day", + "max_benefits", + "earning_deduction", + "earnings", + "deductions", + "conditions_and_formula_variable_and_example", + "net_pay_detail", + "total_earning", + "total_deduction", + "column_break2", + "net_pay", + "account", + "mode_of_payment", + "column_break_28", + "payment_account", + "amended_from" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "allow_on_submit": 1, + "default": "Yes", + "fieldname": "is_active", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Is Active", + "oldfieldname": "is_active", + "oldfieldtype": "Select", + "options": "\nYes\nNo", + "reqd": 1 + }, + { + "default": "Monthly", + "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", + "fieldname": "payroll_frequency", + "fieldtype": "Select", + "label": "Payroll Frequency", + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + }, + { + "default": "No", + "fieldname": "is_default", + "fieldtype": "Select", + "hidden": 1, + "label": "Is Default", + "no_copy": 1, + "options": "Yes\nNo", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "time_sheet_earning_detail", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "salary_slip_based_on_timesheet", + "fieldtype": "Check", + "label": "Salary Slip Based on Timesheet" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "description": "Salary Component for timesheet based payroll.", + "fieldname": "salary_component", + "fieldtype": "Link", + "label": "Salary Component", + "options": "Salary Component" + }, + { + "fieldname": "hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate", + "options": "currency" + }, + { + "fieldname": "leave_encashment_amount_per_day", + "fieldtype": "Currency", + "label": "Leave Encashment Amount Per Day", + "options": "currency" + }, + { + "fieldname": "max_benefits", + "fieldtype": "Currency", + "label": "Max Benefits (Amount)", + "options": "currency" + }, + { + "description": "Salary breakup based on Earning and Deduction.", + "fieldname": "earning_deduction", + "fieldtype": "Section Break", + "oldfieldname": "earning_deduction", + "oldfieldtype": "Section Break", + "precision": "2" + }, + { + "fieldname": "earnings", + "fieldtype": "Table", + "label": "Earnings", + "oldfieldname": "earning_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, + { + "fieldname": "deductions", + "fieldtype": "Table", + "label": "Deductions", + "oldfieldname": "deduction_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, + { + "fieldname": "net_pay_detail", + "fieldtype": "Section Break", + "options": "Simple" + }, + { + "fieldname": "column_break2", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "total_earning", + "fieldtype": "Currency", + "hidden": 1, + "label": "Total Earning", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "total_deduction", + "fieldtype": "Currency", + "hidden": 1, + "label": "Total Deduction", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "net_pay", + "fieldtype": "Currency", + "hidden": 1, + "label": "Net Pay", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Section Break", + "label": "Account" + }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "payment_account", + "fieldtype": "Link", + "label": "Payment Account", + "options": "Account" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Salary Structure", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "conditions_and_formula_variable_and_example", + "fieldtype": "HTML", + "label": "Conditions and Formula variable and example" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 + } + ], + "icon": "fa fa-file-text", + "idx": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-02-03 23:50:10.205676", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Structure", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.py b/hrms/payroll/doctype/salary_structure/salary_structure.py new file mode 100644 index 0000000..261ccec --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/salary_structure.py @@ -0,0 +1,366 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import re + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +from frappe.utils import cint, cstr, flt + +import erpnext + + +class SalaryStructure(Document): + def validate(self): + self.set_missing_values() + self.validate_amount() + self.strip_condition_and_formula_fields() + self.validate_max_benefits_with_flexi() + self.validate_component_based_on_tax_slab() + self.validate_payment_days_based_dependent_component() + self.validate_timesheet_component() + + def set_missing_values(self): + overwritten_fields = [ + "depends_on_payment_days", + "variable_based_on_taxable_salary", + "is_tax_applicable", + "is_flexible_benefit", + ] + overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"] + for table in ["earnings", "deductions"]: + for d in self.get(table): + component_default_value = frappe.db.get_value( + "Salary Component", + cstr(d.salary_component), + overwritten_fields + overwritten_fields_if_missing, + as_dict=1, + ) + if component_default_value: + for fieldname in overwritten_fields: + value = component_default_value.get(fieldname) + if d.get(fieldname) != value: + d.set(fieldname, value) + + if not (d.get("amount") or d.get("formula")): + for fieldname in overwritten_fields_if_missing: + d.set(fieldname, component_default_value.get(fieldname)) + + def validate_component_based_on_tax_slab(self): + for row in self.deductions: + if row.variable_based_on_taxable_salary and (row.amount or row.formula): + frappe.throw( + _( + "Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary" + ).format(row.idx, row.salary_component) + ) + + def validate_amount(self): + if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet: + frappe.throw(_("Net pay cannot be negative")) + + def validate_payment_days_based_dependent_component(self): + abbreviations = self.get_component_abbreviations() + for component_type in ("earnings", "deductions"): + for row in self.get(component_type): + if ( + row.formula + and row.depends_on_payment_days + # check if the formula contains any of the payment days components + and any(re.search(r"\b" + abbr + r"\b", row.formula) for abbr in abbreviations) + ): + message = _("Row #{0}: The {1} Component has the options {2} and {3} enabled.").format( + row.idx, + frappe.bold(row.salary_component), + frappe.bold("Amount based on formula"), + frappe.bold("Depends On Payment Days"), + ) + message += "

" + _( + "Disable {0} for the {1} component, to prevent the amount from being deducted twice, as its formula already uses a payment-days-based component." + ).format( + frappe.bold("Depends On Payment Days"), frappe.bold(row.salary_component) + ) + frappe.throw(message, title=_("Payment Days Dependency")) + + def get_component_abbreviations(self): + abbr = [d.abbr for d in self.earnings if d.depends_on_payment_days] + abbr += [d.abbr for d in self.deductions if d.depends_on_payment_days] + + return abbr + + def validate_timesheet_component(self): + if not self.salary_slip_based_on_timesheet: + return + + for component in self.earnings: + if component.salary_component == self.salary_component: + frappe.msgprint( + _( + "Row #{0}: Timesheet amount will overwrite the Earning component amount for the Salary Component {1}" + ).format(self.idx, frappe.bold(self.salary_component)), + title=_("Warning"), + indicator="orange", + ) + break + + def strip_condition_and_formula_fields(self): + # remove whitespaces from condition and formula fields + for row in self.earnings: + row.condition = row.condition.strip() if row.condition else "" + row.formula = row.formula.strip() if row.formula else "" + + for row in self.deductions: + row.condition = row.condition.strip() if row.condition else "" + row.formula = row.formula.strip() if row.formula else "" + + def validate_max_benefits_with_flexi(self): + have_a_flexi = False + if self.earnings: + flexi_amount = 0 + for earning_component in self.earnings: + if earning_component.is_flexible_benefit == 1: + have_a_flexi = True + max_of_component = frappe.db.get_value( + "Salary Component", earning_component.salary_component, "max_benefit_amount" + ) + flexi_amount += max_of_component + + if have_a_flexi and flt(self.max_benefits) == 0: + frappe.throw(_("Max benefits should be greater than zero to dispense benefits")) + if have_a_flexi and flexi_amount and flt(self.max_benefits) > flexi_amount: + frappe.throw( + _( + "Total flexible benefit component amount {0} should not be less than max benefits {1}" + ).format(flexi_amount, self.max_benefits) + ) + if not have_a_flexi and flt(self.max_benefits) > 0: + frappe.throw( + _("Salary Structure should have flexible benefit component(s) to dispense benefit amount") + ) + + def get_employees(self, **kwargs): + conditions, values = [], [] + for field, value in kwargs.items(): + if value: + conditions.append("{0}=%s".format(field)) + values.append(value) + + condition_str = " and " + " and ".join(conditions) if conditions else "" + + employees = frappe.db.sql_list( + "select name from tabEmployee where status='Active' {condition}".format( + condition=condition_str + ), + tuple(values), + ) + + return employees + + @frappe.whitelist() + def assign_salary_structure( + self, + grade=None, + department=None, + designation=None, + employee=None, + payroll_payable_account=None, + from_date=None, + base=None, + variable=None, + income_tax_slab=None, + ): + employees = self.get_employees( + company=self.company, grade=grade, department=department, designation=designation, name=employee + ) + + if employees: + if len(employees) > 20: + frappe.enqueue( + assign_salary_structure_for_employees, + timeout=600, + employees=employees, + salary_structure=self, + payroll_payable_account=payroll_payable_account, + from_date=from_date, + base=base, + variable=variable, + income_tax_slab=income_tax_slab, + ) + else: + assign_salary_structure_for_employees( + employees, + self, + payroll_payable_account=payroll_payable_account, + from_date=from_date, + base=base, + variable=variable, + income_tax_slab=income_tax_slab, + ) + else: + frappe.msgprint(_("No Employee Found")) + + +def assign_salary_structure_for_employees( + employees, + salary_structure, + payroll_payable_account=None, + from_date=None, + base=None, + variable=None, + income_tax_slab=None, +): + salary_structures_assignments = [] + existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) + count = 0 + for employee in employees: + if employee in existing_assignments_for: + continue + count += 1 + + salary_structures_assignment = create_salary_structures_assignment( + employee, salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab + ) + salary_structures_assignments.append(salary_structures_assignment) + frappe.publish_progress( + count * 100 / len(set(employees) - set(existing_assignments_for)), + title=_("Assigning Structures..."), + ) + + if salary_structures_assignments: + frappe.msgprint(_("Structures have been assigned successfully")) + + +def create_salary_structures_assignment( + employee, + salary_structure, + payroll_payable_account, + from_date, + base, + variable, + income_tax_slab=None, +): + if not payroll_payable_account: + payroll_payable_account = frappe.db.get_value( + "Company", salary_structure.company, "default_payroll_payable_account" + ) + if not payroll_payable_account: + frappe.throw(_('Please set "Default Payroll Payable Account" in Company Defaults')) + payroll_payable_account_currency = frappe.db.get_value( + "Account", payroll_payable_account, "account_currency" + ) + company_curency = erpnext.get_company_currency(salary_structure.company) + if ( + payroll_payable_account_currency != salary_structure.currency + and payroll_payable_account_currency != company_curency + ): + frappe.throw( + _("Invalid Payroll Payable Account. The account currency must be {0} or {1}").format( + salary_structure.currency, company_curency + ) + ) + + assignment = frappe.new_doc("Salary Structure Assignment") + assignment.employee = employee + assignment.salary_structure = salary_structure.name + assignment.company = salary_structure.company + assignment.currency = salary_structure.currency + assignment.payroll_payable_account = payroll_payable_account + assignment.from_date = from_date + assignment.base = base + assignment.variable = variable + assignment.income_tax_slab = income_tax_slab + assignment.save(ignore_permissions=True) + assignment.submit() + return assignment.name + + +def get_existing_assignments(employees, salary_structure, from_date): + salary_structures_assignments = frappe.db.sql_list( + """ + select distinct employee from `tabSalary Structure Assignment` + where salary_structure=%s and employee in (%s) + and from_date=%s and company= %s and docstatus=1 + """ + % ("%s", ", ".join(["%s"] * len(employees)), "%s", "%s"), + [salary_structure.name] + employees + [from_date] + [salary_structure.company], + ) + if salary_structures_assignments: + frappe.msgprint( + _( + "Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}" + ).format("\n".join(salary_structures_assignments)) + ) + return salary_structures_assignments + + +@frappe.whitelist() +def make_salary_slip( + source_name, + target_doc=None, + employee=None, + posting_date=None, + as_print=False, + print_format=None, + for_preview=0, + ignore_permissions=False, +): + def postprocess(source, target): + if employee: + employee_details = frappe.db.get_value( + "Employee", employee, ["employee_name", "branch", "designation", "department"], as_dict=1 + ) + target.employee = employee + target.employee_name = employee_details.employee_name + target.branch = employee_details.branch + target.designation = employee_details.designation + target.department = employee_details.department + + if posting_date: + target.posting_date = posting_date + + target.run_method("process_salary_structure", for_preview=for_preview) + + doc = get_mapped_doc( + "Salary Structure", + source_name, + { + "Salary Structure": { + "doctype": "Salary Slip", + "field_map": { + "total_earning": "gross_pay", + "name": "salary_structure", + "currency": "currency", + }, + } + }, + target_doc, + postprocess, + ignore_child_tables=True, + ignore_permissions=ignore_permissions, + ) + + if cint(as_print): + doc.name = "Preview for {0}".format(employee) + return frappe.get_print(doc.doctype, doc.name, doc=doc, print_format=print_format) + else: + return doc + + +@frappe.whitelist() +def get_employees(salary_structure): + employees = frappe.get_list( + "Salary Structure Assignment", + filters={"salary_structure": salary_structure, "docstatus": 1}, + fields=["employee"], + ) + + if not employees: + frappe.throw( + _( + "There's no Employee with Salary Structure: {0}. Assign {1} to an Employee to preview Salary Slip" + ).format(salary_structure, salary_structure) + ) + + return list(set([d.employee for d in employees])) diff --git a/hrms/payroll/doctype/salary_structure/salary_structure_dashboard.py b/hrms/payroll/doctype/salary_structure/salary_structure_dashboard.py new file mode 100644 index 0000000..cf363b4 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/salary_structure_dashboard.py @@ -0,0 +1,9 @@ +def get_data(): + return { + "fieldname": "salary_structure", + "non_standard_fieldnames": {"Employee Grade": "default_salary_structure"}, + "transactions": [ + {"items": ["Salary Structure Assignment", "Salary Slip"]}, + {"items": ["Employee Grade"]}, + ], + } diff --git a/hrms/payroll/doctype/salary_structure/test_salary_structure.py b/hrms/payroll/doctype/salary_structure/test_salary_structure.py new file mode 100644 index 0000000..265097b --- /dev/null +++ b/hrms/payroll/doctype/salary_structure/test_salary_structure.py @@ -0,0 +1,286 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import unittest + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_years, date_diff, get_first_day, nowdate +from frappe.utils.make_random import get_random + +import erpnext +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( + create_payroll_period, +) +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + create_tax_slab, + make_deduction_salary_component, + make_earning_salary_component, + make_employee_salary_slip, +) +from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip + +test_dependencies = ["Fiscal Year"] + + +class TestSalaryStructure(FrappeTestCase): + def setUp(self): + for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment"]: + frappe.db.sql("delete from `tab%s`" % dt) + + self.make_holiday_list() + frappe.db.set_value( + "Company", + erpnext.get_default_company(), + "default_holiday_list", + "Salary Structure Test Holiday List", + ) + make_employee("test_employee@salary.com") + make_employee("test_employee_2@salary.com") + + def make_holiday_list(self): + if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"): + holiday_list = frappe.get_doc( + { + "doctype": "Holiday List", + "holiday_list_name": "Salary Structure Test Holiday List", + "from_date": nowdate(), + "to_date": add_years(nowdate(), 1), + "weekly_off": "Sunday", + } + ).insert() + holiday_list.get_weekly_off_dates() + holiday_list.save() + + def test_salary_structure_deduction_based_on_gross_pay(self): + + emp = make_employee("test_employee_3@salary.com") + + sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit=True) + + sal_struct.earnings = [sal_struct.earnings[0]] + sal_struct.earnings[0].amount_based_on_formula = 1 + sal_struct.earnings[0].formula = "base" + + sal_struct.deductions = [sal_struct.deductions[0]] + + sal_struct.deductions[0].amount_based_on_formula = 1 + sal_struct.deductions[0].condition = "gross_pay > 100" + sal_struct.deductions[0].formula = "gross_pay * 0.2" + + sal_struct.submit() + + assignment = create_salary_structure_assignment(emp, "Salary Structure 2") + ss = make_salary_slip(sal_struct.name, employee=emp) + + self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) + + def test_amount_totals(self): + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) + sal_slip = frappe.get_value("Salary Slip", {"employee_name": "test_employee_2@salary.com"}) + if not sal_slip: + sal_slip = make_employee_salary_slip( + "test_employee_2@salary.com", "Monthly", "Salary Structure Sample" + ) + self.assertEqual(sal_slip.get("salary_structure"), "Salary Structure Sample") + self.assertEqual(sal_slip.get("earnings")[0].amount, 50000) + self.assertEqual(sal_slip.get("earnings")[1].amount, 3000) + self.assertEqual(sal_slip.get("earnings")[2].amount, 25000) + self.assertEqual(sal_slip.get("gross_pay"), 78000) + self.assertEqual(sal_slip.get("deductions")[0].amount, 200) + self.assertEqual(sal_slip.get("net_pay"), 78000 - sal_slip.get("total_deduction")) + + def test_whitespaces_in_formula_conditions_fields(self): + salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True) + + for row in salary_structure.earnings: + row.formula = "\n%s\n\n" % row.formula + row.condition = "\n%s\n\n" % row.condition + + for row in salary_structure.deductions: + row.formula = "\n%s\n\n" % row.formula + row.condition = "\n%s\n\n" % row.condition + + salary_structure.save() + + for row in salary_structure.earnings: + self.assertFalse("\n" in row.formula or "\n" in row.condition) + + for row in salary_structure.deductions: + self.assertFalse(("\n" in row.formula) or ("\n" in row.condition)) + + def test_salary_structures_assignment(self): + company_currency = erpnext.get_default_currency() + salary_structure = make_salary_structure( + "Salary Structure Sample", "Monthly", currency=company_currency + ) + employee = "test_assign_stucture@salary.com" + employee_doc_name = make_employee(employee) + # clear the already assigned stuctures + frappe.db.sql( + """delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s """, + ("test_assign_stucture@salary.com", salary_structure.name), + ) + # test structure_assignment + salary_structure.assign_salary_structure( + employee=employee_doc_name, from_date="2013-01-01", base=5000, variable=200 + ) + salary_structure_assignment = frappe.get_doc( + "Salary Structure Assignment", {"employee": employee_doc_name, "from_date": "2013-01-01"} + ) + self.assertEqual(salary_structure_assignment.docstatus, 1) + self.assertEqual(salary_structure_assignment.base, 5000) + self.assertEqual(salary_structure_assignment.variable, 200) + + def test_employee_grade_defaults(self): + salary_structure = make_salary_structure( + "Salary Structure - Lead", "Monthly", currency="INR", company="_Test Company" + ) + create_employee_grade("Lead", salary_structure.name) + employee = make_employee("test_employee_grade@salary.com", company="_Test Company", grade="Lead") + + # structure assignment should have the default salary structure and base pay + salary_structure.assign_salary_structure(employee=employee, from_date=nowdate()) + structure, base = frappe.db.get_value( + "Salary Structure Assignment", + {"employee": employee, "salary_structure": salary_structure.name, "from_date": nowdate()}, + ["salary_structure", "base"], + ) + self.assertEqual(structure, salary_structure.name) + self.assertEqual(base, 50000) + + def test_multi_currency_salary_structure(self): + make_employee("test_muti_currency_employee@salary.com") + sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency="USD") + self.assertEqual(sal_struct.currency, "USD") + + +def make_salary_structure( + salary_structure, + payroll_frequency, + employee=None, + from_date=None, + dont_submit=False, + other_details=None, + test_tax=False, + company=None, + currency=erpnext.get_default_currency(), + payroll_period=None, + include_flexi_benefits=False, + base=None, +): + if frappe.db.exists("Salary Structure", salary_structure): + frappe.db.delete("Salary Structure", salary_structure) + + details = { + "doctype": "Salary Structure", + "name": salary_structure, + "company": company or erpnext.get_default_company(), + "earnings": make_earning_salary_component( + setup=True, + test_tax=test_tax, + company_list=["_Test Company"], + include_flexi_benefits=include_flexi_benefits, + ), + "deductions": make_deduction_salary_component( + setup=True, test_tax=test_tax, company_list=["_Test Company"] + ), + "payroll_frequency": payroll_frequency, + "payment_account": get_random("Account", filters={"account_currency": currency}), + "currency": currency, + } + if other_details and isinstance(other_details, dict): + details.update(other_details) + salary_structure_doc = frappe.get_doc(details) + salary_structure_doc.insert() + if not dont_submit: + salary_structure_doc.submit() + + filters = {"employee": employee, "docstatus": 1} + if not from_date and payroll_period: + from_date = payroll_period.start_date + + if from_date: + filters["from_date"] = from_date + + if ( + employee + and not frappe.db.get_value("Salary Structure Assignment", filters) + and salary_structure_doc.docstatus == 1 + ): + create_salary_structure_assignment( + employee, + salary_structure, + from_date=from_date, + company=company, + currency=currency, + payroll_period=payroll_period, + base=base, + ) + + return salary_structure_doc + + +def create_salary_structure_assignment( + employee, + salary_structure, + from_date=None, + company=None, + currency=erpnext.get_default_currency(), + payroll_period=None, + base=None, + allow_duplicate=False, +): + if not allow_duplicate and frappe.db.exists( + "Salary Structure Assignment", {"employee": employee} + ): + frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""", (employee)) + + if not payroll_period: + payroll_period = create_payroll_period() + + income_tax_slab = frappe.db.get_value("Income Tax Slab", {"currency": currency}) + + if not income_tax_slab: + income_tax_slab = create_tax_slab(payroll_period, allow_tax_exemption=True, currency=currency) + + salary_structure_assignment = frappe.new_doc("Salary Structure Assignment") + salary_structure_assignment.employee = employee + salary_structure_assignment.base = base or 50000 + salary_structure_assignment.variable = 5000 + + if not from_date: + from_date = get_first_day(nowdate()) + joining_date = frappe.get_cached_value("Employee", employee, "date_of_joining") + if date_diff(joining_date, from_date) > 0: + from_date = joining_date + + salary_structure_assignment.from_date = from_date + salary_structure_assignment.salary_structure = salary_structure + salary_structure_assignment.currency = currency + salary_structure_assignment.payroll_payable_account = get_payable_account(company) + salary_structure_assignment.company = company or erpnext.get_default_company() + salary_structure_assignment.save(ignore_permissions=True) + salary_structure_assignment.income_tax_slab = income_tax_slab + salary_structure_assignment.submit() + return salary_structure_assignment + + +def get_payable_account(company=None): + if not company: + company = erpnext.get_default_company() + return frappe.db.get_value("Company", company, "default_payroll_payable_account") + + +def create_employee_grade(grade, default_structure=None): + if not frappe.db.exists("Employee Grade", grade): + frappe.get_doc( + { + "doctype": "Employee Grade", + "__newname": grade, + "default_salary_structure": default_structure, + "default_base_pay": 50000, + } + ).insert() diff --git a/hrms/payroll/doctype/salary_structure_assignment/__init__.py b/hrms/payroll/doctype/salary_structure_assignment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js new file mode 100644 index 0000000..220bfbf --- /dev/null +++ b/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -0,0 +1,76 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Salary Structure Assignment', { + setup: function(frm) { + frm.set_query("employee", function() { + return { + query: "erpnext.controllers.queries.employee_query", + } + }); + frm.set_query("salary_structure", function() { + return { + filters: { + company: frm.doc.company, + docstatus: 1, + is_active: "Yes" + } + } + }); + + frm.set_query("income_tax_slab", function() { + return { + filters: { + company: frm.doc.company, + docstatus: 1, + disabled: 0, + currency: frm.doc.currency + } + }; + }); + + frm.set_query("payroll_payable_account", function() { + var company_currency = erpnext.get_currency(frm.doc.company); + return { + filters: { + "company": frm.doc.company, + "root_type": "Liability", + "is_group": 0, + "account_currency": ["in", [frm.doc.currency, company_currency]], + } + } + }); + + frm.set_query("cost_center", "payroll_cost_centers", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.call({ + method: "set_payroll_cost_centers", + doc: frm.doc, + callback: function(data) { + refresh_field("payroll_cost_centers"); + } + }); + } + else { + frm.set_value("payroll_cost_centers", []); + } + }, + + company: function(frm) { + if (frm.doc.company) { + frappe.db.get_value("Company", frm.doc.company, "default_payroll_payable_account", (r) => { + frm.set_value("payroll_payable_account", r.default_payroll_payable_account); + }); + } + } +}); diff --git a/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json new file mode 100644 index 0000000..91d49a0 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -0,0 +1,231 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-SSA-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:38:41.769237", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "grade", + "company", + "payroll_payable_account", + "column_break_6", + "designation", + "salary_structure", + "from_date", + "income_tax_slab", + "currency", + "section_break_7", + "base", + "column_break_9", + "variable", + "amended_from", + "section_break_17", + "payroll_cost_centers" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fetch_from": "grade.default_salary_structure", + "fetch_if_empty": 1, + "fieldname": "salary_structure", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Salary Structure", + "options": "Salary Structure", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Base & Variable" + }, + { + "fetch_from": "grade.default_base_pay", + "fetch_if_empty": 1, + "fieldname": "base", + "fieldtype": "Currency", + "label": "Base", + "options": "currency" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "variable", + "fieldtype": "Currency", + "label": "Variable", + "options": "currency" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Salary Structure Assignment", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "salary_structure", + "fieldname": "income_tax_slab", + "fieldtype": "Link", + "label": "Income Tax Slab", + "options": "Income Tax Slab" + }, + { + "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", + "fetch_from": "salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "employee", + "fieldname": "payroll_payable_account", + "fieldtype": "Link", + "label": "Payroll Payable Account", + "options": "Account" + }, + { + "collapsible": 1, + "depends_on": "employee", + "fieldname": "section_break_17", + "fieldtype": "Section Break", + "label": "Payroll Cost Centers" + }, + { + "allow_on_submit": 1, + "fieldname": "payroll_cost_centers", + "fieldtype": "Table", + "label": "Cost Centers", + "options": "Employee Cost Center" + }, + { + "fetch_from": "employee.grade", + "fieldname": "grade", + "fieldtype": "Link", + "label": "Grade", + "options": "Employee Grade", + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2022-05-06 12:18:36.972336", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Structure Assignment", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee_name, salary_structure", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py new file mode 100644 index 0000000..28b5eb1 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py @@ -0,0 +1,134 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt, getdate + + +class DuplicateAssignment(frappe.ValidationError): + pass + + +class SalaryStructureAssignment(Document): + def validate(self): + self.validate_dates() + self.validate_income_tax_slab() + self.set_payroll_payable_account() + if not self.get("payroll_cost_centers"): + self.set_payroll_cost_centers() + + self.validate_cost_center_distribution() + + def validate_dates(self): + joining_date, relieving_date = frappe.db.get_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) + + if self.from_date: + if frappe.db.exists( + "Salary Structure Assignment", + {"employee": self.employee, "from_date": self.from_date, "docstatus": 1}, + ): + frappe.throw(_("Salary Structure Assignment for Employee already exists"), DuplicateAssignment) + + if joining_date and getdate(self.from_date) < joining_date: + frappe.throw( + _("From Date {0} cannot be before employee's joining Date {1}").format( + self.from_date, joining_date + ) + ) + + # flag - old_employee is for migrating the old employees data via patch + if relieving_date and getdate(self.from_date) > relieving_date and not self.flags.old_employee: + frappe.throw( + _("From Date {0} cannot be after employee's relieving Date {1}").format( + self.from_date, relieving_date + ) + ) + + def validate_income_tax_slab(self): + if not self.income_tax_slab: + return + + income_tax_slab_currency = frappe.db.get_value( + "Income Tax Slab", self.income_tax_slab, "currency" + ) + if self.currency != income_tax_slab_currency: + frappe.throw( + _("Currency of selected Income Tax Slab should be {0} instead of {1}").format( + self.currency, income_tax_slab_currency + ) + ) + + def set_payroll_payable_account(self): + if not self.payroll_payable_account: + payroll_payable_account = frappe.db.get_value( + "Company", self.company, "default_payroll_payable_account" + ) + if not payroll_payable_account: + payroll_payable_account = frappe.db.get_value( + "Account", + { + "account_name": _("Payroll Payable"), + "company": self.company, + "account_currency": frappe.db.get_value("Company", self.company, "default_currency"), + "is_group": 0, + }, + ) + self.payroll_payable_account = payroll_payable_account + + @frappe.whitelist() + def set_payroll_cost_centers(self): + self.payroll_cost_centers = [] + default_payroll_cost_center = self.get_payroll_cost_center() + if default_payroll_cost_center: + self.append( + "payroll_cost_centers", {"cost_center": default_payroll_cost_center, "percentage": 100} + ) + + def get_payroll_cost_center(self): + payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center") + if not payroll_cost_center and self.department: + payroll_cost_center = frappe.db.get_value("Department", self.department, "payroll_cost_center") + + return payroll_cost_center + + def validate_cost_center_distribution(self): + if self.get("payroll_cost_centers"): + total_percentage = sum([flt(d.percentage) for d in self.get("payroll_cost_centers", [])]) + if total_percentage != 100: + frappe.throw(_("Total percentage against cost centers should be 100")) + + +def get_assigned_salary_structure(employee, on_date): + if not employee or not on_date: + return None + salary_structure = frappe.db.sql( + """ + select salary_structure from `tabSalary Structure Assignment` + where employee=%(employee)s + and docstatus = 1 + and %(on_date)s >= from_date order by from_date desc limit 1""", + { + "employee": employee, + "on_date": on_date, + }, + ) + return salary_structure[0][0] if salary_structure else None + + +@frappe.whitelist() +def get_employee_currency(employee): + employee_currency = frappe.db.get_value( + "Salary Structure Assignment", {"employee": employee}, "currency" + ) + if not employee_currency: + frappe.throw( + _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( + employee + ) + ) + return employee_currency diff --git a/hrms/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py b/hrms/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py new file mode 100644 index 0000000..56dd0d0 --- /dev/null +++ b/hrms/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import unittest + + +class TestSalaryStructureAssignment(unittest.TestCase): + pass diff --git a/hrms/payroll/doctype/taxable_salary_slab/__init__.py b/hrms/payroll/doctype/taxable_salary_slab/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json new file mode 100644 index 0000000..65d3824 --- /dev/null +++ b/hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -0,0 +1,68 @@ +{ + "actions": [], + "creation": "2018-04-13 17:42:13.516032", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_amount", + "to_amount", + "percent_deduction", + "condition", + "column_break_5", + "html_6" + ], + "fields": [ + { + "default": "0", + "fieldname": "from_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "From Amount", + "options": "currency", + "reqd": 1 + }, + { + "fieldname": "to_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "To Amount", + "options": "currency" + }, + { + "default": "0", + "fieldname": "percent_deduction", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Percent Deduction", + "reqd": 1 + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Condition" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "html_6", + "fieldtype": "HTML", + "options": "

Condition Examples

\n
    \n
  1. Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)
    \nCondition: date_of_birth>date(1937, 12, 31) and date_of_birth<date(1958, 01, 01)

  2. Applying tax by employee gender
    \nCondition: gender==\"Male\"

  3. \n
  4. Applying tax by Salary Component
    \nCondition: base > 10000
" + } + ], + "istable": 1, + "links": [], + "modified": "2020-10-19 13:44:39.549337", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Taxable Salary Slab", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py b/hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py new file mode 100644 index 0000000..d1ccbe3 --- /dev/null +++ b/hrms/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +# import frappe +from frappe.model.document import Document + + +class TaxableSalarySlab(Document): + pass diff --git a/hrms/payroll/module_onboarding/payroll/payroll.json b/hrms/payroll/module_onboarding/payroll/payroll.json new file mode 100644 index 0000000..b5226b2 --- /dev/null +++ b/hrms/payroll/module_onboarding/payroll/payroll.json @@ -0,0 +1,50 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-06-01 12:10:52.560472", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", + "idx": 0, + "is_complete": 0, + "modified": "2020-07-08 14:06:13.994310", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "owner": "Administrator", + "steps": [ + { + "step": "Create Employee" + }, + { + "step": "Create Salary Component" + }, + { + "step": "Create Payroll Period" + }, + { + "step": "Create Income Tax Slab" + }, + { + "step": "Create Salary Structure" + }, + { + "step": "Assign Salary Structure" + }, + { + "step": "Create Salary Slip" + }, + { + "step": "Payroll Settings" + } + ], + "subtitle": "Salary, Compensation, and more.", + "success_message": "The Payroll Module is all set up!", + "title": "Let's Set Up the Payroll Module. " +} \ No newline at end of file diff --git a/hrms/payroll/notification/as b/hrms/payroll/notification/as new file mode 100644 index 0000000..05c2c1b --- /dev/null +++ b/hrms/payroll/notification/as @@ -0,0 +1 @@ +update from `tabNotification` set module='Payroll' where name = "Retention Bonus" diff --git a/hrms/payroll/notification/retention_bonus/__init__.py b/hrms/payroll/notification/retention_bonus/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/notification/retention_bonus/retention_bonus.json b/hrms/payroll/notification/retention_bonus/retention_bonus.json new file mode 100644 index 0000000..37381fa --- /dev/null +++ b/hrms/payroll/notification/retention_bonus/retention_bonus.json @@ -0,0 +1,27 @@ +{ + "attach_print": 0, + "channel": "Email", + "condition": "doc.docstatus==1", + "creation": "2018-05-15 18:52:36.362838", + "date_changed": "bonus_payment_date", + "days_in_advance": 14, + "docstatus": 0, + "doctype": "Notification", + "document_type": "Retention Bonus", + "enabled": 1, + "event": "Days Before", + "idx": 0, + "is_standard": 1, + "message": "

{{ _(\"Hello\") }},

\n\n

{{ _(\"Retention Bonus for\") }} {{ doc.employee_name }} {{ _(\"due on\") }} {{ doc.bonus_payment_date }}

", + "modified": "2018-05-15 19:00:24.294418", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Retention Bonus", + "owner": "Administrator", + "recipients": [ + { + "email_by_role": "HR Manager" + } + ], + "subject": "Retention Bonus alert for {{ doc.employee }}" +} \ No newline at end of file diff --git a/hrms/payroll/notification/retention_bonus/retention_bonus.md b/hrms/payroll/notification/retention_bonus/retention_bonus.md new file mode 100644 index 0000000..8f48193 --- /dev/null +++ b/hrms/payroll/notification/retention_bonus/retention_bonus.md @@ -0,0 +1,3 @@ +

{{ _("Hello") }},

+ +

{{ _("Retention Bonus for") }} {{ doc.employee_name }} {{ _("due on") }} {{ doc.bonus_payment_date }}

\ No newline at end of file diff --git a/hrms/payroll/notification/retention_bonus/retention_bonus.py b/hrms/payroll/notification/retention_bonus/retention_bonus.py new file mode 100644 index 0000000..02e3e93 --- /dev/null +++ b/hrms/payroll/notification/retention_bonus/retention_bonus.py @@ -0,0 +1,3 @@ +def get_context(context): + # do your magic here + pass diff --git a/hrms/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json b/hrms/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json new file mode 100644 index 0000000..fa5739b --- /dev/null +++ b/hrms/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-22 11:56:34.575627", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee Tax Exemption Declaration", + "dynamic_filters_json": "[[\"Employee Tax Exemption Declaration\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Employee Tax Exemption Declaration\",\"creation\",\"Timespan\",\"last year\",false],[\"Employee Tax Exemption Declaration\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Declaration Submitted", + "modified": "2020-07-22 13:22:46.001099", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Total Declaration Submitted", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json b/hrms/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json new file mode 100644 index 0000000..2106706 --- /dev/null +++ b/hrms/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json @@ -0,0 +1,22 @@ +{ + "aggregate_function_based_on": "incentive_amount", + "creation": "2020-07-22 11:56:34.599047", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Employee Incentive", + "dynamic_filters_json": "", + "filters_json": "[[\"Employee Incentive\",\"docstatus\",\"=\",\"1\",false],[\"Employee Incentive\",\"payroll_date\",\"Timespan\",\"last year\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Incentive Given(Last month)", + "modified": "2020-07-23 12:05:26.963616", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Total Incentive Given(Last month)", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json b/hrms/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json new file mode 100644 index 0000000..44ee722 --- /dev/null +++ b/hrms/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json @@ -0,0 +1,22 @@ +{ + "aggregate_function_based_on": "rounded_total", + "creation": "2020-07-22 11:56:34.626019", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Salary Slip", + "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false],[\"Salary Slip\",\"start_date\",\"Timespan\",\"last month\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Outgoing Salary(Last month)", + "modified": "2020-07-22 13:54:14.678954", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Total Outgoing Salary(Last month)", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/payroll/number_card/total_salary_structure/total_salary_structure.json b/hrms/payroll/number_card/total_salary_structure/total_salary_structure.json new file mode 100644 index 0000000..030935f --- /dev/null +++ b/hrms/payroll/number_card/total_salary_structure/total_salary_structure.json @@ -0,0 +1,21 @@ +{ + "creation": "2020-07-22 11:56:34.688843", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Salary Structure", + "dynamic_filters_json": "[[\"Salary Structure\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Salary Structure\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Count", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Total Salary Structure", + "modified": "2020-07-22 13:24:03.938846", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Total Salary Structure", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json b/hrms/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json new file mode 100644 index 0000000..8a07b10 --- /dev/null +++ b/hrms/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:58:43.927590", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:58:43.927590", + "modified_by": "Administrator", + "name": "Assign Salary Structure", + "owner": "Administrator", + "reference_document": "Salary Structure Assignment", + "show_full_form": 1, + "title": "Assign Salary Structure", + "validate_action": 1 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/create_employee/create_employee.json b/hrms/payroll/onboarding_step/create_employee/create_employee.json new file mode 100644 index 0000000..3aa33c6 --- /dev/null +++ b/hrms/payroll/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:43:25.561152", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:26:28.629074", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "show_full_form": 0, + "title": "Create Employee", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json b/hrms/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json new file mode 100644 index 0000000..faada7e --- /dev/null +++ b/hrms/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:54:54.823796", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:54:54.823796", + "modified_by": "Administrator", + "name": "Create Income Tax Slab", + "owner": "Administrator", + "reference_document": "Income Tax Slab", + "show_full_form": 1, + "title": "Create Income Tax Slab", + "validate_action": 1 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/hrms/payroll/onboarding_step/create_payroll_period/create_payroll_period.json new file mode 100644 index 0000000..b1a7cc2 --- /dev/null +++ b/hrms/payroll/onboarding_step/create_payroll_period/create_payroll_period.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:53:54.553947", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-29 11:53:54.553947", + "modified_by": "Administrator", + "name": "Create Payroll Period", + "owner": "Administrator", + "reference_document": "Payroll Period", + "show_full_form": 0, + "title": "Create Payroll Period", + "validate_action": 1 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/create_salary_component/create_salary_component.json b/hrms/payroll/onboarding_step/create_salary_component/create_salary_component.json new file mode 100644 index 0000000..002d819 --- /dev/null +++ b/hrms/payroll/onboarding_step/create_salary_component/create_salary_component.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:57:04.002073", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:57:04.002073", + "modified_by": "Administrator", + "name": "Create Salary Component", + "owner": "Administrator", + "reference_document": "Salary Component", + "show_full_form": 1, + "title": "Create Salary Component", + "validate_action": 1 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/create_salary_slip/create_salary_slip.json b/hrms/payroll/onboarding_step/create_salary_slip/create_salary_slip.json new file mode 100644 index 0000000..2aa31f4 --- /dev/null +++ b/hrms/payroll/onboarding_step/create_salary_slip/create_salary_slip.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:59:29.972393", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:59:29.972393", + "modified_by": "Administrator", + "name": "Create Salary Slip", + "owner": "Administrator", + "reference_document": "Salary Slip", + "show_full_form": 1, + "title": "Create Salary Slip", + "validate_action": 1 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/create_salary_structure/create_salary_structure.json b/hrms/payroll/onboarding_step/create_salary_structure/create_salary_structure.json new file mode 100644 index 0000000..11d8327 --- /dev/null +++ b/hrms/payroll/onboarding_step/create_salary_structure/create_salary_structure.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:57:54.527808", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:57:54.527808", + "modified_by": "Administrator", + "name": "Create Salary Structure", + "owner": "Administrator", + "reference_document": "Salary Structure", + "show_full_form": 1, + "title": "Create Salary Structure", + "validate_action": 1 +} \ No newline at end of file diff --git a/hrms/payroll/onboarding_step/payroll_settings/payroll_settings.json b/hrms/payroll/onboarding_step/payroll_settings/payroll_settings.json new file mode 100644 index 0000000..a7cf7bf --- /dev/null +++ b/hrms/payroll/onboarding_step/payroll_settings/payroll_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Update Settings", + "creation": "2020-06-04 16:34:29.664917", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-06-29 16:34:29.664917", + "modified_by": "Administrator", + "name": "Payroll Settings", + "owner": "Administrator", + "reference_document": "Payroll Settings", + "show_full_form": 0, + "title": "Payroll Settings", + "validate_action": 0 +} \ No newline at end of file diff --git a/hrms/payroll/payroll_dashboard/payroll/payroll.json b/hrms/payroll/payroll_dashboard/payroll/payroll.json new file mode 100644 index 0000000..47e567e --- /dev/null +++ b/hrms/payroll/payroll_dashboard/payroll/payroll.json @@ -0,0 +1,42 @@ +{ + "cards": [ + { + "card": "Total Declaration Submitted" + }, + { + "card": "Total Salary Structure" + }, + { + "card": "Total Incentive Given(Last month)" + }, + { + "card": "Total Outgoing Salary(Last month)" + } + ], + "charts": [ + { + "chart": "Outgoing Salary", + "width": "Full" + }, + { + "chart": "Designation Wise Salary(Last Month)", + "width": "Half" + }, + { + "chart": "Department Wise Salary(Last Month)", + "width": "Half" + } + ], + "creation": "2020-07-22 11:56:34.727185", + "dashboard_name": "Payroll", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 1, + "is_standard": 1, + "modified": "2022-08-22 14:21:33.653983", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "owner": "Administrator" +} \ No newline at end of file diff --git a/hrms/payroll/print_format/__init__.py b/hrms/payroll/print_format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/print_format/salary_slip_based_on_timesheet/__init__.py b/hrms/payroll/print_format/salary_slip_based_on_timesheet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/hrms/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json new file mode 100644 index 0000000..d5fee6b --- /dev/null +++ b/hrms/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json @@ -0,0 +1,18 @@ +{ + "creation": "2016-07-07 12:38:32.447281", + "custom_format": 0, + "disabled": 0, + "doc_type": "Salary Slip", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \"

{{doc.name}}


\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\"}, {\"print_hide\": 0, \"fieldname\": \"department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_hours\"}, {\"print_hide\": 0, \"fieldname\": \"hour_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"time_sheet\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"working_hours\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"timesheets\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\"}]", + "idx": 0, + "modified": "2016-08-21 21:02:59.896033", + "modified_by": "Administrator", + "name": "Salary Slip based on Timesheet", + "owner": "Administrator", + "print_format_builder": 1, + "print_format_type": "Jinja", + "standard": "Yes" +} \ No newline at end of file diff --git a/hrms/payroll/print_format/salary_slip_standard/__init__.py b/hrms/payroll/print_format/salary_slip_standard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/print_format/salary_slip_standard/salary_slip_standard.json b/hrms/payroll/print_format/salary_slip_standard/salary_slip_standard.json new file mode 100644 index 0000000..98a4435 --- /dev/null +++ b/hrms/payroll/print_format/salary_slip_standard/salary_slip_standard.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2016-07-07 11:45:14.872204", + "custom_format": 0, + "disabled": 0, + "doc_type": "Salary Slip", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"

{{doc.name}}

\\n
\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\", \"label\": \"Employee\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\", \"label\": \"Employee Name\"}, {\"print_hide\": 0, \"fieldname\": \"department\", \"label\": \"Department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\", \"label\": \"Designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\", \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\", \"label\": \"Start Date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\", \"label\": \"End Date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_days\", \"label\": \"Working Days\"}, {\"print_hide\": 0, \"fieldname\": \"leave_without_pay\", \"label\": \"Leave Without Pay\"}, {\"print_hide\": 0, \"fieldname\": \"payment_days\", \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\", \"label\": \"Earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\", \"label\": \"Deductions\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\", \"label\": \"Gross Pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\", \"label\": \"Total Deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\", \"label\": \"Net Pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\", \"label\": \"Total in words\"}]", + "idx": 0, + "line_breaks": 0, + "modified": "2018-07-24 19:31:39.040701", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Slip Standard", + "owner": "Administrator", + "print_format_builder": 1, + "print_format_type": "Jinja", + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/hrms/payroll/print_format/salary_slip_with_year_to_date/__init__.py b/hrms/payroll/print_format/salary_slip_with_year_to_date/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json b/hrms/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json new file mode 100644 index 0000000..71ba37f --- /dev/null +++ b/hrms/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json @@ -0,0 +1,25 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2021-01-14 09:56:42.393623", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Salary Slip", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"

{{doc.name}}

\\n
\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"employee\", \"print_hide\": 0, \"label\": \"Employee\"}, {\"fieldname\": \"company\", \"print_hide\": 0, \"label\": \"Company\"}, {\"fieldname\": \"employee_name\", \"print_hide\": 0, \"label\": \"Employee Name\"}, {\"fieldname\": \"department\", \"print_hide\": 0, \"label\": \"Department\"}, {\"fieldname\": \"designation\", \"print_hide\": 0, \"label\": \"Designation\"}, {\"fieldname\": \"branch\", \"print_hide\": 0, \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"start_date\", \"print_hide\": 0, \"label\": \"Start Date\"}, {\"fieldname\": \"end_date\", \"print_hide\": 0, \"label\": \"End Date\"}, {\"fieldname\": \"total_working_days\", \"print_hide\": 0, \"label\": \"Working Days\"}, {\"fieldname\": \"leave_without_pay\", \"print_hide\": 0, \"label\": \"Leave Without Pay\"}, {\"fieldname\": \"payment_days\", \"print_hide\": 0, \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"earnings\", \"print_hide\": 0, \"label\": \"Earnings\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"deductions\", \"print_hide\": 0, \"label\": \"Deductions\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"gross_pay\", \"print_hide\": 0, \"label\": \"Gross Pay\"}, {\"fieldname\": \"total_deduction\", \"print_hide\": 0, \"label\": \"Total Deduction\"}, {\"fieldname\": \"net_pay\", \"print_hide\": 0, \"label\": \"Net Pay\"}, {\"fieldname\": \"rounded_total\", \"print_hide\": 0, \"label\": \"Rounded Total\"}, {\"fieldname\": \"total_in_words\", \"print_hide\": 0, \"label\": \"Total in words\"}, {\"fieldtype\": \"Section Break\", \"label\": \"net pay info\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"year_to_date\", \"print_hide\": 0, \"label\": \"Year To Date\"}, {\"fieldname\": \"month_to_date\", \"print_hide\": 0, \"label\": \"Month To Date\"}]", + "idx": 0, + "line_breaks": 0, + "modified": "2021-01-14 10:03:45.283725", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip with Year to Date", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/hrms/payroll/report/__init__.py b/hrms/payroll/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/bank_remittance/__init__.py b/hrms/payroll/report/bank_remittance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/bank_remittance/bank_remittance.js b/hrms/payroll/report/bank_remittance/bank_remittance.js new file mode 100644 index 0000000..8b75b4f --- /dev/null +++ b/hrms/payroll/report/bank_remittance/bank_remittance.js @@ -0,0 +1,27 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Bank Remittance"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"from_date", + label: __("From Date"), + fieldtype: "Date", + }, + { + fieldname:"to_date", + label: __("To Date"), + fieldtype: "Date", + }, + + ] +} diff --git a/hrms/payroll/report/bank_remittance/bank_remittance.json b/hrms/payroll/report/bank_remittance/bank_remittance.json new file mode 100644 index 0000000..2a697b2 --- /dev/null +++ b/hrms/payroll/report/bank_remittance/bank_remittance.json @@ -0,0 +1,24 @@ +{ + "add_total_row": 0, + "creation": "2019-03-26 16:57:52.558895", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-28 00:08:08.097494", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Bank Remittance", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Payroll Entry", + "report_name": "Bank Remittance", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/payroll/report/bank_remittance/bank_remittance.py b/hrms/payroll/report/bank_remittance/bank_remittance.py new file mode 100644 index 0000000..9d8efff --- /dev/null +++ b/hrms/payroll/report/bank_remittance/bank_remittance.py @@ -0,0 +1,173 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _, get_all + + +def execute(filters=None): + columns = [ + { + "label": _("Payroll Number"), + "fieldtype": "Link", + "fieldname": "payroll_no", + "options": "Payroll Entry", + "width": 150, + }, + { + "label": _("Debit A/C Number"), + "fieldtype": "Int", + "fieldname": "debit_account", + "hidden": 1, + "width": 200, + }, + {"label": _("Payment Date"), "fieldtype": "Data", "fieldname": "payment_date", "width": 100}, + { + "label": _("Employee Name"), + "fieldtype": "Link", + "fieldname": "employee_name", + "options": "Employee", + "width": 200, + }, + {"label": _("Bank Name"), "fieldtype": "Data", "fieldname": "bank_name", "width": 50}, + { + "label": _("Employee A/C Number"), + "fieldtype": "Int", + "fieldname": "employee_account_no", + "width": 50, + }, + ] + + if frappe.db.has_column("Employee", "ifsc_code"): + columns.append( + {"label": _("IFSC Code"), "fieldtype": "Data", "fieldname": "bank_code", "width": 100} + ) + + columns += [ + {"label": _("Currency"), "fieldtype": "Data", "fieldname": "currency", "width": 50}, + { + "label": _("Net Salary Amount"), + "fieldtype": "Currency", + "options": "currency", + "fieldname": "amount", + "width": 100, + }, + ] + + data = [] + + accounts = get_bank_accounts() + payroll_entries = get_payroll_entries(accounts, filters) + salary_slips = get_salary_slips(payroll_entries) + + if frappe.db.has_column("Employee", "ifsc_code"): + get_emp_bank_ifsc_code(salary_slips) + + for salary in salary_slips: + if ( + salary.bank_name + and salary.bank_account_no + and salary.debit_acc_no + and salary.status in ["Submitted", "Paid"] + ): + row = { + "payroll_no": salary.payroll_entry, + "debit_account": salary.debit_acc_no, + "payment_date": frappe.utils.formatdate(salary.modified.strftime("%Y-%m-%d")), + "bank_name": salary.bank_name, + "employee_account_no": salary.bank_account_no, + "bank_code": salary.ifsc_code, + "employee_name": salary.employee + ": " + salary.employee_name, + "currency": frappe.get_cached_value("Company", filters.company, "default_currency"), + "amount": salary.net_pay, + } + data.append(row) + + return columns, data + + +def get_bank_accounts(): + accounts = [d.name for d in get_all("Account", filters={"account_type": "Bank"})] + return accounts + + +def get_payroll_entries(accounts, filters): + payroll_filter = [ + ("payment_account", "IN", accounts), + ("number_of_employees", ">", 0), + ("Company", "=", filters.company), + ] + if filters.to_date: + payroll_filter.append(("posting_date", "<", filters.to_date)) + + if filters.from_date: + payroll_filter.append(("posting_date", ">", filters.from_date)) + + entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"]) + + payment_accounts = [d.payment_account for d in entries] + entries = set_company_account(payment_accounts, entries) + return entries + + +def get_salary_slips(payroll_entries): + payroll = [d.name for d in payroll_entries] + salary_slips = get_all( + "Salary Slip", + filters=[("payroll_entry", "IN", payroll)], + fields=[ + "modified", + "net_pay", + "bank_name", + "bank_account_no", + "payroll_entry", + "employee", + "employee_name", + "status", + ], + ) + + payroll_entry_map = {} + for entry in payroll_entries: + payroll_entry_map[entry.name] = entry + + # appending company debit accounts + for slip in salary_slips: + if slip.payroll_entry: + slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]["company_account"] + else: + slip["debit_acc_no"] = None + + return salary_slips + + +def get_emp_bank_ifsc_code(salary_slips): + emp_names = [d.employee for d in salary_slips] + ifsc_codes = get_all("Employee", [("name", "IN", emp_names)], ["ifsc_code", "name"]) + + ifsc_codes_map = {} + for code in ifsc_codes: + ifsc_codes_map[code.name] = code + + for slip in salary_slips: + slip["ifsc_code"] = ifsc_codes_map[code.name]["ifsc_code"] + + return salary_slips + + +def set_company_account(payment_accounts, payroll_entries): + company_accounts = get_all( + "Bank Account", [("account", "in", payment_accounts)], ["account", "bank_account_no"] + ) + company_accounts_map = {} + for acc in company_accounts: + company_accounts_map[acc.account] = acc + + for entry in payroll_entries: + company_account = "" + if entry.payment_account in company_accounts_map: + company_account = company_accounts_map[entry.payment_account]["bank_account_no"] + entry["company_account"] = company_account + + return payroll_entries diff --git a/hrms/payroll/report/income_tax_computation/__init__.py b/hrms/payroll/report/income_tax_computation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/income_tax_computation/income_tax_computation.js b/hrms/payroll/report/income_tax_computation/income_tax_computation.js new file mode 100644 index 0000000..26e463f --- /dev/null +++ b/hrms/payroll/report/income_tax_computation/income_tax_computation.js @@ -0,0 +1,47 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Income Tax Computation"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "width": "100px", + "reqd": 1 + }, + { + "fieldname":"payroll_period", + "label": __("Payroll Period"), + "fieldtype": "Link", + "options": "Payroll Period", + "width": "100px", + "reqd": 1 + }, + { + "fieldname":"employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee", + "width": "100px" + }, + { + "fieldname":"department", + "label": __("Department"), + "fieldtype": "Link", + "options": "Department", + "width": "100px", + }, + { + "fieldname":"consider_tax_exemption_declaration", + "label": __("Consider Tax Exemption Declaration"), + "fieldtype": "Check", + "width": "180px" + } + ] +}; + + diff --git a/hrms/payroll/report/income_tax_computation/income_tax_computation.json b/hrms/payroll/report/income_tax_computation/income_tax_computation.json new file mode 100644 index 0000000..7cb5b22 --- /dev/null +++ b/hrms/payroll/report/income_tax_computation/income_tax_computation.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2022-02-17 17:19:30.921422", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2022-02-23 13:07:30.347861", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Income Tax Computation", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Income Tax Computation", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee Self Service" + } + ] +} \ No newline at end of file diff --git a/hrms/payroll/report/income_tax_computation/income_tax_computation.py b/hrms/payroll/report/income_tax_computation/income_tax_computation.py new file mode 100644 index 0000000..bc6fb32 --- /dev/null +++ b/hrms/payroll/report/income_tax_computation/income_tax_computation.py @@ -0,0 +1,516 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _, scrub +from frappe.query_builder.functions import Sum +from frappe.utils import add_days, flt, getdate, rounded + +from hrms.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates +from hrms.payroll.doctype.salary_slip.salary_slip import calculate_tax_by_tax_slab + + +def execute(filters=None): + return IncomeTaxComputationReport(filters).run() + + +class IncomeTaxComputationReport(object): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.columns = [] + self.data = [] + self.employees = frappe._dict() + self.payroll_period_start_date = None + self.payroll_period_end_date = None + if self.filters.payroll_period: + self.payroll_period_start_date, self.payroll_period_end_date = frappe.db.get_value( + "Payroll Period", self.filters.payroll_period, ["start_date", "end_date"] + ) + + def run(self): + self.get_fixed_columns() + self.get_data() + return self.columns, self.data + + def get_data(self): + self.get_employee_details() + self.get_future_salary_slips() + self.get_ctc() + self.get_tax_exempted_earnings_and_deductions() + self.get_employee_tax_exemptions() + self.get_hra() + self.get_standard_tax_exemption() + self.get_total_taxable_amount() + self.get_applicable_tax() + self.get_total_deducted_tax() + self.get_payable_tax() + + self.data = list(self.employees.values()) + + def get_employee_details(self): + filters, or_filters = self.get_employee_filters() + fields = [ + "name as employee", + "employee_name", + "department", + "designation", + "date_of_joining", + "relieving_date", + ] + + employees = frappe.get_all("Employee", filters=filters, or_filters=or_filters, fields=fields) + ss_assignments = self.get_ss_assignments([d.employee for d in employees]) + + for d in employees: + if d.employee in list(ss_assignments.keys()): + d.update(ss_assignments[d.employee]) + self.employees.setdefault(d.employee, d) + + if not self.employees: + frappe.throw(_("No employees found with selected filters and active salary structure")) + + def get_employee_filters(self): + filters = {"company": self.filters.company} + or_filters = { + "status": "Active", + "relieving_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]], + } + if self.filters.employee: + filters = {"name": self.filters.employee} + elif self.filters.department: + filters.update({"department": self.filters.department}) + + return filters, or_filters + + def get_ss_assignments(self, employees): + ss_assignments = frappe.get_all( + "Salary Structure Assignment", + filters={ + "employee": ["in", employees], + "docstatus": 1, + "salary_structure": ["is", "set"], + "income_tax_slab": ["is", "set"], + }, + fields=["employee", "income_tax_slab", "salary_structure"], + order_by="from_date desc", + ) + + employee_ss_assignments = frappe._dict() + for d in ss_assignments: + if d.employee not in list(employee_ss_assignments.keys()): + tax_slab = frappe.get_cached_value( + "Income Tax Slab", d.income_tax_slab, ["allow_tax_exemption", "disabled"], as_dict=1 + ) + + if tax_slab and not tax_slab.disabled: + employee_ss_assignments.setdefault( + d.employee, + { + "salary_structure": d.salary_structure, + "income_tax_slab": d.income_tax_slab, + "allow_tax_exemption": tax_slab.allow_tax_exemption, + }, + ) + return employee_ss_assignments + + def get_future_salary_slips(self): + self.future_salary_slips = frappe._dict() + for employee in list(self.employees.keys()): + last_ss = self.get_last_salary_slip(employee) + if last_ss and last_ss.end_date == self.payroll_period_end_date: + continue + + relieving_date = self.employees[employee].get("relieving_date", "") + if last_ss: + ss_start_date = add_days(last_ss.end_date, 1) + else: + ss_start_date = self.payroll_period_start_date + last_ss = frappe._dict( + { + "payroll_frequency": "Monthly", + "salary_structure": self.employees[employee].get("salary_structure"), + } + ) + + while getdate(ss_start_date) < getdate(self.payroll_period_end_date) and ( + not relieving_date or getdate(ss_start_date) < relieving_date + ): + ss_end_date = get_start_end_dates(last_ss.payroll_frequency, ss_start_date).end_date + + ss = frappe.new_doc("Salary Slip") + ss.employee = employee + ss.start_date = ss_start_date + ss.end_date = ss_end_date + ss.salary_structure = last_ss.salary_structure + ss.payroll_frequency = last_ss.payroll_frequency + ss.company = self.filters.company + try: + ss.process_salary_structure(for_preview=1) + self.future_salary_slips.setdefault(employee, []).append(ss.as_dict()) + except Exception: + break + + ss_start_date = add_days(ss_end_date, 1) + + def get_last_salary_slip(self, employee): + last_salary_slip = frappe.db.get_value( + "Salary Slip", + { + "employee": employee, + "docstatus": 1, + "start_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]], + }, + ["start_date", "end_date", "salary_structure", "payroll_frequency"], + order_by="start_date desc", + as_dict=1, + ) + + return last_salary_slip + + def get_ctc(self): + # Get total earnings from existing salary slip + ss = frappe.qb.DocType("Salary Slip") + existing_ss = frappe._dict( + ( + frappe.qb.from_(ss) + .select(ss.employee, Sum(ss.base_gross_pay).as_("amount")) + .where(ss.docstatus == 1) + .where(ss.employee.isin(list(self.employees.keys()))) + .where(ss.start_date >= self.payroll_period_start_date) + .where(ss.end_date <= self.payroll_period_end_date) + .groupby(ss.employee) + ).run() + ) + + for employee in list(self.employees.keys()): + future_ss_earnings = self.get_future_earnings(employee) + ctc = flt(existing_ss.get(employee)) + future_ss_earnings + + self.employees[employee].setdefault("ctc", ctc) + + def get_future_earnings(self, employee): + future_earnings = 0.0 + for ss in self.future_salary_slips.get(employee, []): + future_earnings += flt(ss.base_gross_pay) + + return future_earnings + + def get_tax_exempted_earnings_and_deductions(self): + tax_exempted_components = self.get_tax_exempted_components() + + if not tax_exempted_components: + return + + # Get component totals from existing salary slips + ss = frappe.qb.DocType("Salary Slip") + ss_comps = frappe.qb.DocType("Salary Detail") + + records = ( + frappe.qb.from_(ss) + .inner_join(ss_comps) + .on(ss.name == ss_comps.parent) + .select(ss.name, ss.employee, ss_comps.salary_component, Sum(ss_comps.amount).as_("amount")) + .where(ss.docstatus == 1) + .where(ss.employee.isin(list(self.employees.keys()))) + .where(ss_comps.salary_component.isin(tax_exempted_components)) + .where(ss.start_date >= self.payroll_period_start_date) + .where(ss.end_date <= self.payroll_period_end_date) + .groupby(ss.employee, ss_comps.salary_component) + ).run(as_dict=True) + + existing_ss_exemptions = frappe._dict() + for d in records: + existing_ss_exemptions.setdefault(d.employee, {}).setdefault( + scrub(d.salary_component), d.amount + ) + + for employee in list(self.employees.keys()): + if not self.employees[employee]["allow_tax_exemption"]: + continue + + exemptions = existing_ss_exemptions.get(employee, {}) + self.add_exemptions_from_future_salary_slips(employee, exemptions) + self.employees[employee].update(exemptions) + + total_exemptions = sum(list(exemptions.values())) + self.add_to_total_exemption(employee, total_exemptions) + + def add_exemptions_from_future_salary_slips(self, employee, exemptions): + for ss in self.future_salary_slips.get(employee, []): + for e in ss.earnings: + if not e.is_tax_applicable: + exemptions.setdefault(scrub(e.salary_component), 0) + exemptions[scrub(e.salary_component)] += flt(e.amount) + + for d in ss.deductions: + if d.exempted_from_income_tax: + exemptions.setdefault(scrub(d.salary_component), 0) + exemptions[scrub(d.salary_component)] += flt(d.amount) + + return exemptions + + def get_tax_exempted_components(self): + # nontaxable earning components + nontaxable_earning_components = [ + d.name + for d in frappe.get_all( + "Salary Component", {"type": "Earning", "is_tax_applicable": 0, "disabled": 0} + ) + ] + + # tax exempted deduction components + tax_exempted_deduction_components = [ + d.name + for d in frappe.get_all( + "Salary Component", {"type": "Deduction", "exempted_from_income_tax": 1, "disabled": 0} + ) + ] + + tax_exempted_components = nontaxable_earning_components + tax_exempted_deduction_components + + # Add columns + for d in tax_exempted_components: + self.add_column(d) + + return tax_exempted_components + + def add_to_total_exemption(self, employee, amount): + self.employees[employee].setdefault("total_exemption", 0) + self.employees[employee]["total_exemption"] += amount + + def get_employee_tax_exemptions(self): + # add columns + exemption_categories = frappe.get_all("Employee Tax Exemption Category", {"is_active": 1}) + for d in exemption_categories: + self.add_column(d.name) + + self.employees_with_proofs = [] + self.get_tax_exemptions("Employee Tax Exemption Proof Submission") + if self.filters.consider_tax_exemption_declaration: + self.get_tax_exemptions("Employee Tax Exemption Declaration") + + def get_tax_exemptions(self, source): + # Get category-wise exmeptions based on submitted proofs or declarations + if source == "Employee Tax Exemption Proof Submission": + child_doctype = "Employee Tax Exemption Proof Submission Detail" + else: + child_doctype = "Employee Tax Exemption Declaration Category" + + max_exemptions = self.get_max_exemptions_based_on_category() + + par = frappe.qb.DocType(source) + child = frappe.qb.DocType(child_doctype) + + records = ( + frappe.qb.from_(par) + .inner_join(child) + .on(par.name == child.parent) + .select(par.employee, child.exemption_category, Sum(child.amount).as_("amount")) + .where(par.docstatus == 1) + .where(par.employee.isin(list(self.employees.keys()))) + .where(par.payroll_period == self.filters.payroll_period) + .groupby(par.employee, child.exemption_category) + ).run(as_dict=True) + + for d in records: + if not self.employees[d.employee]["allow_tax_exemption"]: + continue + + if source == "Employee Tax Exemption Declaration" and d.employee in self.employees_with_proofs: + continue + + amount = flt(d.amount) + max_eligible_amount = flt(max_exemptions.get(d.exemption_category)) + if max_eligible_amount and amount > max_eligible_amount: + amount = max_eligible_amount + + self.employees[d.employee].setdefault(scrub(d.exemption_category), amount) + self.add_to_total_exemption(d.employee, amount) + + if ( + source == "Employee Tax Exemption Proof Submission" + and d.employee not in self.employees_with_proofs + ): + self.employees_with_proofs.append(d.employee) + + def get_max_exemptions_based_on_category(self): + return dict( + frappe.get_all( + "Employee Tax Exemption Category", + filters={"is_active": 1}, + fields=["name", "max_amount"], + as_list=1, + ) + ) + + def get_hra(self): + if not frappe.get_meta("Employee Tax Exemption Declaration").has_field("monthly_house_rent"): + return + + self.add_column("HRA") + + self.employees_with_proofs = [] + self.get_eligible_hra("Employee Tax Exemption Proof Submission") + if self.filters.consider_tax_exemption_declaration: + self.get_eligible_hra("Employee Tax Exemption Declaration") + + def get_eligible_hra(self, source): + if source == "Employee Tax Exemption Proof Submission": + hra_amount_field = "total_eligible_hra_exemption" + else: + hra_amount_field = "annual_hra_exemption" + + records = frappe.get_all( + source, + filters={ + "docstatus": 1, + "employee": ["in", list(self.employees.keys())], + "payroll_period": self.filters.payroll_period, + }, + fields=["employee", hra_amount_field], + as_list=1, + ) + + for d in records: + if not self.employees[d[0]]["allow_tax_exemption"]: + continue + + if d[0] not in self.employees_with_proofs: + self.employees[d[0]].setdefault("hra", d[1]) + self.add_to_total_exemption(d[0], d[1]) + self.employees_with_proofs.append(d[0]) + + def get_standard_tax_exemption(self): + self.add_column("Standard Tax Exemption") + + standard_exemptions_per_slab = dict( + frappe.get_all( + "Income Tax Slab", + filters={"company": self.filters.company, "docstatus": 1, "disabled": 0}, + fields=["name", "standard_tax_exemption_amount"], + as_list=1, + ) + ) + + for emp, emp_details in self.employees.items(): + if not self.employees[emp]["allow_tax_exemption"]: + continue + + income_tax_slab = emp_details.get("income_tax_slab") + standard_exemption = standard_exemptions_per_slab.get(income_tax_slab, 0) + emp_details["standard_tax_exemption"] = standard_exemption + self.add_to_total_exemption(emp, standard_exemption) + + self.add_column("Total Exemption") + + def get_total_taxable_amount(self): + self.add_column("Total Taxable Amount") + for emp, emp_details in self.employees.items(): + emp_details["total_taxable_amount"] = flt(emp_details.get("ctc")) - flt( + emp_details.get("total_exemption") + ) + + def get_applicable_tax(self): + self.add_column("Applicable Tax") + + is_tax_rounded = frappe.db.get_value( + "Salary Component", + {"variable_based_on_taxable_salary": 1, "disabled": 0}, + "round_to_the_nearest_integer", + ) + + for emp, emp_details in self.employees.items(): + tax_slab = emp_details.get("income_tax_slab") + if tax_slab: + tax_slab = frappe.get_cached_doc("Income Tax Slab", tax_slab) + employee_dict = frappe.get_doc("Employee", emp).as_dict() + tax_amount = calculate_tax_by_tax_slab( + emp_details["total_taxable_amount"], tax_slab, eval_globals=None, eval_locals=employee_dict + ) + else: + tax_amount = 0.0 + + if is_tax_rounded: + tax_amount = rounded(tax_amount) + emp_details["applicable_tax"] = tax_amount + + def get_total_deducted_tax(self): + self.add_column("Total Tax Deducted") + + ss = frappe.qb.DocType("Salary Slip") + ss_ded = frappe.qb.DocType("Salary Detail") + + records = ( + frappe.qb.from_(ss) + .inner_join(ss_ded) + .on(ss.name == ss_ded.parent) + .select(ss.employee, Sum(ss_ded.amount).as_("amount")) + .where(ss.docstatus == 1) + .where(ss.employee.isin(list(self.employees.keys()))) + .where(ss_ded.parentfield == "deductions") + .where(ss_ded.variable_based_on_taxable_salary == 1) + .where(ss.start_date >= self.payroll_period_start_date) + .where(ss.end_date <= self.payroll_period_end_date) + .groupby(ss.employee) + ).run(as_dict=True) + + for d in records: + self.employees[d.employee].setdefault("total_tax_deducted", d.amount) + + def get_payable_tax(self): + self.add_column("Payable Tax") + + for emp, emp_details in self.employees.items(): + emp_details["payable_tax"] = flt(emp_details.get("applicable_tax")) - flt( + emp_details.get("total_tax_deducted") + ) + + def add_column(self, label, fieldname=None, fieldtype=None, options=None, width=None): + col = { + "label": _(label), + "fieldname": fieldname or scrub(label), + "fieldtype": fieldtype or "Currency", + "options": options, + "width": width or "140px", + } + self.columns.append(col) + + def get_fixed_columns(self): + self.columns = [ + { + "label": _("Employee"), + "fieldname": "employee", + "fieldtype": "Link", + "options": "Employee", + "width": "140px", + }, + { + "label": _("Employee Name"), + "fieldname": "employee_name", + "fieldtype": "Data", + "width": "160px", + }, + { + "label": _("Department"), + "fieldname": "department", + "fieldtype": "Link", + "options": "Department", + "width": "140px", + }, + { + "label": _("Designation"), + "fieldname": "designation", + "fieldtype": "Link", + "options": "Designation", + "width": "140px", + }, + {"label": _("Date of Joining"), "fieldname": "date_of_joining", "fieldtype": "Date"}, + { + "label": _("Income Tax Slab"), + "fieldname": "income_tax_slab", + "fieldtype": "Link", + "options": "Income Tax Slab", + "width": "140px", + }, + {"label": _("CTC"), "fieldname": "ctc", "fieldtype": "Currency", "width": "140px"}, + ] diff --git a/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py b/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py new file mode 100644 index 0000000..7a1a6bf --- /dev/null +++ b/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py @@ -0,0 +1,116 @@ +import unittest + +import frappe +from frappe.utils import getdate + +from erpnext.setup.doctype.employee.test_employee import make_employee + +from hrms.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( + create_payroll_period, +) +from hrms.payroll.doctype.salary_slip.test_salary_slip import ( + create_exemption_declaration, + create_salary_slips_for_payroll_period, + create_tax_slab, +) +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure +from hrms.payroll.report.income_tax_computation.income_tax_computation import execute + + +class TestIncomeTaxComputation(unittest.TestCase): + def setUp(self): + self.cleanup_records() + self.create_records() + + def tearDown(self): + frappe.db.rollback() + + def cleanup_records(self): + frappe.db.sql("delete from `tabEmployee Tax Exemption Declaration`") + frappe.db.sql("delete from `tabPayroll Period`") + frappe.db.sql("delete from `tabIncome Tax Slab`") + frappe.db.sql("delete from `tabSalary Component`") + frappe.db.sql("delete from `tabEmployee Benefit Application`") + frappe.db.sql("delete from `tabEmployee Benefit Claim`") + frappe.db.sql("delete from `tabEmployee` where company='_Test Company'") + frappe.db.sql("delete from `tabSalary Slip`") + + def create_records(self): + self.employee = make_employee( + "employee_tax_computation@example.com", + company="_Test Company", + date_of_joining=getdate("01-10-2021"), + ) + + self.payroll_period = create_payroll_period( + name="_Test Payroll Period 1", company="_Test Company" + ) + + self.income_tax_slab = create_tax_slab( + self.payroll_period, + allow_tax_exemption=True, + effective_date=getdate("2019-04-01"), + company="_Test Company", + ) + salary_structure = make_salary_structure( + "Monthly Salary Structure Test Income Tax Computation", + "Monthly", + employee=self.employee, + company="_Test Company", + currency="INR", + payroll_period=self.payroll_period, + test_tax=True, + ) + + create_exemption_declaration(self.employee, self.payroll_period.name) + + create_salary_slips_for_payroll_period( + self.employee, salary_structure.name, self.payroll_period, deduct_random=False, num=3 + ) + + def test_report(self): + filters = frappe._dict( + { + "company": "_Test Company", + "payroll_period": self.payroll_period.name, + "employee": self.employee, + } + ) + + result = execute(filters) + + expected_data = { + "employee": self.employee, + "employee_name": "employee_tax_computation@example.com", + "department": "All Departments", + "income_tax_slab": self.income_tax_slab, + "ctc": 936000.0, + "professional_tax": 2400.0, + "standard_tax_exemption": 50000, + "total_exemption": 52400.0, + "total_taxable_amount": 883600.0, + "applicable_tax": 92789.0, + "total_tax_deducted": 17997.0, + "payable_tax": 74792, + } + + for key, val in expected_data.items(): + self.assertEqual(result[1][0].get(key), val) + + # Run report considering tax exemption declaration + filters.consider_tax_exemption_declaration = 1 + + result = execute(filters) + + expected_data.update( + { + "_test_category": 100000.0, + "total_exemption": 152400.0, + "total_taxable_amount": 783600.0, + "applicable_tax": 71989.0, + "payable_tax": 53992.0, + } + ) + + for key, val in expected_data.items(): + self.assertEqual(result[1][0].get(key), val) diff --git a/hrms/payroll/report/income_tax_deductions/__init__.py b/hrms/payroll/report/income_tax_deductions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/income_tax_deductions/income_tax_deductions.js b/hrms/payroll/report/income_tax_deductions/income_tax_deductions.js new file mode 100644 index 0000000..c3ed6ea --- /dev/null +++ b/hrms/payroll/report/income_tax_deductions/income_tax_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/hrms/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Income Tax Deductions"] = hrms.salary_slip_deductions_report_filters; +}); diff --git a/hrms/payroll/report/income_tax_deductions/income_tax_deductions.json b/hrms/payroll/report/income_tax_deductions/income_tax_deductions.json new file mode 100644 index 0000000..cf80398 --- /dev/null +++ b/hrms/payroll/report/income_tax_deductions/income_tax_deductions.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-30 00:07:56.744372", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-30 00:07:56.744372", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Income Tax Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Income Tax Deductions", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} \ No newline at end of file diff --git a/hrms/payroll/report/income_tax_deductions/income_tax_deductions.py b/hrms/payroll/report/income_tax_deductions/income_tax_deductions.py new file mode 100644 index 0000000..ccf1656 --- /dev/null +++ b/hrms/payroll/report/income_tax_deductions/income_tax_deductions.py @@ -0,0 +1,136 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + +import erpnext + + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) if len(data) else [] + + return columns, data + + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200, + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160, + }, + ] + + if erpnext.get_region() == "India": + columns.append( + {"label": _("PAN Number"), "fieldname": "pan_number", "fieldtype": "Data", "width": 140} + ) + + columns += [ + {"label": _("Income Tax Component"), "fieldname": "it_comp", "fieldtype": "Data", "width": 170}, + { + "label": _("Income Tax Amount"), + "fieldname": "it_amount", + "fieldtype": "Currency", + "options": "currency", + "width": 140, + }, + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140, + }, + {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 140}, + ] + + return columns + + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("sal.department = '%s' " % (filters["department"])) + + if filters.get("branch"): + conditions.append("sal.branch = '%s' " % (filters["branch"])) + + if filters.get("company"): + conditions.append("sal.company = '%s' " % (filters["company"])) + + if filters.get("month"): + conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) + + return " and ".join(conditions) + + +def get_data(filters): + + data = [] + + if erpnext.get_region() == "India": + employee_pan_dict = frappe._dict( + frappe.db.sql(""" select employee, pan_number from `tabEmployee`""") + ) + + component_types = frappe.db.sql( + """ select name from `tabSalary Component` + where is_income_tax_component = 1 """ + ) + + component_types = [comp_type[0] for comp_type in component_types] + + if not len(component_types): + return [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql( + """ select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ + % (conditions, ", ".join(["%s"] * len(component_types))), + tuple(component_types), + as_dict=1, + ) + + for d in entry: + + employee = { + "employee": d.employee, + "employee_name": d.employee_name, + "it_comp": d.salary_component, + "posting_date": d.posting_date, + # "pan_number": employee_pan_dict.get(d.employee), + "it_amount": d.amount, + "gross_pay": d.gross_pay, + } + + if erpnext.get_region() == "India": + employee["pan_number"] = employee_pan_dict.get(d.employee) + + data.append(employee) + + return data diff --git a/hrms/payroll/report/professional_tax_deductions/__init__.py b/hrms/payroll/report/professional_tax_deductions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.js b/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.js new file mode 100644 index 0000000..fa15a39 --- /dev/null +++ b/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/hrms/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Professional Tax Deductions"] = hrms.salary_slip_deductions_report_filters; +}); diff --git a/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.json b/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.json new file mode 100644 index 0000000..87b070c --- /dev/null +++ b/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.json @@ -0,0 +1,20 @@ +{ + "add_total_row": 0, + "creation": "2020-06-02 00:37:44.537355", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2022-06-26 19:02:26.306348", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Professional Tax Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Professional Tax Deductions", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.py b/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.py new file mode 100644 index 0000000..0565b28 --- /dev/null +++ b/hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.py @@ -0,0 +1,76 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + +from hrms.payroll.report.provident_fund_deductions.provident_fund_deductions import get_conditions + + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) if len(data) else [] + + return columns, data + + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200, + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160, + }, + {"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 140}, + ] + + return columns + + +def get_data(filters): + + data = [] + + component_type_dict = frappe._dict( + frappe.db.sql( + """ select name, component_type from `tabSalary Component` + where component_type = 'Professional Tax' """ + ) + ) + + if not len(component_type_dict): + return [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql( + """ select sal.employee, sal.employee_name, ded.salary_component, ded.amount + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ + % (conditions, ", ".join(["%s"] * len(component_type_dict))), + tuple(component_type_dict.keys()), + as_dict=1, + ) + + for d in entry: + + employee = {"employee": d.employee, "employee_name": d.employee_name, "amount": d.amount} + + data.append(employee) + + return data diff --git a/hrms/payroll/report/provident_fund_deductions/__init__.py b/hrms/payroll/report/provident_fund_deductions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.js b/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.js new file mode 100644 index 0000000..879da50 --- /dev/null +++ b/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/hrms/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Provident Fund Deductions"] = hrms.salary_slip_deductions_report_filters; +}); diff --git a/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.json b/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.json new file mode 100644 index 0000000..454ffbe --- /dev/null +++ b/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.json @@ -0,0 +1,20 @@ +{ + "add_total_row": 0, + "creation": "2020-06-01 23:44:07.919117", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2022-06-26 18:54:19.305763", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Provident Fund Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Provident Fund Deductions", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py b/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py new file mode 100644 index 0000000..ab4b6e7 --- /dev/null +++ b/hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py @@ -0,0 +1,174 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ +from frappe.utils import getdate + + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) if len(data) else [] + + return columns, data + + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200, + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160, + }, + {"label": _("PF Account"), "fieldname": "pf_account", "fieldtype": "Data", "width": 140}, + {"label": _("PF Amount"), "fieldname": "pf_amount", "fieldtype": "Currency", "width": 140}, + { + "label": _("Additional PF"), + "fieldname": "additional_pf", + "fieldtype": "Currency", + "width": 140, + }, + {"label": _("PF Loan"), "fieldname": "pf_loan", "fieldtype": "Currency", "width": 140}, + {"label": _("Total"), "fieldname": "total", "fieldtype": "Currency", "width": 140}, + ] + + return columns + + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("sal.department = '%s' " % (filters["department"])) + + if filters.get("branch"): + conditions.append("sal.branch = '%s' " % (filters["branch"])) + + if filters.get("company"): + conditions.append("sal.company = '%s' " % (filters["company"])) + + if filters.get("month"): + conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) + + if filters.get("mode_of_payment"): + conditions.append("sal.mode_of_payment = '%s' " % (filters["mode_of_payment"])) + + return " and ".join(conditions) + + +def prepare_data(entry, component_type_dict): + data_list = {} + + employee_account_dict = frappe._dict( + frappe.db.sql(""" select name, provident_fund_account from `tabEmployee`""") + ) + + for d in entry: + + component_type = component_type_dict.get(d.salary_component) + + if data_list.get(d.name): + data_list[d.name][component_type] = d.amount + else: + data_list.setdefault( + d.name, + { + "employee": d.employee, + "employee_name": d.employee_name, + "pf_account": employee_account_dict.get(d.employee), + component_type: d.amount, + }, + ) + + return data_list + + +def get_data(filters): + data = [] + + conditions = get_conditions(filters) + + salary_slips = frappe.db.sql( + """ select sal.name from `tabSalary Slip` sal + where docstatus = 1 %s + """ + % (conditions), + as_dict=1, + ) + + component_type_dict = frappe._dict( + frappe.db.sql( + """ select name, component_type from `tabSalary Component` + where component_type in ('Provident Fund', 'Additional Provident Fund', 'Provident Fund Loan')""" + ) + ) + + if not len(component_type_dict): + return [] + + entry = frappe.db.sql( + """ select sal.name, sal.employee, sal.employee_name, ded.salary_component, ded.amount + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ + % (conditions, ", ".join(["%s"] * len(component_type_dict))), + tuple(component_type_dict.keys()), + as_dict=1, + ) + + data_list = prepare_data(entry, component_type_dict) + + for d in salary_slips: + total = 0 + if data_list.get(d.name): + employee = { + "employee": data_list.get(d.name).get("employee"), + "employee_name": data_list.get(d.name).get("employee_name"), + "pf_account": data_list.get(d.name).get("pf_account"), + } + + if data_list.get(d.name).get("Provident Fund"): + employee["pf_amount"] = data_list.get(d.name).get("Provident Fund") + total += data_list.get(d.name).get("Provident Fund") + + if data_list.get(d.name).get("Additional Provident Fund"): + employee["additional_pf"] = data_list.get(d.name).get("Additional Provident Fund") + total += data_list.get(d.name).get("Additional Provident Fund") + + if data_list.get(d.name).get("Provident Fund Loan"): + employee["pf_loan"] = data_list.get(d.name).get("Provident Fund Loan") + total += data_list.get(d.name).get("Provident Fund Loan") + + employee["total"] = total + + data.append(employee) + + return data + + +@frappe.whitelist() +def get_years(): + year_list = frappe.db.sql_list( + """select distinct YEAR(end_date) from `tabSalary Slip` ORDER BY YEAR(end_date) DESC""" + ) + if not year_list: + year_list = [getdate().year] + + return "\n".join(str(year) for year in year_list) diff --git a/hrms/payroll/report/salary_payments_based_on_payment_mode/__init__.py b/hrms/payroll/report/salary_payments_based_on_payment_mode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js new file mode 100644 index 0000000..e82dba6 --- /dev/null +++ b/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/hrms/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Salary Payments Based On Payment Mode"] = hrms.salary_slip_deductions_report_filters; +}); diff --git a/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json b/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json new file mode 100644 index 0000000..c04cc32 --- /dev/null +++ b/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-06-16 18:43:43.107246", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:43:43.107246", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payments Based On Payment Mode", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Payments Based On Payment Mode", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} \ No newline at end of file diff --git a/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py new file mode 100644 index 0000000..8a00118 --- /dev/null +++ b/hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py @@ -0,0 +1,179 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + +import erpnext + +from hrms.payroll.report.provident_fund_deductions.provident_fund_deductions import get_conditions + + +def execute(filters=None): + mode_of_payments = get_payment_modes() + + if not len(mode_of_payments): + return [], [] + + columns = get_columns(filters, mode_of_payments) + data, total_rows, report_summary = get_data(filters, mode_of_payments) + chart = get_chart(mode_of_payments, total_rows) + + return columns, data, None, chart, report_summary + + +def get_columns(filters, mode_of_payments): + columns = [ + { + "label": _("Branch"), + "options": "Branch", + "fieldname": "branch", + "fieldtype": "Link", + "width": 200, + } + ] + + for mode in mode_of_payments: + columns.append({"label": _(mode), "fieldname": mode, "fieldtype": "Currency", "width": 160}) + + columns.append({"label": _("Total"), "fieldname": "total", "fieldtype": "Currency", "width": 140}) + + return columns + + +def get_payment_modes(): + mode_of_payments = frappe.db.sql_list( + """ + select distinct mode_of_payment from `tabSalary Slip` where docstatus = 1 + """ + ) + return mode_of_payments + + +def prepare_data(entry): + branch_wise_entries = {} + gross_pay = 0 + + for d in entry: + gross_pay += d.gross_pay + if branch_wise_entries.get(d.branch): + branch_wise_entries[d.branch][d.mode_of_payment] = d.net_pay + else: + branch_wise_entries.setdefault(d.branch, {}).setdefault(d.mode_of_payment, d.net_pay) + + return branch_wise_entries, gross_pay + + +def get_data(filters, mode_of_payments): + data = [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql( + """ + select branch, mode_of_payment, sum(net_pay) as net_pay, sum(gross_pay) as gross_pay + from `tabSalary Slip` sal + where docstatus = 1 %s + group by branch, mode_of_payment + """ + % (conditions), + as_dict=1, + ) + + branch_wise_entries, gross_pay = prepare_data(entry) + + branches = frappe.db.sql_list( + """ + select distinct branch from `tabSalary Slip` sal + where docstatus = 1 %s + """ + % (conditions) + ) + + total_row = {"total": 0, "branch": "Total"} + + for branch in branches: + total = 0 + row = {"branch": branch} + for mode in mode_of_payments: + if branch_wise_entries.get(branch).get(mode): + row[mode] = branch_wise_entries.get(branch).get(mode) + total += branch_wise_entries.get(branch).get(mode) + + row["total"] = total + data.append(row) + + total_row = get_total_based_on_mode_of_payment(data, mode_of_payments) + total_deductions = gross_pay - total_row.get("total") + + report_summary = [] + + if data: + data.append(total_row) + data.append({}) + data.append({"branch": "Total Gross Pay", mode_of_payments[0]: gross_pay}) + data.append({"branch": "Total Deductions", mode_of_payments[0]: total_deductions}) + data.append({"branch": "Total Net Pay", mode_of_payments[0]: total_row.get("total")}) + + currency = erpnext.get_company_currency(filters.company) + report_summary = get_report_summary( + gross_pay, total_deductions, total_row.get("total"), currency + ) + + return data, total_row, report_summary + + +def get_total_based_on_mode_of_payment(data, mode_of_payments): + + total = 0 + total_row = {"branch": "Total"} + for mode in mode_of_payments: + sum_of_payment = sum([detail[mode] for detail in data if mode in detail.keys()]) + total_row[mode] = sum_of_payment + total += sum_of_payment + + total_row["total"] = total + return total_row + + +def get_report_summary(gross_pay, total_deductions, net_pay, currency): + return [ + { + "value": gross_pay, + "label": _("Total Gross Pay"), + "indicator": "Green", + "datatype": "Currency", + "currency": currency, + }, + { + "value": total_deductions, + "label": _("Total Deduction"), + "datatype": "Currency", + "indicator": "Red", + "currency": currency, + }, + { + "value": net_pay, + "label": _("Total Net Pay"), + "datatype": "Currency", + "indicator": "Blue", + "currency": currency, + }, + ] + + +def get_chart(mode_of_payments, data): + if data: + values = [] + labels = [] + + for mode in mode_of_payments: + values.append(data[mode]) + labels.append([mode]) + + chart = { + "data": {"labels": labels, "datasets": [{"name": "Mode Of Payments", "values": values}]} + } + chart["type"] = "bar" + return chart diff --git a/hrms/payroll/report/salary_payments_via_ecs/__init__.py b/hrms/payroll/report/salary_payments_via_ecs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js new file mode 100644 index 0000000..75e87a4 --- /dev/null +++ b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/hrms/js/salary_slip_deductions_report_filters.js", function() { + + let ecs_checklist_filter = hrms.salary_slip_deductions_report_filters + ecs_checklist_filter['filters'].push({ + fieldname: "type", + label: __("Type"), + fieldtype: "Select", + options:["", "Bank", "Cash", "Cheque"] + }) + + frappe.query_reports["Salary Payments via ECS"] = ecs_checklist_filter +}); diff --git a/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json new file mode 100644 index 0000000..dd0ac7c --- /dev/null +++ b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2020-06-16 18:35:30.508143", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:38:23.680185", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payments via ECS", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Payments via ECS", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py new file mode 100644 index 0000000..4f9370b --- /dev/null +++ b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py @@ -0,0 +1,140 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + + +import frappe +from frappe import _ + +import erpnext + + +def execute(filters=None): + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + + +def get_columns(filters): + columns = [ + { + "label": _("Branch"), + "options": "Branch", + "fieldname": "branch", + "fieldtype": "Link", + "width": 200, + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160, + }, + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 140, + }, + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140, + }, + {"label": _("Bank"), "fieldname": "bank", "fieldtype": "Data", "width": 140}, + {"label": _("Account No"), "fieldname": "account_no", "fieldtype": "Data", "width": 140}, + ] + if erpnext.get_region() == "India": + columns += [ + {"label": _("IFSC"), "fieldname": "ifsc", "fieldtype": "Data", "width": 140}, + {"label": _("MICR"), "fieldname": "micr", "fieldtype": "Data", "width": 140}, + ] + + return columns + + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("department = '%s' " % (filters["department"])) + + if filters.get("branch"): + conditions.append("branch = '%s' " % (filters["branch"])) + + if filters.get("company"): + conditions.append("company = '%s' " % (filters["company"])) + + if filters.get("month"): + conditions.append("month(start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) + + return " and ".join(conditions) + + +def get_data(filters): + + data = [] + + fields = ["employee", "branch", "bank_name", "bank_ac_no", "salary_mode"] + if erpnext.get_region() == "India": + fields += ["ifsc_code", "micr_code"] + + employee_details = frappe.get_list("Employee", fields=fields) + employee_data_dict = {} + + for d in employee_details: + employee_data_dict.setdefault( + d.employee, + { + "bank_ac_no": d.bank_ac_no, + "ifsc_code": d.ifsc_code or None, + "micr_code": d.micr_code or None, + "branch": d.branch, + "salary_mode": d.salary_mode, + "bank_name": d.bank_name, + }, + ) + + conditions = get_conditions(filters) + + entry = frappe.db.sql( + """ select employee, employee_name, gross_pay + from `tabSalary Slip` + where docstatus = 1 %s """ + % (conditions), + as_dict=1, + ) + + for d in entry: + + employee = { + "branch": employee_data_dict.get(d.employee).get("branch"), + "employee_name": d.employee_name, + "employee": d.employee, + "gross_pay": d.gross_pay, + } + + if employee_data_dict.get(d.employee).get("salary_mode") == "Bank": + employee["bank"] = employee_data_dict.get(d.employee).get("bank_name") + employee["account_no"] = employee_data_dict.get(d.employee).get("bank_ac_no") + if erpnext.get_region() == "India": + employee["ifsc"] = employee_data_dict.get(d.employee).get("ifsc_code") + employee["micr"] = employee_data_dict.get(d.employee).get("micr_code") + else: + employee["account_no"] = employee_data_dict.get(d.employee).get("salary_mode") + + if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get( + "type" + ): + data.append(employee) + elif not filters.get("type"): + data.append(employee) + + return data diff --git a/hrms/payroll/report/salary_register/__init__.py b/hrms/payroll/report/salary_register/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/payroll/report/salary_register/salary_register.html b/hrms/payroll/report/salary_register/salary_register.html new file mode 100644 index 0000000..3abc3a0 --- /dev/null +++ b/hrms/payroll/report/salary_register/salary_register.html @@ -0,0 +1,40 @@ +{% + var report_columns = report.get_columns_for_print(); +%} +
+ {%= frappe.boot.letter_heads[filters.letter_head || frappe.defaults.get_default("letter_head")].header %} +
+

{%= __(report.report_name) %}

+
{{ __("From") }} {%= filters.from_date %} {{ __("to") }} {%= filters.to_date %}
+
+ + + + {% for(var i=1, l=report_columns.length; i{%= report_columns[i].label %} + {% } %} + + + + {% for(var j=0, k=data.length; j + {% for(var i=1, l=report_columns.length; i + {% var fieldname = report_columns[i].fieldname; %} + {% if (report_columns[i].fieldtype=='Currency' && !isNaN(row[fieldname])) { %} + {%= format_currency(row[fieldname]) %} + {% } else { %} + {% if (!is_null(row[fieldname])) { %} + {%= row[fieldname] %} + {% } %} + {% } %} + + {% } %} + + {% } %} + +
+

{{ __("Printed On") }} {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

diff --git a/hrms/payroll/report/salary_register/salary_register.js b/hrms/payroll/report/salary_register/salary_register.js new file mode 100644 index 0000000..eb4acb9 --- /dev/null +++ b/hrms/payroll/report/salary_register/salary_register.js @@ -0,0 +1,55 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Salary Register"] = { + "filters": [ + { + "fieldname":"from_date", + "label": __("From"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(),-1), + "reqd": 1, + "width": "100px" + }, + { + "fieldname":"to_date", + "label": __("To"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, + "width": "100px" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "options": "Currency", + "label": __("Currency"), + "default": erpnext.get_currency(frappe.defaults.get_default("Company")), + "width": "50px" + }, + { + "fieldname":"employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee", + "width": "100px" + }, + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "width": "100px", + "reqd": 1 + }, + { + "fieldname":"docstatus", + "label":__("Document Status"), + "fieldtype":"Select", + "options":["Draft", "Submitted", "Cancelled"], + "default": "Submitted", + "width": "100px" + } + ] +} diff --git a/hrms/payroll/report/salary_register/salary_register.json b/hrms/payroll/report/salary_register/salary_register.json new file mode 100644 index 0000000..5a70c32 --- /dev/null +++ b/hrms/payroll/report/salary_register/salary_register.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 1, + "creation": "2017-01-10 17:36:58.153863", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2020-05-28 00:07:18.576661", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Register", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Register", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/hrms/payroll/report/salary_register/salary_register.py b/hrms/payroll/report/salary_register/salary_register.py new file mode 100644 index 0000000..0a62b43 --- /dev/null +++ b/hrms/payroll/report/salary_register/salary_register.py @@ -0,0 +1,229 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe import _ +from frappe.utils import flt + +import erpnext + + +def execute(filters=None): + if not filters: + filters = {} + currency = None + if filters.get("currency"): + currency = filters.get("currency") + company_currency = erpnext.get_company_currency(filters.get("company")) + salary_slips = get_salary_slips(filters, company_currency) + if not salary_slips: + return [], [] + + columns, earning_types, ded_types = get_columns(salary_slips) + ss_earning_map = get_ss_earning_map(salary_slips, currency, company_currency) + ss_ded_map = get_ss_ded_map(salary_slips, currency, company_currency) + doj_map = get_employee_doj_map() + + data = [] + for ss in salary_slips: + row = [ + ss.name, + ss.employee, + ss.employee_name, + doj_map.get(ss.employee), + ss.branch, + ss.department, + ss.designation, + ss.company, + ss.start_date, + ss.end_date, + ss.leave_without_pay, + ss.payment_days, + ] + + if ss.branch is not None: + columns[3] = columns[3].replace("-1", "120") + if ss.department is not None: + columns[4] = columns[4].replace("-1", "120") + if ss.designation is not None: + columns[5] = columns[5].replace("-1", "120") + if ss.leave_without_pay is not None: + columns[9] = columns[9].replace("-1", "130") + + for e in earning_types: + row.append(ss_earning_map.get(ss.name, {}).get(e)) + + if currency == company_currency: + row += [flt(ss.gross_pay) * flt(ss.exchange_rate)] + else: + row += [ss.gross_pay] + + for d in ded_types: + row.append(ss_ded_map.get(ss.name, {}).get(d)) + + row.append(ss.total_loan_repayment) + + if currency == company_currency: + row += [ + flt(ss.total_deduction) * flt(ss.exchange_rate), + flt(ss.net_pay) * flt(ss.exchange_rate), + ] + else: + row += [ss.total_deduction, ss.net_pay] + row.append(currency or company_currency) + data.append(row) + + return columns, data + + +def get_columns(salary_slips): + """ + columns = [ + _("Salary Slip ID") + ":Link/Salary Slip:150", + _("Employee") + ":Link/Employee:120", + _("Employee Name") + "::140", + _("Date of Joining") + "::80", + _("Branch") + ":Link/Branch:120", + _("Department") + ":Link/Department:120", + _("Designation") + ":Link/Designation:120", + _("Company") + ":Link/Company:120", + _("Start Date") + "::80", + _("End Date") + "::80", + _("Leave Without Pay") + ":Float:130", + _("Payment Days") + ":Float:120", + _("Currency") + ":Link/Currency:80" + ] + """ + columns = [ + _("Salary Slip ID") + ":Link/Salary Slip:150", + _("Employee") + ":Link/Employee:120", + _("Employee Name") + "::140", + _("Date of Joining") + "::80", + _("Branch") + ":Link/Branch:-1", + _("Department") + ":Link/Department:-1", + _("Designation") + ":Link/Designation:120", + _("Company") + ":Link/Company:120", + _("Start Date") + "::80", + _("End Date") + "::80", + _("Leave Without Pay") + ":Float:50", + _("Payment Days") + ":Float:120", + ] + + salary_components = {_("Earning"): [], _("Deduction"): []} + + for component in frappe.db.sql( + """select distinct sd.salary_component, sc.type + from `tabSalary Detail` sd, `tabSalary Component` sc + where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)""" + % (", ".join(["%s"] * len(salary_slips))), + tuple([d.name for d in salary_slips]), + as_dict=1, + ): + salary_components[_(component.type)].append(component.salary_component) + + columns = ( + columns + + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + + [_("Gross Pay") + ":Currency:120"] + + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + + [ + _("Loan Repayment") + ":Currency:120", + _("Total Deduction") + ":Currency:120", + _("Net Pay") + ":Currency:120", + ] + ) + + return columns, salary_components[_("Earning")], salary_components[_("Deduction")] + + +def get_salary_slips(filters, company_currency): + filters.update({"from_date": filters.get("from_date"), "to_date": filters.get("to_date")}) + conditions, filters = get_conditions(filters, company_currency) + salary_slips = frappe.db.sql( + """select * from `tabSalary Slip` where %s + order by employee""" + % conditions, + filters, + as_dict=1, + ) + + return salary_slips or [] + + +def get_conditions(filters, company_currency): + conditions = "" + doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} + + if filters.get("docstatus"): + conditions += "docstatus = {0}".format(doc_status[filters.get("docstatus")]) + + if filters.get("from_date"): + conditions += " and start_date >= %(from_date)s" + if filters.get("to_date"): + conditions += " and end_date <= %(to_date)s" + if filters.get("company"): + conditions += " and company = %(company)s" + if filters.get("employee"): + conditions += " and employee = %(employee)s" + if filters.get("currency") and filters.get("currency") != company_currency: + conditions += " and currency = %(currency)s" + + return conditions, filters + + +def get_employee_doj_map(): + return frappe._dict( + frappe.db.sql( + """ + SELECT + employee, + date_of_joining + FROM `tabEmployee` + """ + ) + ) + + +def get_ss_earning_map(salary_slips, currency, company_currency): + ss_earnings = frappe.db.sql( + """select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name + from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" + % (", ".join(["%s"] * len(salary_slips))), + tuple([d.name for d in salary_slips]), + as_dict=1, + ) + + ss_earning_map = {} + for d in ss_earnings: + ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) + if currency == company_currency: + ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt( + d.exchange_rate if d.exchange_rate else 1 + ) + else: + ss_earning_map[d.parent][d.salary_component] += flt(d.amount) + + return ss_earning_map + + +def get_ss_ded_map(salary_slips, currency, company_currency): + ss_deductions = frappe.db.sql( + """select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name + from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" + % (", ".join(["%s"] * len(salary_slips))), + tuple([d.name for d in salary_slips]), + as_dict=1, + ) + + ss_ded_map = {} + for d in ss_deductions: + ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) + if currency == company_currency: + ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt( + d.exchange_rate if d.exchange_rate else 1 + ) + else: + ss_ded_map[d.parent][d.salary_component] += flt(d.amount) + + return ss_ded_map diff --git a/hrms/payroll/workspace/payroll/payroll.json b/hrms/payroll/workspace/payroll/payroll.json new file mode 100644 index 0000000..8d4f14a --- /dev/null +++ b/hrms/payroll/workspace/payroll/payroll.json @@ -0,0 +1,91 @@ +{ + "charts": [ + { + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" + } + ], + "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Payroll\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Outgoing Salary\",\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Salary Register\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Quick Links\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "creation": "2020-05-27 19:54:23.405607", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "money-coins-1", + "idx": 0, + "label": "Payroll", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "link_count": 1, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Settings", + "link_count": 0, + "link_to": "Payroll Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quick Links", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Entry", + "link_count": 0, + "link_to": "Payroll Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Slip", + "link_count": 0, + "link_to": "Salary Slip", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 12:11:30.520597", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 15.0, + "shortcuts": [ + { + "label": "Dashboard", + "link_to": "Payroll", + "type": "Dashboard" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Salary Register", + "link_to": "Salary Register", + "type": "Report" + } + ], + "title": "Payroll" +} \ No newline at end of file diff --git a/hrms/payroll/workspace/salary_payout/salary_payout.json b/hrms/payroll/workspace/salary_payout/salary_payout.json new file mode 100644 index 0000000..38f656d --- /dev/null +++ b/hrms/payroll/workspace/salary_payout/salary_payout.json @@ -0,0 +1,426 @@ +{ + "charts": [], + "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Salary Slip\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payroll Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Salary Register\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Transactions & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Masters\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Payroll\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Incentives\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Loans\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Payroll Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Deduction Reports\",\"col\":4}}]", + "creation": "2022-08-20 16:43:32.769568", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "income", + "idx": 0, + "label": "Salary Payout", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Masters", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Component", + "link_count": 0, + "link_to": "Salary Component", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure", + "link_count": 0, + "link_to": "Salary Structure", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Income Tax Slab", + "link_count": 0, + "link_to": "Income Tax Slab", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Period", + "link_count": 0, + "link_to": "Payroll Period", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Incentives", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Additional Salary", + "link_count": 0, + "link_to": "Additional Salary", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Incentive", + "link_count": 0, + "link_to": "Employee Incentive", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Retention Bonus", + "link_count": 0, + "link_to": "Retention Bonus", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loans", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Type", + "link_count": 0, + "link_to": "Loan Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Application", + "link_count": 0, + "link_to": "Loan Application", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "link_count": 0, + "link_to": "Loan", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Reports", + "link_count": 5, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Register", + "link_count": 0, + "link_to": "Salary Register", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Bank Remittance", + "link_count": 0, + "link_to": "Bank Remittance", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Payments Based On Payment Mode", + "link_count": 0, + "link_to": "Salary Payments Based On Payment Mode", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Payments via ECS", + "link_count": 0, + "link_to": "Salary Payments via ECS", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Income Tax Computation", + "link_count": 0, + "link_to": "Income Tax Computation", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Deduction Reports", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Provident Fund Deductions", + "link_count": 0, + "link_to": "Provident Fund Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Professional Tax Deductions", + "link_count": 0, + "link_to": "Professional Tax Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Income Tax Deductions", + "link_count": 0, + "link_to": "Income Tax Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting", + "link_count": 7, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Accounts", + "link_count": 0, + "link_to": "Account", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Cost Centers", + "link_count": 0, + "link_to": "Cost Center", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Entry", + "link_count": 0, + "link_to": "Payment Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Journal Entry", + "link_count": 0, + "link_to": "Journal Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Settings", + "link_count": 0, + "link_to": "Accounts Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Dimension", + "link_count": 0, + "link_to": "Accounting Dimension", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Currency", + "link_count": 0, + "link_to": "Currency", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Reports", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "General Ledger", + "link_count": 0, + "link_to": "General Ledger", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Payable", + "link_count": 0, + "link_to": "Accounts Payable", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Receivable", + "link_count": 0, + "link_to": "Accounts Receivable", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure Assignment", + "link_count": 0, + "link_to": "Salary Structure Assignment", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Slip", + "link_count": 0, + "link_to": "Salary Slip", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Entry", + "link_count": 0, + "link_to": "Payroll Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-21 12:27:38.631110", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payout", + "owner": "Administrator", + "parent_page": "Payroll", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 10.0, + "shortcuts": [ + { + "color": "Red", + "doc_view": "List", + "format": "{} Draft", + "label": "Salary Slip", + "link_to": "Salary Slip", + "stats_filter": "{\"status\":[\"=\",\"Draft\"]}", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Salary Register", + "link_to": "Salary Register", + "type": "Report" + }, + { + "color": "Red", + "doc_view": "List", + "format": "{} Draft", + "label": "Payroll Entry", + "link_to": "Payroll Entry", + "stats_filter": "{\"status\":[\"=\",\"Draft\"]}", + "type": "DocType" + } + ], + "title": "Salary Payout" +} \ No newline at end of file diff --git a/hrms/payroll/workspace/tax_&_benefits/tax_&_benefits.json b/hrms/payroll/workspace/tax_&_benefits/tax_&_benefits.json new file mode 100644 index 0000000..bc6ba5a --- /dev/null +++ b/hrms/payroll/workspace/tax_&_benefits/tax_&_benefits.json @@ -0,0 +1,172 @@ +{ + "charts": [], + "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee Tax Exemption Declaration\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Additional Salary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Income Tax Computation\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tax Setup\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Exemption\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Benefits\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "creation": "2022-08-20 17:04:52.350699", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "crm", + "idx": 0, + "label": "Tax & Benefits", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Benefits", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Benefit Application", + "link_count": 0, + "link_to": "Employee Benefit Application", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Benefit Claim", + "link_count": 0, + "link_to": "Employee Benefit Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tax Setup", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Income Tax Slab", + "link_count": 0, + "link_to": "Income Tax Slab", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Declaration Category", + "link_count": 0, + "link_to": "Employee Tax Exemption Declaration Category", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Sub Category", + "link_count": 0, + "link_to": "Employee Tax Exemption Sub Category", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exemption", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Declaration", + "link_count": 0, + "link_to": "Employee Tax Exemption Declaration", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Proof Submission", + "link_count": 0, + "link_to": "Employee Tax Exemption Proof Submission", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 2, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Income Tax Computation", + "link_count": 0, + "link_to": "Income Tax Computation", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Income Tax Deductions", + "link_count": 0, + "link_to": "Income Tax Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2022-09-16 11:48:54.314522", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Tax & Benefits", + "owner": "Administrator", + "parent_page": "Payroll", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 38.0, + "shortcuts": [ + { + "color": "Red", + "doc_view": "List", + "format": "{} Draft", + "label": "Employee Tax Exemption Declaration", + "link_to": "Employee Tax Exemption Declaration", + "stats_filter": "{\"docstatus\":[\"=\",\"0\"]}", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Additional Salary", + "link_to": "Additional Salary", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Income Tax Computation", + "link_to": "Income Tax Computation", + "type": "Report" + } + ], + "title": "Tax & Benefits" +} \ No newline at end of file diff --git a/hrms/public/.gitkeep b/hrms/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/hrms/public/js/bank_transaction.js b/hrms/public/js/bank_transaction.js new file mode 100644 index 0000000..5dad99d --- /dev/null +++ b/hrms/public/js/bank_transaction.js @@ -0,0 +1,14 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Bank Transaction", { + get_payment_doctypes: function() { + return [ + "Payment Entry", + "Journal Entry", + "Sales Invoice", + "Purchase Invoice", + "Expense Claim", + ]; + } +}) \ No newline at end of file diff --git a/hrms/public/js/company.js b/hrms/public/js/company.js new file mode 100644 index 0000000..6efbe97 --- /dev/null +++ b/hrms/public/js/company.js @@ -0,0 +1,41 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Company", { + onload: function(frm) { + frm.set_query("default_expense_claim_payable_account", function() { + return { + filters: { + "company": frm.doc.name, + "is_group": 0, + } + }; + }); + + frm.set_query("default_employee_advance_account", function() { + return { + filters: { + "company": frm.doc.name, + "is_group": 0, + "root_type": "Asset", + } + }; + }); + + frm.set_query("default_payroll_payable_account", function() { + return { + filters: { + "company": frm.doc.name, + "is_group": 0, + "root_type": "Liability", + } + }; + }); + + frm.set_query("hra_component", function() { + return { + filters: {"type": "Earning"} + } + }); + } +}) \ No newline at end of file diff --git a/hrms/public/js/delivery_trip.js b/hrms/public/js/delivery_trip.js new file mode 100644 index 0000000..276a7e3 --- /dev/null +++ b/hrms/public/js/delivery_trip.js @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Delivery Trip", { + refresh: function(frm) { + if (frm.doc.docstatus == 1 && frm.doc.employee) { + frm.add_custom_button(__("Expense Claim"), function() { + frappe.model.open_mapped_doc({ + method: "hrms.hr.doctype.expense_claim.expense_claim.make_expense_claim_for_delivery_trip", + frm: frm, + }); + }, __("Create")); + } + } +}) \ No newline at end of file diff --git a/hrms/public/js/department.js b/hrms/public/js/department.js new file mode 100644 index 0000000..9a1c43c --- /dev/null +++ b/hrms/public/js/department.js @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Department", { + onload: function(frm) { + frm.set_query("payroll_cost_center", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + } +}) \ No newline at end of file diff --git a/hrms/public/js/employee.js b/hrms/public/js/employee.js new file mode 100644 index 0000000..ba09af5 --- /dev/null +++ b/hrms/public/js/employee.js @@ -0,0 +1,35 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Employee", { + setup: function (frm) { + frm.set_query("leave_policy", function() { + return { + filters: { + "docstatus": 1 + } + }; + }); + + frm.set_query("payroll_cost_center", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + }, + + date_of_birth(frm) { + frm.call({ + method: "hrms.overrides.employee_master.get_retirement_date", + args: { + date_of_birth: frm.doc.date_of_birth + } + }).then((r) => { + if (r && r.message) + frm.set_value("date_of_retirement", r.message); + }); + } +}) \ No newline at end of file diff --git a/hrms/public/js/hrms.bundle.js b/hrms/public/js/hrms.bundle.js new file mode 100644 index 0000000..80a9c0a --- /dev/null +++ b/hrms/public/js/hrms.bundle.js @@ -0,0 +1 @@ +import "./templates/employees_to_mark_attendance.html"; diff --git a/hrms/public/js/journal_entry.js b/hrms/public/js/journal_entry.js new file mode 100644 index 0000000..83ef5df --- /dev/null +++ b/hrms/public/js/journal_entry.js @@ -0,0 +1,34 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Journal Entry", { + setup(frm) { + frm.set_query("reference_name", "accounts", function(frm, cdt, cdn) { + let jvd = frappe.get_doc(cdt, cdn); + + if (jvd.reference_type === "Expense Claim") { + return { + filters: { + "total_sanctioned_amount": [">", 0], + "status": ["!=", "Paid"], + "docstatus": 1 + } + }; + } + + if (jvd.reference_type === "Employee Advance") { + return { + filters: { + "docstatus": 1 + } + }; + } + + if (jvd.reference_type === "Payroll Entry") { + return { + query: "hrms.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", + }; + } + }) + } +}) \ No newline at end of file diff --git a/hrms/public/js/payment_entry.js b/hrms/public/js/payment_entry.js new file mode 100644 index 0000000..7b53f3c --- /dev/null +++ b/hrms/public/js/payment_entry.js @@ -0,0 +1,84 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Payment Entry", { + setup: function(frm) { + frm.set_query("reference_doctype", "references", function() { + let doctypes = []; + + if (frm.doc.party_type == "Customer") { + doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"]; + } else if (frm.doc.party_type == "Supplier") { + doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"]; + } else if (frm.doc.party_type == "Employee") { + doctypes = ["Expense Claim", "Journal Entry"]; + } else { + doctypes = ["Journal Entry"]; + } + + return { + filters: { "name": ["in", doctypes] } + }; + }); + + frm.set_query("reference_name", "references", function(doc, cdt, cdn) { + const child = locals[cdt][cdn]; + const filters = {"docstatus": 1, "company": doc.company}; + const party_type_doctypes = ["Sales Invoice", "Sales Order", "Purchase Invoice", + "Purchase Order", "Expense Claim", "Dunning"]; + + if (in_list(party_type_doctypes, child.reference_doctype)) { + filters[doc.party_type.toLowerCase()] = doc.party; + } + + if (child.reference_doctype == "Expense Claim") { + filters["docstatus"] = 1; + filters["is_paid"] = 0; + } + + return { + filters: filters + }; + }); + }, + + get_order_doctypes: function(frm) { + return ["Sales Order", "Purchase Order", "Expense Claim"]; + }, + + get_invoice_doctypes: function(frm) { + return ['Sales Invoice', 'Purchase Invoice', "Expense Claim"]; + }, +}) + + +frappe.ui.form.on("Payment Entry Reference", { + reference_name: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.reference_name && row.reference_doctype) { + return frappe.call({ + method: "hrms.overrides.employee_payment_entry.get_payment_reference_details", + args: { + reference_doctype: row.reference_doctype, + reference_name: row.reference_name, + party_account_currency: (frm.doc.payment_type == "Receive") ? + frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency + }, + callback: function(r, rt) { + if (r.message) { + $.each(r.message, function(field, value) { + frappe.model.set_value(cdt, cdn, field, value); + }) + + let allocated_amount = frm.doc.unallocated_amount > row.outstanding_amount ? + row.outstanding_amount : frm.doc.unallocated_amount; + + frappe.model.set_value(cdt, cdn, "allocated_amount", allocated_amount); + frm.refresh_fields(); + } + } + }) + } + }, +}) \ No newline at end of file diff --git a/hrms/public/js/salary_slip_deductions_report_filters.js b/hrms/public/js/salary_slip_deductions_report_filters.js new file mode 100644 index 0000000..0dc1810 --- /dev/null +++ b/hrms/public/js/salary_slip_deductions_report_filters.js @@ -0,0 +1,65 @@ +frappe.provide("hrms.salary_slip_deductions_report_filters"); + +hrms.salary_slip_deductions_report_filters = { + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + }, + { + fieldname: "month", + label: __("Month"), + fieldtype: "Select", + reqd: 1, + options: [ + { "value": 1, "label": __("Jan") }, + { "value": 2, "label": __("Feb") }, + { "value": 3, "label": __("Mar") }, + { "value": 4, "label": __("Apr") }, + { "value": 5, "label": __("May") }, + { "value": 6, "label": __("June") }, + { "value": 7, "label": __("July") }, + { "value": 8, "label": __("Aug") }, + { "value": 9, "label": __("Sep") }, + { "value": 10, "label": __("Oct") }, + { "value": 11, "label": __("Nov") }, + { "value": 12, "label": __("Dec") }, + ], + default: frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1 + }, + { + fieldname:"year", + label: __("Year"), + fieldtype: "Select", + reqd: 1 + }, + { + fieldname: "department", + label: __("Department"), + fieldtype: "Link", + options: "Department", + }, + { + fieldname: "branch", + label: __("Branch"), + fieldtype: "Link", + options: "Branch", + } + ], + onload: function() { + return frappe.call({ + method: "hrms.payroll.report.provident_fund_deductions.provident_fund_deductions.get_years", + callback: function(r) { + var year_filter = frappe.query_report.get_filter('year'); + year_filter.df.options = r.message; + year_filter.df.default = r.message.split("\n")[0]; + year_filter.refresh(); + year_filter.set_input(year_filter.df.default); + } + }); + } +} diff --git a/hrms/public/js/templates/employees_to_mark_attendance.html b/hrms/public/js/templates/employees_to_mark_attendance.html new file mode 100644 index 0000000..167c775 --- /dev/null +++ b/hrms/public/js/templates/employees_to_mark_attendance.html @@ -0,0 +1,14 @@ +{% if data %} +
+
+ Employees to mark attendance +
+ {% for item in data %} +
+ {{ item.employee }}    {{ item.employee_name }} +
+ {% } %} +
+{% } else { %} +
+{% } %} diff --git a/hrms/public/js/timesheet.js b/hrms/public/js/timesheet.js new file mode 100644 index 0000000..c92aff4 --- /dev/null +++ b/hrms/public/js/timesheet.js @@ -0,0 +1,21 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Timesheet", { + refresh(frm) { + if (frm.doc.docstatus === 1 && frappe.model.can_create("Salary Slip")) { + if (!frm.doc.salary_slip && frm.doc.employee) { + frm.add_custom_button(__("Create Salary Slip"), function() { + frm.trigger("make_salary_slip"); + }); + } + } + }, + + make_salary_slip: function(frm) { + frappe.model.open_mapped_doc({ + method: "hrms.payroll.doctype.salary_slip.salary_slip.make_salary_slip_from_timesheet", + frm: frm + }); + }, +}); diff --git a/hrms/regional/india/data/salary_components.json b/hrms/regional/india/data/salary_components.json new file mode 100644 index 0000000..3605df2 --- /dev/null +++ b/hrms/regional/india/data/salary_components.json @@ -0,0 +1,44 @@ +[ + { + "doctype": "Salary Component", + "salary_component": "Professional Tax", + "description": "Professional Tax", + "type": "Deduction", + "exempted_from_income_tax": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "Provident Fund", + "description": "Provident fund", + "type": "Deduction", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "House Rent Allowance", + "description": "House Rent Allowance", + "type": "Earning", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "Basic", + "description": "Basic", + "type": "Earning", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "Arrear", + "description": "Arrear", + "type": "Earning", + "is_tax_applicable": 1 + }, + { + "doctype": "Salary Component", + "salary_component": "Leave Encashment", + "description": "Leave Encashment", + "type": "Earning", + "is_tax_applicable": 1 + } +] \ No newline at end of file diff --git a/hrms/regional/india/setup.py b/hrms/regional/india/setup.py new file mode 100644 index 0000000..8f53e00 --- /dev/null +++ b/hrms/regional/india/setup.py @@ -0,0 +1,269 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + + +def setup(): + make_custom_fields() + add_custom_roles_for_reports() + create_gratuity_rule_for_india() + + +def make_custom_fields(update=True): + custom_fields = get_custom_fields() + create_custom_fields(custom_fields, update=update) + + +def get_custom_fields(): + return { + "Salary Component": [ + { + "fieldname": "component_type", + "label": "Component Type", + "fieldtype": "Select", + "insert_after": "description", + "options": ( + "\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax" + ), + "depends_on": 'eval:doc.type == "Deduction"', + "translatable": 0, + }, + ], + "Employee": [ + { + "fieldname": "bank_cb", + "fieldtype": "Column Break", + "insert_after": "bank_ac_no", + }, + { + "fieldname": "ifsc_code", + "label": "IFSC Code", + "fieldtype": "Data", + "insert_after": "bank_cb", + "print_hide": 1, + "depends_on": 'eval:doc.salary_mode == "Bank"', + "translatable": 0, + }, + { + "fieldname": "pan_number", + "label": "PAN Number", + "fieldtype": "Data", + "insert_after": "payroll_cost_center", + "print_hide": 1, + "translatable": 0, + }, + { + "fieldname": "micr_code", + "label": "MICR Code", + "fieldtype": "Data", + "insert_after": "ifsc_code", + "print_hide": 1, + "depends_on": 'eval:doc.salary_mode == "Bank"', + "translatable": 0, + }, + { + "fieldname": "provident_fund_account", + "label": "Provident Fund Account", + "fieldtype": "Data", + "insert_after": "pan_number", + "translatable": 0, + }, + ], + "Company": [ + { + "fieldname": "hra_section", + "label": "HRA Settings", + "fieldtype": "Section Break", + "insert_after": "asset_received_but_not_billed", + "collapsible": 1, + }, + { + "fieldname": "basic_component", + "label": "Basic Component", + "fieldtype": "Link", + "options": "Salary Component", + "insert_after": "hra_section", + }, + { + "fieldname": "hra_component", + "label": "HRA Component", + "fieldtype": "Link", + "options": "Salary Component", + "insert_after": "basic_component", + }, + { + "fieldname": "hra_column_break", + "fieldtype": "Column Break", + "insert_after": "hra_component", + }, + { + "fieldname": "arrear_component", + "label": "Arrear Component", + "fieldtype": "Link", + "options": "Salary Component", + "insert_after": "hra_column_break", + }, + ], + "Employee Tax Exemption Declaration": [ + { + "fieldname": "hra_section", + "label": "HRA Exemption", + "fieldtype": "Section Break", + "insert_after": "declarations", + }, + { + "fieldname": "monthly_house_rent", + "label": "Monthly House Rent", + "fieldtype": "Currency", + "insert_after": "hra_section", + }, + { + "fieldname": "rented_in_metro_city", + "label": "Rented in Metro City", + "fieldtype": "Check", + "insert_after": "monthly_house_rent", + "depends_on": "monthly_house_rent", + }, + { + "fieldname": "salary_structure_hra", + "label": "HRA as per Salary Structure", + "fieldtype": "Currency", + "insert_after": "rented_in_metro_city", + "read_only": 1, + "depends_on": "monthly_house_rent", + }, + { + "fieldname": "hra_column_break", + "fieldtype": "Column Break", + "insert_after": "salary_structure_hra", + "depends_on": "monthly_house_rent", + }, + { + "fieldname": "annual_hra_exemption", + "label": "Annual HRA Exemption", + "fieldtype": "Currency", + "insert_after": "hra_column_break", + "read_only": 1, + "depends_on": "monthly_house_rent", + }, + { + "fieldname": "monthly_hra_exemption", + "label": "Monthly HRA Exemption", + "fieldtype": "Currency", + "insert_after": "annual_hra_exemption", + "read_only": 1, + "depends_on": "monthly_house_rent", + }, + ], + "Employee Tax Exemption Proof Submission": [ + { + "fieldname": "hra_section", + "label": "HRA Exemption", + "fieldtype": "Section Break", + "insert_after": "tax_exemption_proofs", + }, + { + "fieldname": "house_rent_payment_amount", + "label": "House Rent Payment Amount", + "fieldtype": "Currency", + "insert_after": "hra_section", + }, + { + "fieldname": "rented_in_metro_city", + "label": "Rented in Metro City", + "fieldtype": "Check", + "insert_after": "house_rent_payment_amount", + "depends_on": "house_rent_payment_amount", + }, + { + "fieldname": "rented_from_date", + "label": "Rented From Date", + "fieldtype": "Date", + "insert_after": "rented_in_metro_city", + "depends_on": "house_rent_payment_amount", + }, + { + "fieldname": "rented_to_date", + "label": "Rented To Date", + "fieldtype": "Date", + "insert_after": "rented_from_date", + "depends_on": "house_rent_payment_amount", + }, + { + "fieldname": "hra_column_break", + "fieldtype": "Column Break", + "insert_after": "rented_to_date", + "depends_on": "house_rent_payment_amount", + }, + { + "fieldname": "monthly_house_rent", + "label": "Monthly House Rent", + "fieldtype": "Currency", + "insert_after": "hra_column_break", + "read_only": 1, + "depends_on": "house_rent_payment_amount", + }, + { + "fieldname": "monthly_hra_exemption", + "label": "Monthly Eligible Amount", + "fieldtype": "Currency", + "insert_after": "monthly_house_rent", + "read_only": 1, + "depends_on": "house_rent_payment_amount", + }, + { + "fieldname": "total_eligible_hra_exemption", + "label": "Total Eligible HRA Exemption", + "fieldtype": "Currency", + "insert_after": "monthly_hra_exemption", + "read_only": 1, + "depends_on": "house_rent_payment_amount", + }, + ], + } + + +def add_custom_roles_for_reports(): + for report_name in ( + "Professional Tax Deductions", + "Provident Fund Deductions", + "Income Tax Deductions", + ): + if not frappe.db.get_value("Custom Role", dict(report=report_name)): + frappe.get_doc( + dict( + doctype="Custom Role", + report=report_name, + roles=[dict(role="HR User"), dict(role="HR Manager"), dict(role="Employee")], + ) + ).insert() + + +def create_gratuity_rule_for_india(): + if not frappe.db.exists("DocType", "Gratuity Rule"): + return + + if frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"): + return + + rule = frappe.new_doc("Gratuity Rule") + rule.update( + { + "name": "Indian Standard Gratuity Rule", + "calculate_gratuity_amount_based_on": "Current Slab", + "work_experience_calculation_method": "Round Off Work Experience", + "minimum_year_for_gratuity": 5, + "gratuity_rule_slabs": [ + { + "from_year": 0, + "to_year": 0, + "fraction_of_applicable_earnings": 15 / 26, + } + ], + } + ) + rule.flags.ignore_mandatory = True + rule.save() diff --git a/hrms/regional/india/utils.py b/hrms/regional/india/utils.py new file mode 100644 index 0000000..c666c90 --- /dev/null +++ b/hrms/regional/india/utils.py @@ -0,0 +1,203 @@ +import math + +import frappe +from frappe import _ +from frappe.utils import add_days, date_diff, flt, get_link_to_form, month_diff + +from hrms.hr.utils import get_salary_assignments +from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip + + +def calculate_annual_eligible_hra_exemption(doc): + basic_component, hra_component = frappe.db.get_value( + "Company", doc.company, ["basic_component", "hra_component"] + ) + + if not (basic_component and hra_component): + frappe.throw( + _("Please set Basic and HRA component in Company {0}").format( + get_link_to_form("Company", doc.company) + ) + ) + + annual_exemption = monthly_exemption = hra_amount = basic_amount = 0 + + if hra_component and basic_component: + assignments = get_salary_assignments(doc.employee, doc.payroll_period) + + if not assignments and doc.docstatus == 1: + frappe.throw( + _("Salary Structure must be submitted before submission of {0}").format(doc.doctype) + ) + + assignment_dates = [assignment.from_date for assignment in assignments] + + for idx, assignment in enumerate(assignments): + if has_hra_component(assignment.salary_structure, hra_component): + basic_salary_amt, hra_salary_amt = get_component_amt_from_salary_slip( + doc.employee, + assignment.salary_structure, + basic_component, + hra_component, + assignment.from_date, + ) + to_date = get_end_date_for_assignment(assignment_dates, idx, doc.payroll_period) + + frequency = frappe.get_value( + "Salary Structure", assignment.salary_structure, "payroll_frequency" + ) + basic_amount += get_component_pay(frequency, basic_salary_amt, assignment.from_date, to_date) + hra_amount += get_component_pay(frequency, hra_salary_amt, assignment.from_date, to_date) + + if hra_amount: + if doc.monthly_house_rent: + annual_exemption = calculate_hra_exemption( + assignment.salary_structure, + basic_amount, + hra_amount, + doc.monthly_house_rent, + doc.rented_in_metro_city, + ) + if annual_exemption > 0: + monthly_exemption = annual_exemption / 12 + else: + annual_exemption = 0 + + return frappe._dict( + { + "hra_amount": hra_amount, + "annual_exemption": annual_exemption, + "monthly_exemption": monthly_exemption, + } + ) + + +def has_hra_component(salary_structure, hra_component): + return frappe.db.exists( + "Salary Detail", + { + "parent": salary_structure, + "salary_component": hra_component, + "parentfield": "earnings", + "parenttype": "Salary Structure", + }, + ) + + +def get_end_date_for_assignment(assignment_dates, idx, payroll_period): + end_date = None + + try: + end_date = assignment_dates[idx + 1] + end_date = add_days(end_date, -1) + except IndexError: + pass + + if not end_date: + end_date = frappe.db.get_value("Payroll Period", payroll_period, "end_date") + + return end_date + + +def get_component_amt_from_salary_slip( + employee, salary_structure, basic_component, hra_component, from_date +): + salary_slip = make_salary_slip( + salary_structure, + employee=employee, + for_preview=1, + ignore_permissions=True, + posting_date=from_date, + ) + + basic_amt, hra_amt = 0, 0 + for earning in salary_slip.earnings: + if earning.salary_component == basic_component: + basic_amt = earning.amount + elif earning.salary_component == hra_component: + hra_amt = earning.amount + if basic_amt and hra_amt: + return basic_amt, hra_amt + return basic_amt, hra_amt + + +def calculate_hra_exemption( + salary_structure, annual_basic, annual_hra, monthly_house_rent, rented_in_metro_city +): + # TODO make this configurable + exemptions = [] + # case 1: The actual amount allotted by the employer as the HRA. + exemptions.append(annual_hra) + + # case 2: Actual rent paid less 10% of the basic salary. + actual_annual_rent = monthly_house_rent * 12 + exemptions.append(flt(actual_annual_rent) - flt(annual_basic * 0.1)) + + # case 3: 50% of the basic salary, if the employee is staying in a metro city (40% for a non-metro city). + exemptions.append(annual_basic * 0.5 if rented_in_metro_city else annual_basic * 0.4) + + # return minimum of 3 cases + return min(exemptions) + + +def get_component_pay(frequency, amount, from_date, to_date): + days = date_diff(to_date, from_date) + 1 + + if frequency == "Daily": + return amount * days + elif frequency == "Weekly": + return amount * math.floor(days / 7) + elif frequency == "Fortnightly": + return amount * math.floor(days / 14) + elif frequency == "Monthly": + return amount * month_diff(to_date, from_date) + elif frequency == "Bimonthly": + return amount * (month_diff(to_date, from_date) / 2) + + +def validate_house_rent_dates(doc): + if not doc.rented_to_date or not doc.rented_from_date: + frappe.throw(_("House rented dates required for exemption calculation")) + + if date_diff(doc.rented_to_date, doc.rented_from_date) < 14: + frappe.throw(_("House rented dates should be atleast 15 days apart")) + + proofs = frappe.db.sql( + """ + select name + from `tabEmployee Tax Exemption Proof Submission` + where + docstatus=1 and employee=%(employee)s and payroll_period=%(payroll_period)s + and (rented_from_date between %(from_date)s and %(to_date)s or rented_to_date between %(from_date)s and %(to_date)s) + """, + { + "employee": doc.employee, + "payroll_period": doc.payroll_period, + "from_date": doc.rented_from_date, + "to_date": doc.rented_to_date, + }, + ) + + if proofs: + frappe.throw(_("House rent paid days overlapping with {0}").format(proofs[0][0])) + + +def calculate_hra_exemption_for_period(doc): + monthly_rent, eligible_hra = 0, 0 + if doc.house_rent_payment_amount: + validate_house_rent_dates(doc) + # TODO receive rented months or validate dates are start and end of months? + # Calc monthly rent, round to nearest .5 + factor = flt(date_diff(doc.rented_to_date, doc.rented_from_date) + 1) / 30 + factor = round(factor * 2) / 2 + monthly_rent = doc.house_rent_payment_amount / factor + # update field used by calculate_annual_eligible_hra_exemption + doc.monthly_house_rent = monthly_rent + exemptions = calculate_annual_eligible_hra_exemption(doc) + + if exemptions["monthly_exemption"]: + # calc total exemption amount + eligible_hra = exemptions["monthly_exemption"] * factor + exemptions["monthly_house_rent"] = monthly_rent + exemptions["total_eligible_hra_exemption"] = eligible_hra + return exemptions diff --git a/hrms/regional/united_arab_emirates/setup.py b/hrms/regional/united_arab_emirates/setup.py new file mode 100644 index 0000000..50e804c --- /dev/null +++ b/hrms/regional/united_arab_emirates/setup.py @@ -0,0 +1,59 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + + +def setup(): + create_gratuity_rules_for_uae() + + +def create_gratuity_rules_for_uae(): + docs = get_gratuity_rules() + for d in docs: + doc = frappe.get_doc(d) + doc.flags.ignore_mandatory = True + doc.flags.ignore_permissions = True + doc.insert(ignore_if_duplicate=True) + + +def get_gratuity_rules(): + return [ + { + "doctype": "Gratuity Rule", + "name": "Rule Under Limited Contract (UAE)", + "calculate_gratuity_amount_based_on": "Sum of all previous slabs", + "work_experience_calculation_method": "Take Exact Completed Years", + "minimum_year_for_gratuity": 1, + "gratuity_rule_slabs": [ + {"from_year": 0, "to_year": 1, "fraction_of_applicable_earnings": 0}, + {"from_year": 1, "to_year": 5, "fraction_of_applicable_earnings": 21 / 30}, + {"from_year": 5, "to_year": 0, "fraction_of_applicable_earnings": 1}, + ], + }, + { + "doctype": "Gratuity Rule", + "name": "Rule Under Unlimited Contract on termination (UAE)", + "calculate_gratuity_amount_based_on": "Current Slab", + "work_experience_calculation_method": "Take Exact Completed Years", + "minimum_year_for_gratuity": 1, + "gratuity_rule_slabs": [ + {"from_year": 0, "to_year": 1, "fraction_of_applicable_earnings": 0}, + {"from_year": 1, "to_year": 5, "fraction_of_applicable_earnings": 21 / 30}, + {"from_year": 5, "to_year": 0, "fraction_of_applicable_earnings": 1}, + ], + }, + { + "doctype": "Gratuity Rule", + "name": "Rule Under Unlimited Contract on resignation (UAE)", + "calculate_gratuity_amount_based_on": "Current Slab", + "work_experience_calculation_method": "Take Exact Completed Years", + "minimum_year_for_gratuity": 1, + "gratuity_rule_slabs": [ + {"from_year": 0, "to_year": 1, "fraction_of_applicable_earnings": 0}, + {"from_year": 1, "to_year": 3, "fraction_of_applicable_earnings": 1 / 3 * 21 / 30}, + {"from_year": 3, "to_year": 5, "fraction_of_applicable_earnings": 2 / 3 * 21 / 30}, + {"from_year": 5, "to_year": 0, "fraction_of_applicable_earnings": 21 / 30}, + ], + }, + ] diff --git a/hrms/setup.py b/hrms/setup.py new file mode 100644 index 0000000..030219f --- /dev/null +++ b/hrms/setup.py @@ -0,0 +1,659 @@ +import frappe +import os +import click +from frappe import _ + +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.desk.page.setup_wizard.setup_wizard import make_records +from frappe.installer import update_site_config + +from hrms.subscription_utils import update_erpnext_access + + +def after_install(): + create_custom_fields(get_custom_fields()) + make_fixtures() + setup_notifications() + update_hr_defaults() + add_non_standard_user_types() + set_single_defaults() + update_erpnext_access() + frappe.db.commit() + run_post_install_patches() + click.secho("Thank you for installing Frappe HR!", fg="green") + + +def get_custom_fields(): + """HR specific custom fields that need to be added to the masters in ERPNext""" + return { + "Employee": [ + { + "fieldname": "employment_type", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Employment Type", + "oldfieldname": "employment_type", + "oldfieldtype": "Link", + "options": "Employment Type", + "insert_after": "department", + }, + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "label": "Job Applicant", + "options": "Job Applicant", + "insert_after": "employment_details", + }, + { + "fieldname": "grade", + "fieldtype": "Link", + "label": "Grade", + "options": "Employee Grade", + "insert_after": "branch", + }, + { + "fieldname": "default_shift", + "fieldtype": "Link", + "label": "Default Shift", + "options": "Shift Type", + "insert_after": "holiday_list", + }, + { + "collapsible": 1, + "fieldname": "health_insurance_section", + "fieldtype": "Section Break", + "label": "Health Insurance", + "insert_after": "health_details", + }, + { + "fieldname": "health_insurance_provider", + "fieldtype": "Link", + "label": "Health Insurance Provider", + "options": "Employee Health Insurance", + "insert_after": "health_insurance_section", + }, + { + "depends_on": "eval:doc.health_insurance_provider", + "fieldname": "health_insurance_no", + "fieldtype": "Data", + "label": "Health Insurance No", + "insert_after": "health_insurance_provider", + }, + { + "fieldname": "approvers_section", + "fieldtype": "Section Break", + "label": "Approvers", + "insert_after": "default_shift", + }, + { + "fieldname": "expense_approver", + "fieldtype": "Link", + "label": "Expense Approver", + "options": "User", + "insert_after": "approvers_section", + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User", + "insert_after": "expense_approver", + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break", + "insert_after": "leave_approver", + }, + { + "fieldname": "shift_request_approver", + "fieldtype": "Link", + "label": "Shift Request Approver", + "options": "User", + "insert_after": "column_break_45", + }, + { + "fieldname": "salary_cb", + "fieldtype": "Column Break", + "insert_after": "salary_mode", + }, + { + "fetch_from": "department.payroll_cost_center", + "fetch_if_empty": 1, + "fieldname": "payroll_cost_center", + "fieldtype": "Link", + "label": "Payroll Cost Center", + "options": "Cost Center", + "insert_after": "salary_cb", + }, + ], + "Company": [ + { + "fieldname": "hr_settings_section", + "fieldtype": "Section Break", + "label": "HR & Payroll Settings", + "insert_after": "credit_limit", + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_claim_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Expense Claim Payable Account", + "no_copy": 1, + "options": "Account", + "insert_after": "hr_settings_section", + }, + { + "fieldname": "default_employee_advance_account", + "fieldtype": "Link", + "label": "Default Employee Advance Account", + "no_copy": 1, + "options": "Account", + "insert_after": "default_expense_claim_payable_account", + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break", + "insert_after": "default_employee_advance_account", + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payroll_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payroll Payable Account", + "no_copy": 1, + "options": "Account", + "insert_after": "column_break_10", + }, + ], + "Department": [ + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "insert_after": "disabled", + }, + { + "fieldname": "payroll_cost_center", + "fieldtype": "Link", + "label": "Payroll Cost Center", + "options": "Cost Center", + "insert_after": "section_break_4", + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break", + "insert_after": "payroll_cost_center", + }, + { + "description": "Days for which Holidays are blocked for this department.", + "fieldname": "leave_block_list", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Leave Block List", + "options": "Leave Block List", + "insert_after": "column_break_9", + }, + { + "description": "The first Approver in the list will be set as the default Approver.", + "fieldname": "approvers", + "fieldtype": "Section Break", + "label": "Approvers", + "insert_after": "leave_block_list", + }, + { + "fieldname": "shift_request_approver", + "fieldtype": "Table", + "label": "Shift Request Approver", + "options": "Department Approver", + "insert_after": "approvers", + }, + { + "fieldname": "leave_approvers", + "fieldtype": "Table", + "label": "Leave Approver", + "options": "Department Approver", + "insert_after": "shift_request_approver", + }, + { + "fieldname": "expense_approvers", + "fieldtype": "Table", + "label": "Expense Approver", + "options": "Department Approver", + "insert_after": "leave_approvers", + }, + ], + "Designation": [ + { + "fieldname": "required_skills_section", + "fieldtype": "Section Break", + "label": "Required Skills", + "insert_after": "description", + }, + { + "fieldname": "skills", + "fieldtype": "Table", + "label": "Skills", + "options": "Designation Skill", + "insert_after": "required_skills_section", + }, + ], + "Project": [ + { + "fieldname": "total_expense_claim", + "fieldtype": "Currency", + "label": "Total Expense Claim (via Expense Claims)", + "read_only": 1, + "insert_after": "total_costing_amount", + }, + ], + "Task": [ + { + "fieldname": "total_expense_claim", + "fieldtype": "Currency", + "label": "Total Expense Claim (via Expense Claim)", + "options": "Company:company:default_currency", + "read_only": 1, + "insert_after": "total_costing_amount", + }, + ], + "Timesheet": [ + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "no_copy": 1, + "options": "Salary Slip", + "print_hide": 1, + "read_only": 1, + "insert_after": "column_break_3", + }, + ], + "Terms and Conditions": [ + { + "default": "1", + "fieldname": "hr", + "fieldtype": "Check", + "label": "HR", + "insert_after": "buying", + }, + ], + "Loan": [ + { + "default": "0", + "depends_on": 'eval:doc.applicant_type=="Employee"', + "fieldname": "repay_from_salary", + "fieldtype": "Check", + "label": "Repay From Salary", + "insert_after": "status", + }, + ], + "Loan Repayment": [ + { + "default": "0", + "fetch_from": "against_loan.repay_from_salary", + "fieldname": "repay_from_salary", + "fieldtype": "Check", + "label": "Repay From Salary", + "insert_after": "is_term_loan", + }, + { + "depends_on": "eval:doc.repay_from_salary", + "fieldname": "payroll_payable_account", + "fieldtype": "Link", + "label": "Payroll Payable Account", + "mandatory_depends_on": "eval:doc.repay_from_salary", + "options": "Account", + "insert_after": "rate_of_interest", + }, + ], + } + + +def make_fixtures(): + records = [ + # expense claim type + {"doctype": "Expense Claim Type", "name": _("Calls"), "expense_type": _("Calls")}, + {"doctype": "Expense Claim Type", "name": _("Food"), "expense_type": _("Food")}, + {"doctype": "Expense Claim Type", "name": _("Medical"), "expense_type": _("Medical")}, + {"doctype": "Expense Claim Type", "name": _("Others"), "expense_type": _("Others")}, + {"doctype": "Expense Claim Type", "name": _("Travel"), "expense_type": _("Travel")}, + # leave type + { + "doctype": "Leave Type", + "leave_type_name": _("Casual Leave"), + "name": _("Casual Leave"), + "allow_encashment": 1, + "is_carry_forward": 1, + "max_continuous_days_allowed": "3", + "include_holiday": 1, + }, + { + "doctype": "Leave Type", + "leave_type_name": _("Compensatory Off"), + "name": _("Compensatory Off"), + "allow_encashment": 0, + "is_carry_forward": 0, + "include_holiday": 1, + "is_compensatory": 1, + }, + { + "doctype": "Leave Type", + "leave_type_name": _("Sick Leave"), + "name": _("Sick Leave"), + "allow_encashment": 0, + "is_carry_forward": 0, + "include_holiday": 1, + }, + { + "doctype": "Leave Type", + "leave_type_name": _("Privilege Leave"), + "name": _("Privilege Leave"), + "allow_encashment": 0, + "is_carry_forward": 0, + "include_holiday": 1, + }, + { + "doctype": "Leave Type", + "leave_type_name": _("Leave Without Pay"), + "name": _("Leave Without Pay"), + "allow_encashment": 0, + "is_carry_forward": 0, + "is_lwp": 1, + "include_holiday": 1, + }, + # Employment Type + {"doctype": "Employment Type", "employee_type_name": _("Full-time")}, + {"doctype": "Employment Type", "employee_type_name": _("Part-time")}, + {"doctype": "Employment Type", "employee_type_name": _("Probation")}, + {"doctype": "Employment Type", "employee_type_name": _("Contract")}, + {"doctype": "Employment Type", "employee_type_name": _("Commission")}, + {"doctype": "Employment Type", "employee_type_name": _("Piecework")}, + {"doctype": "Employment Type", "employee_type_name": _("Intern")}, + {"doctype": "Employment Type", "employee_type_name": _("Apprentice")}, + # Job Applicant Source + {"doctype": "Job Applicant Source", "source_name": _("Website Listing")}, + {"doctype": "Job Applicant Source", "source_name": _("Walk In")}, + {"doctype": "Job Applicant Source", "source_name": _("Employee Referral")}, + {"doctype": "Job Applicant Source", "source_name": _("Campaign")}, + # Offer Term + {"doctype": "Offer Term", "offer_term": _("Date of Joining")}, + {"doctype": "Offer Term", "offer_term": _("Annual Salary")}, + {"doctype": "Offer Term", "offer_term": _("Probationary Period")}, + {"doctype": "Offer Term", "offer_term": _("Employee Benefits")}, + {"doctype": "Offer Term", "offer_term": _("Working Hours")}, + {"doctype": "Offer Term", "offer_term": _("Stock Options")}, + {"doctype": "Offer Term", "offer_term": _("Department")}, + {"doctype": "Offer Term", "offer_term": _("Job Description")}, + {"doctype": "Offer Term", "offer_term": _("Responsibilities")}, + {"doctype": "Offer Term", "offer_term": _("Leaves per Year")}, + {"doctype": "Offer Term", "offer_term": _("Notice Period")}, + {"doctype": "Offer Term", "offer_term": _("Incentives")}, + # Email Account + {"doctype": "Email Account", "email_id": "jobs@example.com", "append_to": "Job Applicant"}, + ] + + make_records(records) + + +def setup_notifications(): + base_path = frappe.get_app_path("hrms", "hr", "doctype") + + # Leave Application + response = frappe.read_file( + os.path.join(base_path, "leave_application/leave_application_email_template.html") + ) + records = [ + { + "doctype": "Email Template", + "name": _("Leave Approval Notification"), + "response": response, + "subject": _("Leave Approval Notification"), + "owner": frappe.session.user, + } + ] + records += [ + { + "doctype": "Email Template", + "name": _("Leave Status Notification"), + "response": response, + "subject": _("Leave Status Notification"), + "owner": frappe.session.user, + } + ] + + # Interview + response = frappe.read_file( + os.path.join(base_path, "interview/interview_reminder_notification_template.html") + ) + records += [ + { + "doctype": "Email Template", + "name": _("Interview Reminder"), + "response": response, + "subject": _("Interview Reminder"), + "owner": frappe.session.user, + } + ] + response = frappe.read_file( + os.path.join(base_path, "interview/interview_feedback_reminder_template.html") + ) + records += [ + { + "doctype": "Email Template", + "name": _("Interview Feedback Reminder"), + "response": response, + "subject": _("Interview Feedback Reminder"), + "owner": frappe.session.user, + } + ] + + # Exit Interview + response = frappe.read_file( + os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html") + ) + records += [ + { + "doctype": "Email Template", + "name": _("Exit Questionnaire Notification"), + "response": response, + "subject": _("Exit Questionnaire Notification"), + "owner": frappe.session.user, + } + ] + + make_records(records) + + +def update_hr_defaults(): + hr_settings = frappe.get_doc("HR Settings") + hr_settings.emp_created_by = "Naming Series" + hr_settings.leave_approval_notification_template = _("Leave Approval Notification") + hr_settings.leave_status_notification_template = _("Leave Status Notification") + + hr_settings.send_interview_reminder = 1 + hr_settings.interview_reminder_template = _("Interview Reminder") + hr_settings.remind_before = "00:15:00" + + hr_settings.send_interview_feedback_reminder = 1 + hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder") + + hr_settings.exit_questionnaire_notification_template = _("Exit Questionnaire Notification") + hr_settings.save() + + +def add_non_standard_user_types(): + user_types = get_user_types_data() + + user_type_limit = {} + for user_type, data in user_types.items(): + user_type_limit.setdefault(frappe.scrub(user_type), 20) + + update_site_config("user_type_doctype_limit", user_type_limit) + + for user_type, data in user_types.items(): + create_custom_role(data) + create_user_type(user_type, data) + + +def get_user_types_data(): + return { + "Employee Self Service": { + "role": "Employee Self Service", + "apply_user_permission_on": "Employee", + "user_id_field": "user_id", + "doctypes": { + # masters + "Holiday List": ["read"], + "Employee": ["read", "write"], + # payroll + "Salary Slip": ["read"], + "Employee Benefit Application": ["read", "write", "create", "delete"], + # expenses + "Expense Claim": ["read", "write", "create", "delete"], + "Employee Advance": ["read", "write", "create", "delete"], + # leave and attendance + "Leave Application": ["read", "write", "create", "delete"], + "Attendance Request": ["read", "write", "create", "delete"], + "Compensatory Leave Request": ["read", "write", "create", "delete"], + # tax + "Employee Tax Exemption Declaration": ["read", "write", "create", "delete"], + "Employee Tax Exemption Proof Submission": ["read", "write", "create", "delete"], + # projects + "Timesheet": ["read", "write", "create", "delete", "submit", "cancel", "amend"], + # trainings + "Training Program": ["read"], + "Training Feedback": ["read", "write", "create", "delete", "submit", "cancel", "amend"], + # shifts + "Shift Request": ["read", "write", "create", "delete", "submit", "cancel", "amend"], + # misc + "Employee Grievance": ["read", "write", "create", "delete"], + "Employee Referral": ["read", "write", "create", "delete"], + "Travel Request": ["read", "write", "create", "delete"], + }, + } + } + + +def create_custom_role(data): + if data.get("role") and not frappe.db.exists("Role", data.get("role")): + frappe.get_doc( + {"doctype": "Role", "role_name": data.get("role"), "desk_access": 1, "is_custom": 1} + ).insert(ignore_permissions=True) + + +def create_user_type(user_type, data): + if frappe.db.exists("User Type", user_type): + doc = frappe.get_cached_doc("User Type", user_type) + doc.user_doctypes = [] + else: + doc = frappe.new_doc("User Type") + doc.update( + { + "name": user_type, + "role": data.get("role"), + "user_id_field": data.get("user_id_field"), + "apply_user_permission_on": data.get("apply_user_permission_on"), + } + ) + + create_role_permissions_for_doctype(doc, data) + doc.flags.ignore_links = True + doc.save(ignore_permissions=True) + + +def create_role_permissions_for_doctype(doc, data): + for doctype, perms in data.get("doctypes").items(): + args = {"document_type": doctype} + for perm in perms: + args[perm] = 1 + + doc.append("user_doctypes", args) + + +def update_select_perm_after_install(): + if not frappe.flags.update_select_perm_after_migrate: + return + + frappe.flags.ignore_select_perm = False + for row in frappe.get_all("User Type", filters={"is_standard": 0}): + print("Updating user type :- ", row.name) + doc = frappe.get_doc("User Type", row.name) + doc.flags.ignore_links = True + doc.save() + + frappe.flags.update_select_perm_after_migrate = False + + +def set_single_defaults(): + for dt in ("HR Settings", "Payroll Settings"): + default_values = frappe.db.sql( + """ + select fieldname, `default` from `tabDocField` + where parent=%s""", + dt, + ) + if default_values: + try: + doc = frappe.get_doc(dt, dt) + for fieldname, value in default_values: + doc.set(fieldname, value) + doc.flags.ignore_mandatory = True + doc.save() + except frappe.ValidationError: + pass + + frappe.db.set_default("date_format", "dd-mm-yyyy") + + +def get_post_install_patches(): + return ( + "erpnext.patches.v10_0.rename_offer_letter_to_job_offer", + "erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group", + "erpnext.patches.v11_0.move_leave_approvers_from_employee", + "erpnext.patches.v11_0.rename_field_max_days_allowed", + "erpnext.patches.v11_0.set_department_for_doctypes", + "erpnext.patches.v11_0.add_expense_claim_default_account", + "erpnext.patches.v11_0.drop_column_max_days_allowed", + "erpnext.patches.v11_0.rename_additional_salary_component_additional_salary", + "erpnext.patches.v11_1.set_salary_details_submittable", + "erpnext.patches.v11_1.rename_depends_on_lwp", + "erpnext.patches.v12_0.generate_leave_ledger_entries", + "erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger", + "erpnext.patches.v12_0.set_employee_preferred_emails", + "erpnext.patches.v12_0.set_job_offer_applicant_email", + "erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab", + "erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries", + "erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount", + "erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll", + "erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings", + "erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment", + "erpnext.patches.v13_0.updates_for_multi_currency_payroll", + "erpnext.patches.v13_0.update_reason_for_resignation_in_employee", + "erpnext.patches.v13_0.set_company_in_leave_ledger_entry", + "erpnext.patches.v13_0.rename_stop_to_send_birthday_reminders", + "erpnext.patches.v13_0.set_training_event_attendance", + "erpnext.patches.v14_0.set_payroll_cost_centers", + "erpnext.patches.v13_0.update_employee_advance_status", + "erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances", + "erpnext.patches.v14_0.delete_employee_transfer_property_doctype", + "erpnext.patches.v13_0.set_payroll_entry_status", + # HRMS + "create_country_fixtures", + ) + + +def run_post_install_patches(): + print("\nPatching Existing Data...") + + POST_INSTALL_PATCHES = get_post_install_patches() + frappe.flags.in_patch = True + + try: + for patch in POST_INSTALL_PATCHES: + # patch has not run on the site before + if not frappe.db.exists("Patch Log", {"patch": ("like", f"%{patch}%")}): + patch_name = patch.split(".")[-1] + frappe.get_attr(f"hrms.patches.post_install.{patch_name}.execute")() + finally: + frappe.flags.in_patch = False diff --git a/hrms/subscription_utils.py b/hrms/subscription_utils.py new file mode 100644 index 0000000..b914dd9 --- /dev/null +++ b/hrms/subscription_utils.py @@ -0,0 +1,177 @@ +import requests + +import frappe + +STANDARD_ROLES = [ + # standard roles + "Administrator", + "All", + "Guest", + # accounts + "Accounts Manager", + "Accounts User", + # projects + "Projects User", + "Projects Manager", + # framework + "Blogger", + "Dashboard Manager", + "Inbox User", + "Newsletter Manager", + "Prepared Report User", + "Report Manager", + "Script Manager", + "System Manager", + "Website Manager", + "Workspace Manager", +] + + +@frappe.whitelist(allow_guest=True) +def get_add_on_details(plan: str) -> dict[str, int]: + """ + Returns the number of employees to be billed under add-ons for SAAS subscription + site_details = { + "country": "India", + "plan": "Basic", + "credit_balance": 1000, + "add_ons": { + "employee": 2, + }, + "expiry_date": "2021-01-01", # as per current usage + } + """ + EMPLOYEE_LIMITS = {"Basic": 25, "Essential": 50, "Professional": 100} + add_on_details = {} + + employees_included_in_plan = EMPLOYEE_LIMITS.get(plan) + if employees_included_in_plan: + active_employees = get_active_employees() + add_on_employees = ( + active_employees - employees_included_in_plan + if active_employees > employees_included_in_plan + else 0 + ) + else: + add_on_employees = 0 + + add_on_details["employees"] = add_on_employees + return add_on_details + + +def get_active_employees() -> int: + return frappe.db.count("Employee", {"status": "Active"}) + + +@frappe.whitelist(allow_guest=True) +def subscription_updated(app: str, plan: str): + if app == "erpnext" and plan: + update_erpnext_access() + + +def update_erpnext_access(): + """ + ignores if user has no hrms subscription + enables erpnext workspaces and roles if user has subscribed to hrms and erpnext + disables erpnext workspaces and roles if user has subscribed to hrms but not erpnext + """ + disable = hide_erpnext() + update_erpnext_workspaces(disable) + update_erpnext_roles(disable) + + +def update_erpnext_workspaces(disable: bool = True): + erpnext_workspaces = [ + "Home", + "Assets", + "Accounting", + "Buying", + "CRM", + "ERPNext Integrations", + "ERPNext Settings", + "Loans", + "Manufacturing", + "Quality", + "Retail", + "Selling", + "Stock", + "Support", + ] + + for workspace in erpnext_workspaces: + try: + workspace_doc = frappe.get_doc("Workspace", workspace) + workspace_doc.flags.ignore_links = True + workspace_doc.flags.ignore_validate = True + workspace_doc.public = 0 if disable else 1 + workspace_doc.save() + except Exception: + pass + + +def update_erpnext_roles(disable: bool = True): + roles = get_erpnext_roles() + for role in roles: + try: + role_doc = frappe.get_doc("Role", role) + role_doc.disabled = disable + role_doc.ignore_links = True + role_doc.save() + except Exception: + pass + + +def get_erpnext_roles() -> set: + erpnext_roles = get_roles_for_app("erpnext") + hrms_roles = get_roles_for_app("hrms") + return erpnext_roles - hrms_roles - set(STANDARD_ROLES) + + +def get_roles_for_app(app_name: str) -> set: + erpnext_modules = get_modules_by_app(app_name) + doctypes = get_doctypes_by_modules(erpnext_modules) + roles = roles_by_doctype(doctypes) + + return roles + + +def get_modules_by_app(app_name: str) -> list: + return frappe.db.get_all("Module Def", filters={"app_name": app_name}, pluck="name") + + +def get_doctypes_by_modules(modules: list) -> list: + return frappe.db.get_all("DocType", filters={"module": ("in", modules)}, pluck="name") + + +def roles_by_doctype(doctypes: list) -> set: + roles = [] + for d in doctypes: + permissions = frappe.get_meta(d).permissions + + for d in permissions: + roles.append(d.role) + + return set(roles) + + +def hide_erpnext() -> bool: + hr_subscription = has_subscription(frappe.conf.sk_hrms) + erpnext_subscription = has_subscription(frappe.conf.sk_erpnext_smb or frappe.conf.sk_erpnext) + + if not hr_subscription: + return False + + if hr_subscription and erpnext_subscription: + # subscribed for ERPNext + return False + + # no subscription for ERPNext + return True + + +def has_subscription(secret_key) -> bool: + url = f"https://frappecloud.com/api/method/press.api.developer.saas.get_subscription_status?secret_key={secret_key}" + response = requests.request(method="POST", url=url, timeout=5) + + status = response.json().get("message") + return True if status == "Active" else False diff --git a/hrms/templates/__init__.py b/hrms/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/templates/emails/anniversary_reminder.html b/hrms/templates/emails/anniversary_reminder.html new file mode 100644 index 0000000..db338dd --- /dev/null +++ b/hrms/templates/emails/anniversary_reminder.html @@ -0,0 +1,25 @@ +
+
+ {% for person in anniversary_persons %} + {% if person.image %} + + + {% else %} + + {{ frappe.utils.get_abbr(person.name) }} + + {% endif %} + {% endfor %} +
+
+ {{ reminder_text }} +

{{ message }}

+
+
diff --git a/hrms/templates/emails/birthday_reminder.html b/hrms/templates/emails/birthday_reminder.html new file mode 100644 index 0000000..1f57b49 --- /dev/null +++ b/hrms/templates/emails/birthday_reminder.html @@ -0,0 +1,25 @@ +
+
+ {% for person in birthday_persons %} + {% if person.image %} + + + {% else %} + + {{ frappe.utils.get_abbr(person.name) }} + + {% endif %} + {% endfor %} +
+
+ {{ reminder_text }} +

{{ message }}

+
+
diff --git a/hrms/templates/emails/daily_work_summary.html b/hrms/templates/emails/daily_work_summary.html new file mode 100644 index 0000000..1764e8f --- /dev/null +++ b/hrms/templates/emails/daily_work_summary.html @@ -0,0 +1,55 @@ + + +

{{ title }}

+ +
+{% for reply in replies %} + + + + + + + + + + +
+ {% if reply.image %} + + {% else %} +
+ {{ reply.sender_name[0] }} +
+ {% endif %} +
+
+ {{ reply.sender_name }} +
+
+ + + + + + + + +
+
+ {{ reply.content }} +
+
+ + +
+{% endfor %} +{% if did_not_reply %} + + +
+

{{ did_not_reply_title }}: {{ did_not_reply }}

+
+ +
+{% endif %} diff --git a/hrms/templates/emails/daily_work_summary.txt b/hrms/templates/emails/daily_work_summary.txt new file mode 100644 index 0000000..2fb4380 --- /dev/null +++ b/hrms/templates/emails/daily_work_summary.txt @@ -0,0 +1,11 @@ +{{ title }} + +{% for reply in replies %} +{{ reply.sender_name }}: +{{ reply.content }} + + +{% endfor %} +{% if did_not_reply %} +{{ did_not_reply_title }}: {{ did_not_reply }} +{% endif %} \ No newline at end of file diff --git a/hrms/templates/emails/holiday_reminder.html b/hrms/templates/emails/holiday_reminder.html new file mode 100644 index 0000000..bbef6be --- /dev/null +++ b/hrms/templates/emails/holiday_reminder.html @@ -0,0 +1,16 @@ +
+ {{ reminder_text }} +

{{ message }}

+
+ +{% if advance_holiday_reminder %} + {% if holidays | len > 0 %} +
    + {% for holiday in holidays %} +
  1. {{ frappe.format(holiday.holiday_date, 'Date') }} - {{ holiday.description }}
  2. + {% endfor %} +
+ {% else %} +

You have no upcoming holidays this {{ frequency }}.

+ {% endif %} +{% endif %} diff --git a/hrms/templates/emails/training_event.html b/hrms/templates/emails/training_event.html new file mode 100644 index 0000000..8a2414a --- /dev/null +++ b/hrms/templates/emails/training_event.html @@ -0,0 +1,21 @@ +

{{_("Training Event")}}

+

{{ message }}

+ +

{{_("Details")}}

+{{_("Event Name")}}: {{ name }} +
{{_("Event Location")}}: {{ location }} +
{{_("Start Time")}}: {{ start_time }} +
{{_("End Time")}}: {{ end_time }} +
{{_("Attendance")}}: {{ attendance }} + +

{{_("Update Response")}}

+{% if not self_study %} +

{{_("Please update your status for this training event")}}:

+
+
+{% else %} +

{{_("Please confirm once you have completed your training")}}:

+
+{% endif %} +

{{_("Thank you")}},
+{{ user_fullname }}

diff --git a/hrms/templates/generators/job_opening.html b/hrms/templates/generators/job_opening.html new file mode 100644 index 0000000..2cd91b7 --- /dev/null +++ b/hrms/templates/generators/job_opening.html @@ -0,0 +1,33 @@ +{% extends "templates/web.html" %} + +{% block breadcrumbs %} + {% include "templates/includes/breadcrumbs.html" %} +{% endblock %} + +{% block header %} +

{{ job_title }}

+{% endblock %} + +{% block page_content %} + +{%- if description -%} +
{{ description }}
+{% endif %} + +{%- if publish_salary_range -%} +
{{_("Salary range per month")}}: {{ frappe.format_value(frappe.utils.flt(lower_range), currency=currency) }} - {{ frappe.format_value(frappe.utils.flt(upper_range), currency=currency) }}
+{% endif %} + +

+ {%- if job_application_route -%} + + {{ _("Apply Now") }} + {% else %} + + {{ _("Apply Now") }} + {% endif %} +

+ +{% endblock %} diff --git a/hrms/templates/includes/salary_slip_log.html b/hrms/templates/includes/salary_slip_log.html new file mode 100644 index 0000000..22c62ce --- /dev/null +++ b/hrms/templates/includes/salary_slip_log.html @@ -0,0 +1,19 @@ + + + + + {% for key in keys %} + + {% endfor %} + + + + {% for ss_dict in ss_list %} + + {% for key, value in ss_dict.items()|sort %} + + {% endfor %} + + {% endfor %} + +
{{title}}
{{ key }}
{{value}}
diff --git a/hrms/templates/pages/__init__.py b/hrms/templates/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hrms/tests/test_utils.py b/hrms/tests/test_utils.py new file mode 100644 index 0000000..3d7c1ad --- /dev/null +++ b/hrms/tests/test_utils.py @@ -0,0 +1,19 @@ +import frappe +from frappe.utils import get_first_day, get_last_day, getdate + + +def get_first_sunday(holiday_list="Salary Slip Test Holiday List", for_date=None): + date = for_date or getdate() + month_start_date = get_first_day(date) + month_end_date = get_last_day(date) + first_sunday = frappe.db.sql( + """ + select holiday_date from `tabHoliday` + where parent = %s + and holiday_date between %s and %s + order by holiday_date + """, + (holiday_list, month_start_date, month_end_date), + )[0][0] + + return first_sunday diff --git a/hrms/utils.py b/hrms/utils.py new file mode 100644 index 0000000..b4ce317 --- /dev/null +++ b/hrms/utils.py @@ -0,0 +1,60 @@ +import requests + +import frappe +from frappe.utils import now_datetime + +from erpnext.setup.utils import enable_all_roles_and_domains + +country_info = {} + + +@frappe.whitelist(allow_guest=True) +def get_country(fields=None): + global country_info + ip = frappe.local.request_ip + + if ip not in country_info: + fields = ["countryCode", "country", "regionName", "city"] + res = requests.get( + "https://pro.ip-api.com/json/{ip}?key={key}&fields={fields}".format( + ip=ip, key=frappe.conf.get("ip-api-key"), fields=",".join(fields) + ) + ) + + try: + country_info[ip] = res.json() + + except Exception: + country_info[ip] = {} + + return country_info[ip] + + +def before_tests(): + frappe.clear_cache() + # complete setup if missing + from frappe.desk.page.setup_wizard.setup_wizard import setup_complete + + year = now_datetime().year + if not frappe.get_list("Company"): + setup_complete( + { + "currency": "INR", + "full_name": "Test User", + "company_name": "Wind Power LLC", + "timezone": "Asia/Kolkata", + "company_abbr": "WP", + "industry": "Manufacturing", + "country": "India", + "fy_start_date": f"{year}-01-01", + "fy_end_date": f"{year}-12-31", + "language": "english", + "company_tagline": "Testing", + "email": "test@erpnext.com", + "password": "test", + "chart_of_accounts": "Standard", + } + ) + + enable_all_roles_and_domains() + frappe.db.commit() # nosemgrep diff --git a/hrms/www/__init__.py b/hrms/www/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..a238a97 --- /dev/null +++ b/license.txt @@ -0,0 +1,675 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +institute, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..84e452a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[project] +name = "hrms" +authors = [ + { name = "Frappe Technologies Pvt Ltd", email = "developers@frappe.io"} +] +description = "Open Source HR & Payroll Software" +requires-python = ">=3.10" +readme = "README.md" +dynamic = ["version"] + +[build-system] +requires = ["flit_core >=3.4,<4"] +build-backend = "flit_core.buildapi" + +[tool.black] +line-length = 99 + +[tool.isort] +line_length = 99 +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +ensure_newline_before_comments = true +indent = "\t" +known_frappe = ["frappe"] +known_erpnext = ["erpnext"] +known_hrms = ["hrms"] +sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FRAPPE", "ERPNEXT", "HRMS", "FIRSTPARTY", "LOCALFOLDER"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7668191 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +# frappe -- https://github.com/frappe/frappe is installed via 'bench init' \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a8a341e --- /dev/null +++ b/setup.py @@ -0,0 +1,5 @@ +from setuptools import setup + +name = "hrms" + +setup() diff --git a/sider.yml b/sider.yml new file mode 100644 index 0000000..2ca6e8d --- /dev/null +++ b/sider.yml @@ -0,0 +1,3 @@ +linter: + flake8: + config: .flake8 \ No newline at end of file

XmO^W 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%0D#sB)t`ax+T%c#|+Z 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�qry2T$e(bkZpjq2}hi>UVRU8RB_ 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

XmO^W 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%0D#sB)t`ax+T%c#|+Z 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�qry2T$e(bkZpjq2}hi>UVRU8RB_ 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