From d6e0892e98886aff6be631735d7ff17680cc0cad Mon Sep 17 00:00:00 2001 From: ahmed-nouri051 Date: Thu, 9 Oct 2025 18:14:27 +0200 Subject: [PATCH] exp_payroll_custom --- odex30_base/exp_payroll_custom/__init__.py | 5 + .../exp_payroll_custom/__manifest__.py | 69 + odex30_base/exp_payroll_custom/data/data.xml | 36 + odex30_base/exp_payroll_custom/i18n/ar_001.po | 3018 ++++++++++++++ .../exp_payroll_custom/models/__init__.py | 16 + .../models/company_costum.py | 8 + .../models/employee_promotions.py | 172 + .../models/employee_reward.py | 477 +++ .../models/hr_advance_payslip.py | 3570 +++++++++++++++++ .../exp_payroll_custom/models/hr_contract.py | 349 ++ .../exp_payroll_custom/models/hr_employee.py | 12 + .../models/hr_recontract.py | 94 + .../models/hr_salary_rules.py | 390 ++ .../models/hr_salary_scale.py | 71 + .../models/hr_salary_scale_level.py | 11 + .../models/hr_salary_scale_level_degree.py | 19 + .../models/hr_salary_scale_level_group.py | 23 + .../models/salary_advance.py | 180 + .../exp_payroll_custom/report/__init__.py | 4 + .../report/bank_pdf_report.py | 2880 +++++++++++++ .../report/payslip_monthly_report.py | 188 + .../security/ir.model.access.csv | 55 + .../exp_payroll_custom/security/ir_rule.xml | 134 + .../exp_payroll_custom/security/security.xml | 21 + .../static/description/icon.png | Bin 0 -> 32929 bytes .../static/src/css/website_rtl.css | 22 + .../templates/bank_pdf_report.xml | 81 + .../templates/employee_cost_template.xml | 98 + .../templates/hr_payslip_run_template.xml | 95 + .../templates/payroll_bank_text.xml | 13 + .../templates/payslip_monthly_report.xml | 134 + .../templates/report_payslip.xml | 84 + .../templates/report_payslip_details.xml | 38 + .../views/bank_pdf_report.xml | 612 +++ .../views/company_custom.xml | 16 + .../views/contract_advantage.xml | 128 + .../views/employee_promotions_view.xml | 158 + .../views/employee_reward_view.xml | 171 + .../exp_payroll_custom/views/hr_contract.xml | 109 + .../exp_payroll_custom/views/hr_employee.xml | 22 + .../views/hr_recontract.xml | 44 + .../views/hr_salary_menus.xml | 47 + .../views/hr_salary_rules.xml | 189 + .../views/hr_salary_scale.xml | 140 + .../views/hr_salary_scale_level.xml | 29 + .../views/hr_salary_scale_level_degree.xml | 16 + .../views/menu_security_cus.xml | 10 + .../views/payroll_menus.xml | 24 + .../views/payroll_report.xml | 64 + .../exp_payroll_custom/views/payslip_view.xml | 339 ++ .../views/salary_advance.xml | 165 + .../views/salary_scale_level_group.xml | 15 + .../views/salary_structure.xml | 74 + .../exp_payroll_custom/wizard/__init__.py | 11 + .../wizard/employee_selection_wizard.py | 82 + .../wizard/employee_selection_wizard.xml | 36 + .../wizard/payroll_bank_report.py | 195 + .../wizard/payroll_bank_report_view.xml | 86 + .../wizard/payslip_monthly_report.py | 118 + .../wizard/payslip_monthly_report_view.xml | 84 + 60 files changed, 15351 insertions(+) create mode 100644 odex30_base/exp_payroll_custom/__init__.py create mode 100644 odex30_base/exp_payroll_custom/__manifest__.py create mode 100644 odex30_base/exp_payroll_custom/data/data.xml create mode 100644 odex30_base/exp_payroll_custom/i18n/ar_001.po create mode 100644 odex30_base/exp_payroll_custom/models/__init__.py create mode 100644 odex30_base/exp_payroll_custom/models/company_costum.py create mode 100644 odex30_base/exp_payroll_custom/models/employee_promotions.py create mode 100644 odex30_base/exp_payroll_custom/models/employee_reward.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_advance_payslip.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_contract.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_employee.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_recontract.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_salary_rules.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_salary_scale.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_salary_scale_level.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_salary_scale_level_degree.py create mode 100644 odex30_base/exp_payroll_custom/models/hr_salary_scale_level_group.py create mode 100644 odex30_base/exp_payroll_custom/models/salary_advance.py create mode 100644 odex30_base/exp_payroll_custom/report/__init__.py create mode 100644 odex30_base/exp_payroll_custom/report/bank_pdf_report.py create mode 100644 odex30_base/exp_payroll_custom/report/payslip_monthly_report.py create mode 100644 odex30_base/exp_payroll_custom/security/ir.model.access.csv create mode 100644 odex30_base/exp_payroll_custom/security/ir_rule.xml create mode 100644 odex30_base/exp_payroll_custom/security/security.xml create mode 100644 odex30_base/exp_payroll_custom/static/description/icon.png create mode 100644 odex30_base/exp_payroll_custom/static/src/css/website_rtl.css create mode 100644 odex30_base/exp_payroll_custom/templates/bank_pdf_report.xml create mode 100644 odex30_base/exp_payroll_custom/templates/employee_cost_template.xml create mode 100644 odex30_base/exp_payroll_custom/templates/hr_payslip_run_template.xml create mode 100644 odex30_base/exp_payroll_custom/templates/payroll_bank_text.xml create mode 100644 odex30_base/exp_payroll_custom/templates/payslip_monthly_report.xml create mode 100644 odex30_base/exp_payroll_custom/templates/report_payslip.xml create mode 100644 odex30_base/exp_payroll_custom/templates/report_payslip_details.xml create mode 100644 odex30_base/exp_payroll_custom/views/bank_pdf_report.xml create mode 100644 odex30_base/exp_payroll_custom/views/company_custom.xml create mode 100644 odex30_base/exp_payroll_custom/views/contract_advantage.xml create mode 100644 odex30_base/exp_payroll_custom/views/employee_promotions_view.xml create mode 100644 odex30_base/exp_payroll_custom/views/employee_reward_view.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_contract.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_employee.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_recontract.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_salary_menus.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_salary_rules.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_salary_scale.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_salary_scale_level.xml create mode 100644 odex30_base/exp_payroll_custom/views/hr_salary_scale_level_degree.xml create mode 100644 odex30_base/exp_payroll_custom/views/menu_security_cus.xml create mode 100644 odex30_base/exp_payroll_custom/views/payroll_menus.xml create mode 100644 odex30_base/exp_payroll_custom/views/payroll_report.xml create mode 100644 odex30_base/exp_payroll_custom/views/payslip_view.xml create mode 100644 odex30_base/exp_payroll_custom/views/salary_advance.xml create mode 100644 odex30_base/exp_payroll_custom/views/salary_scale_level_group.xml create mode 100644 odex30_base/exp_payroll_custom/views/salary_structure.xml create mode 100644 odex30_base/exp_payroll_custom/wizard/__init__.py create mode 100644 odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.py create mode 100644 odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.xml create mode 100644 odex30_base/exp_payroll_custom/wizard/payroll_bank_report.py create mode 100644 odex30_base/exp_payroll_custom/wizard/payroll_bank_report_view.xml create mode 100644 odex30_base/exp_payroll_custom/wizard/payslip_monthly_report.py create mode 100644 odex30_base/exp_payroll_custom/wizard/payslip_monthly_report_view.xml diff --git a/odex30_base/exp_payroll_custom/__init__.py b/odex30_base/exp_payroll_custom/__init__.py new file mode 100644 index 0000000..0f77775 --- /dev/null +++ b/odex30_base/exp_payroll_custom/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import wizard +from . import report diff --git a/odex30_base/exp_payroll_custom/__manifest__.py b/odex30_base/exp_payroll_custom/__manifest__.py new file mode 100644 index 0000000..c6e4ae1 --- /dev/null +++ b/odex30_base/exp_payroll_custom/__manifest__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'HR Advance Payroll', + 'version': '18.0.1.0.0', + 'category': 'Odex25-HR/Odex25-HR', + 'sequence': 4, + 'website': 'http://exp-sa.com', + 'license': 'GPL-3', + 'author': 'Expert Co. Ltd.', + 'summary': 'Advance Payroll In HR', + 'description': """ + Helps you to manage All Payroll Requests of your company's staff. + """, + 'depends': [ + 'exp_hr_payroll', + 'hr_holidays_community', + 'account', + 'hr_contract', + 'hr_base', + 'report_xlsx', + 'hr_contract_custom' + ], + 'data': [ + 'security/security.xml', + 'security/ir.model.access.csv', + 'security/ir_rule.xml', + + 'data/data.xml', + + 'views/salary_structure.xml', + # 'views/salary_advance.xml', + 'views/payslip_view.xml', + 'views/employee_promotions_view.xml', + 'views/hr_salary_rules.xml', + 'views/hr_salary_scale.xml', + 'views/salary_scale_level_group.xml', + 'views/hr_salary_scale_level.xml', + 'views/hr_salary_scale_level_degree.xml', + 'views/hr_recontract.xml', + 'views/hr_employee.xml', + 'views/hr_contract.xml', + 'views/employee_reward_view.xml', + 'views/payroll_report.xml', + 'views/contract_advantage.xml', + 'views/bank_pdf_report.xml', + 'views/company_custom.xml', + 'views/menu_security_cus.xml', + + # menus + 'views/payroll_menus.xml', + 'views/hr_salary_menus.xml', + 'wizard/payslip_monthly_report_view.xml', + 'wizard/payroll_bank_report_view.xml', + 'wizard/employee_selection_wizard.xml', + + + # reports templates + 'templates/payroll_bank_text.xml', + 'templates/hr_payslip_run_template.xml', + 'templates/payslip_monthly_report.xml', + 'templates/report_payslip_details.xml', + 'templates/report_payslip.xml', + 'templates/employee_cost_template.xml', + + ], + 'installable': True, + 'auto_install': False, + 'application': True, +} diff --git a/odex30_base/exp_payroll_custom/data/data.xml b/odex30_base/exp_payroll_custom/data/data.xml new file mode 100644 index 0000000..a2c8e76 --- /dev/null +++ b/odex30_base/exp_payroll_custom/data/data.xml @@ -0,0 +1,36 @@ + + + + + level_sequence_name + hr.salary.scale.level + LO + 5 + + + + + degree_sequence_name + hr.salary.scale.level.degree + DO + 5 + + + + + group_sequence_name + hr.salary.scale.level.group + GO + 5 + + + + + salary_sequence_name + hr.payroll.structure + SO + 5 + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/i18n/ar_001.po b/odex30_base/exp_payroll_custom/i18n/ar_001.po new file mode 100644 index 0000000..433c587 --- /dev/null +++ b/odex30_base/exp_payroll_custom/i18n/ar_001.po @@ -0,0 +1,3018 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * exp_payroll_custom +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-01 12:02+0000\n" +"PO-Revision-Date: 2024-12-01 12:02+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_payslip__state +msgid "" +"* When the payslip is created the status is 'Draft'\n" +" \n" +"* If the payslip is under verification, the status is 'Waiting'.\n" +" \n" +"* If the payslip is confirmed then status is set to 'Done'.\n" +" \n" +"* When user cancel payslip the status is 'Rejected'." +msgstr "" +"* عندما يتم انشاء قسيمة دفع فان الحالة تكون 'مسودة'\n" +" \n" +"* إذا كانت قسيمة الدفع قيد التحقق فإن الحالة تكون 'إنتظار'.\n" +" \n" +"* اذا تم تأكيد قسيمة الدفع فان الحالة تكون 'مكتملة'.\n" +" \n" +"* عنمما يلغي المستخدم قسيمة الدفع فان الحالة تكون 'مرفوضة'." + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid ",,,,,,,N,N,,,,,,@SACH@," +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid ",00" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid ",P,1.0," +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid ",Salary,,@1ST@," +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid "00" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid "00," +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "بنك :" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payslip_details_inherit +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payslip_inherit +msgid "NET" +msgstr "الصافي" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payslip_inherit +msgid "Total Allowances" +msgstr "اجمالي استحقاقات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payslip_inherit +msgid "Total Deductions" +msgstr "اجمالي خصومات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payslip_inherit +msgid "Total Loans" +msgstr "اجمالي السلف" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__entry_type__all +msgid "ALL" +msgstr "الكل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__account_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__account_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__account_id +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Account" +msgstr "حساب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Account Number" +msgstr "رقم الحساب" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__transfer_type__accounting +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_contract_salary_form_view +msgid "Accounting" +msgstr "الحسابات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "Accounting Info" +msgstr "معلومات الحساب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_needaction +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_needaction +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_needaction +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_needaction +msgid "Action Needed" +msgstr "إجراء مطلوب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__active +msgid "Active" +msgstr "نشط" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_ids +msgid "Activities" +msgstr "الأنشطة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_exception_decoration +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_exception_decoration +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_exception_decoration +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "زخرفة استثناء النشاط" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_state +msgid "Activity State" +msgstr "حالة النشاط" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_type_icon +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_type_icon +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_type_icon +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_type_icon +msgid "Activity Type Icon" +msgstr "أيقونة نوع النشاط" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_employee_selection_wizard_form +msgid "Add" +msgstr "اضافه" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Add Employees" +msgstr "اضافة موظفين" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__advance +msgid "Advance" +msgstr "تقدم" + +#. module: exp_payroll_custom +#: model:hr.salary.rule,name:exp_payroll_custom.hr_payslip_rule_advance +msgid "Advance Salary" +msgstr "راتب مقدما" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__advantages +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_contract_salary_form_view +msgid "Advantages" +msgstr "الاستحقاقات والخصومات الخاصة" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__bank_type__alahli +msgid "Al-Ahli Bank" +msgstr "البنك الأهلي" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__bank_type__rajhi +msgid "Al-Rajhi Bank" +msgstr "بنك الراجحي" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__bank_type__riyadh +msgid "Al-Riyadh Bank" +msgstr "بنك الرياض" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#, python-format +msgid "All Employee" +msgstr "قيد لكل الموظفين" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__reward_type__allowance +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__report_type__allowance +#, python-format +msgid "Allowance" +msgstr "البدلات" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule_category__rule_type__allowance +msgid "Allowance" +msgstr "الراتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__advantage_id +msgid "Allowance Employee" +msgstr "استحقاق الموظف" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__allowance_name +msgid "Allowance Name" +msgstr "اسم البدل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__allowance_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__allow +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Allowances" +msgstr "البدلات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +msgid "Allowances Or Deductions Types" +msgstr "نوع الإستحقاق او الخصم" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "Allowances and deduction Totals" +msgstr "اجمالي البدلات والخصومات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__amount +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__amount +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__amount +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__amount +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__reward_type__amount +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "Amount" +msgstr "المبلغ" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__amount_base +msgid "Amount Base" +msgstr "اساس المبلغ" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Annual Cost" +msgstr "التكلفة السنوية" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__check_appraisal +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__check_appraisal +msgid "Appraisal‏ Percentage" +msgstr "النسبة حسب التقييم" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_promotions.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__salary_advance__state__approve +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#, python-format +msgid "Approved" +msgstr "إعتمـاد" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Are you sure to Delete Compute Salaries?" +msgstr "هل أنت متأكد من حذف حساب الرواتب؟" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Are you sure to Recompute Salaries?" +msgstr "هل أنت متأكد من إعادة حساب الرواتب؟" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Are you sure to Reset To Draft This Record?" +msgstr "هل أنت متأكد من إرجاع السجل لحالة المسوده؟" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Are you sure to Return Compute Salaries?" +msgstr "هل أنت متأكد من الإرجاع لمرحلة حساب الرواتب؟" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Are you sure to Transfer Accounting?" +msgstr "هل أنت متأكد من الترحيل للمالية؟" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_attachment_count +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_attachment_count +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_attachment_count +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_attachment_count +msgid "Attachment Count" +msgstr "عدد المرفقات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid "BATHDR,ACH-CR," +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__bank_id +msgid "Bank" +msgstr "البنك" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.action_bank_payroll_report +#: model:ir.ui.menu,name:exp_payroll_custom.menu_bank_payroll_report +msgid "Bank Payslip Report" +msgstr "تقرير البنك للرواتب" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_payroll_bank_wiz +msgid "Bank Payslips Report" +msgstr "تقرير البنك للرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__bank_ids +msgid "Banks" +msgstr "المصارف" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule_category__rule_type__base +#, python-format +msgid "Base" +msgstr "أساسى" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__base_salary +msgid "Base Salary" +msgstr "الراتب الاساسي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__basic_allowances +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Basic Salary" +msgstr "الراتب الأساسي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__benefits_discounts_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_form_view +msgid "Benefits and discounts" +msgstr "الفوائد والخصومات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__benefits_discounts +msgid "Benefits/Discounts" +msgstr "استحقاق/خصم" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__discount_absence__by_day +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__discount_absence__by_day +#, python-format +msgid "By Day" +msgstr "باليوم" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__discount_absence__by_hour +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__discount_absence__by_hour +#, python-format +msgid "By Hour" +msgstr "بالساعة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_xslx_report_form +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_employee_selection_wizard_form +msgid "Cancel" +msgstr "إلغاء" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__salary_advance__state__cancel +msgid "Cancelled" +msgstr "ألغيت" + +#. module: exp_payroll_custom +#: model_terms:ir.actions.act_window,help:exp_payroll_custom.employee_promotions_action_hr_employee +msgid "Click to add a Promotions..." +msgstr "انقر على إنشاء لإضافة الترقيات ..." + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip__state__close +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_run__state__close +msgid "Close" +msgstr "إلغـــاء" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__code +msgid "Code" +msgstr "الكود" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__comment +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Comment" +msgstr "تعليق" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__comments +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +msgid "Comments" +msgstr "ملاحظات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__company_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__company_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__company_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__company_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__company_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__company_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__company_id +msgid "Company" +msgstr "الشركة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__company_insurnce +msgid "Company Insurnce" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_res_company__company_pay_no +msgid "Company Pay Number" +msgstr "رقم الدفع في الشركة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "Computation" +msgstr "طريقة الحساب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Compute Sheet" +msgstr "احسب الورقة" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip__state__computed +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_run__state__computed +msgid "Computed" +msgstr "تم الحساب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Confirm" +msgstr "تأكيد" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip__state__confirmed +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_run__state__confirmed +#, python-format +msgid "Confirmed" +msgstr "تم التأكيد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__contract_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__employee_contract_id +msgid "Contract" +msgstr "العقد " + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__contract_advantage_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__contract_advantage_id +msgid "Contract Advantage" +msgstr "العقد " + +#. module: exp_payroll_custom +#: model:res.groups,name:exp_payroll_custom.group_hr_payroll_contributor +msgid "Contributor" +msgstr "مساهم" + +#. module: exp_payroll_custom +#: model_terms:ir.actions.act_window,help:exp_payroll_custom.employee_promotions_action +msgid "Create Requests." +msgstr "إنشاء طلبات." + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__create_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__create_uid +msgid "Created by" +msgstr "أنشئ بواسطة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__create_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__create_date +msgid "Created on" +msgstr "أنشئ في" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__credit +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_account__credit_account_id +msgid "Credit account" +msgstr "حساب الائتمان" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__currency_id +msgid "Currency" +msgstr "العملة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Current Degree" +msgstr "الدرجة الحالية" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Current Group" +msgstr "المجموعة الحالية" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Current Level" +msgstr "المستوى الحالي" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Current Scale" +msgstr "سلم الرواتب الحالي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_payslip__contract_id +msgid "Current contract of the employee" +msgstr "عقد الموظف الحالي " + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__contract_advantage__type__customize +#, python-format +msgid "Customize" +msgstr "يعدل" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Daily Wage" +msgstr "راتب اليوم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__date +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +msgid "Date" +msgstr "التاريخ" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__date_from +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__date_from +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__date_from +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__date_from +msgid "Date From" +msgstr "تاريخ البداية" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#: code:addons/exp_payroll_custom/wizard/payslip_monthly_report.py:0 +#, python-format +msgid "Date From must be less than or equal Date To" +msgstr "عفوا يجب ان يكون تاريخ البداية سابقاً لتاريخ النهاية" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__date +msgid "Date Request" +msgstr "تاريخ الطلب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__date_to +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__date_to +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__date_to +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__date_to +msgid "Date To" +msgstr "تاريخ النهاية" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__debit +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "Debit account" +msgstr "الحساب المدئن" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule_category__rule_type__deduction +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "Deduction" +msgstr "الخصم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__deduction_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__deduct +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Deductions" +msgstr "الخصومات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_payslip__struct_id +msgid "" +"Defines the rules that have to be applied to this payslip, accordingly to " +"the contract chosen. If you let empty the field contract, this field isn't " +"mandatory anymore and thus the rules applied will be all the rules set on " +"the structure of all contracts of the employee valid for the chosen period" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__degree_id +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__type__degree +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_filter_view +#, python-format +msgid "Degree" +msgstr "المؤهل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__department +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Department" +msgstr "الهيكل الإداري" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__department_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +msgid "Departments" +msgstr "الهيكل الإداري" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__detailed +msgid "Detail By Employees" +msgstr "تفصيلي بالموظفين" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Direct Manager" +msgstr "المدير المباشر" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__discount_absence +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__discount_absence +msgid "Discount Absence" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_category__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_payslip_monthly_report__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_payslip_monthly_report_xlsx__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_pdf__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_pdf_docx__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_xlsx__display_name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__display_name +msgid "Display Name" +msgstr "الاسم المعروض" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__done +msgid "Done in Payroll" +msgstr "تم الحساب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_promotions.py:0 +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__state__draft +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__lines_ids_reward__reward_state__draft +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__salary_advance__state__draft +#, python-format +msgid "Draft" +msgstr "مسودة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "EMP #" +msgstr "رقم الوظف" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#: model:ir.model,name:exp_payroll_custom.model_hr_employee +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__employee_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__employee_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__employee_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__employee_reward_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__employee_id +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_report_pdf_template +#, python-format +msgid "Employee" +msgstr "الموظف" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "" +"Employee \"%s\" has no contract Please create contract to add line to " +"advantages" +msgstr "لا يوجد عقد يرجى إنشاء عقد لإضافة عناصر إلى المزايا \"%s\" الموظف" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Employee Address" +msgstr "عنوان الموظف" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Employee Number" +msgstr "رقم الموظف" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.employee_advantage_list_action +#: model:ir.ui.menu,name:exp_payroll_custom.employee_advantage_menu +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_tree_view +msgid "Employee Allowances and Deductions" +msgstr "مستحقات وخصومات الموظفين الإضافية" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Employee Basic Wage" +msgstr "الراتب الاساسى" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_contract +msgid "Employee Contract" +msgstr "عقد الموظف" + +#. module: exp_payroll_custom +#: model:ir.actions.report,name:exp_payroll_custom.employee_cost_report_act +#: model:ir.actions.report,name:exp_payroll_custom.employee_cost_report_act_xlsx +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Employee Cost Report" +msgstr "تقرير تكلفة الإدارة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Employee GOSI" +msgstr "نسبة التأمينات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Employee ID" +msgstr " رقم الموظف" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__employee_insurnce +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Employee Insurnce" +msgstr "خصم التامينات الاجتماعية للموظف" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__employee_id +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_tree_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Employee Name" +msgstr "إسم الموظف" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_report_pdf_template +msgid "Employee No" +msgstr "رقم الموظف" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_employee_promotions +#: model:ir.ui.menu,name:exp_payroll_custom.employee_promotions_view +msgid "Employee Promotions" +msgstr "ترقيات الموظف" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.employee_reward_list_action +#: model:ir.ui.menu,name:exp_payroll_custom.employee_reward_menu +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_tree_view +msgid "Employee Reward and Allowances" +msgstr "مكافأة و مستحقات الموظفين" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_employee_selection_wizard +msgid "Employee Selection Wizard" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__employee_value_ids +msgid "Employee Value" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.employee_promotions_action +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +msgid "Employee promotions" +msgstr "ترقيات الموظف" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__employee_reward_id +msgid "Employee_reward_id" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__employee_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__employee_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__employee_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__employee_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_xslx_report_form +msgid "Employees" +msgstr "الموظفون" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "Employees Paysheet" +msgstr "كشف مرتبات الموظفين" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Employer GOSI" +msgstr "نسبة التأمينات للشركة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__end_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__end_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__end_date +msgid "End Date" +msgstr "تاريخ النهاية" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__termination +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__termination +#, python-format +msgid "End Of Services" +msgstr "إستحقاق نهاية خدمة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__insurnce +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__insurnce +#, python-format +msgid "Insurnce Deduction" +msgstr "خصم التأمينات الاجتماعية" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__other +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__other +#, python-format +msgid "Other" +msgstr "اخـــــرى" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule_category__rule_type__end_of_service +#, python-format +msgid "End of Service" +msgstr "نهاية الخدمة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__entry_type +msgid "Entry Type" +msgstr "نوع القيد" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#, python-format +msgid "Error, Select Salary type to calculate rule" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__exceed_condition +msgid "Exceed than maximum" +msgstr "تتجاوز من الحد الأقصى" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__contract_advantage__type__exception +#, python-format +msgid "Exception" +msgstr "استثناء" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__fixed_amount +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__fixed_amount +msgid "Fixed Amount" +msgstr "مبلغ ثابت" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__salary_type__fixed +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__salary_type__fixed +#, python-format +msgid "Fixed for all" +msgstr "ثابت للكل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_follower_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_follower_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_follower_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_follower_ids +msgid "Followers" +msgstr "المتابعون" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_channel_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_channel_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_channel_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_channel_ids +msgid "Followers (Channels)" +msgstr "المتابعون (القنوات)" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_partner_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_partner_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_partner_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_partner_ids +msgid "Followers (Partners)" +msgstr "المتابعون (الشركاء)" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__activity_type_icon +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__activity_type_icon +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__activity_type_icon +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "رمز الخط الرائع ، على سبيل المثال FA- المهام" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/bank_pdf_report.py:0 +#, python-format +msgid "Form content is missing, this report cannot be printed." +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__state__done +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__lines_ids_reward__reward_state__done +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "GM Approval" +msgstr "تم التصديق والترحيل" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "General" +msgstr "عام" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +msgid "Generate Payslip" +msgstr "إنشاء كشوف المرتبات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__gread_max +msgid "Gread Max" +msgstr "الحد الاعلى للراتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__gread_min +msgid "Gread Min" +msgstr "الحد الاقل للراتب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule_category__rule_type__gross +#, python-format +msgid "Gross" +msgstr "إجمالي" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__group_id +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__type__group +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_filter_view +#, python-format +msgid "Group" +msgstr "المجموعة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_promotions.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__employee_promotions__state__hr_manager +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#, python-format +msgid "HR Manager" +msgstr "مدير الموارد البشرية" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_promotions.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__employee_promotions__state__confirm +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#, python-format +msgid "HR Officer" +msgstr "مسؤول الموارد البشرية" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__state__hrm +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__lines_ids_reward__reward_state__hrm +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "HRM Approval" +msgstr "انتظار الرئيس التنفيذي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__hide +msgid "Hide" +msgstr "إخفاء" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__house_allowances +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__house_allowance_temp +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__house +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__house +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "House Allowance" +msgstr "بدل السكن" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_category__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_payslip_monthly_report__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_payslip_monthly_report_xlsx__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_pdf__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_pdf_docx__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_xlsx__id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__id +msgid "ID" +msgstr "المُعرف" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid "IFH,IFILE,CSV,ABCD5953001,SASABBGSA003888815," +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_exception_icon +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_exception_icon +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_exception_icon +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_exception_icon +msgid "Icon" +msgstr "الأيقونة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__activity_exception_icon +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__activity_exception_icon +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__activity_exception_icon +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "الأيقونة للإشارة إلى استثناء النشاط" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_needaction +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_unread +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_needaction +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_unread +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_needaction +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_unread +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_needaction +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_unread +msgid "If checked, new messages require your attention." +msgstr "إذا كان محددًا، فهناك رسائل جديدة تحتاج لرؤيتها." + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_has_error +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_has_sms_error +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_has_error +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_has_sms_error +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_has_error +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_has_sms_error +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_has_error +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "إذا كان محددًا، فقد حدث خطأ في تسليم بعض الرسائل." + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "Input Data" +msgstr "البيانات المدخله" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "Inputs" +msgstr "المدخلات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__date +msgid "Installment Date" +msgstr "تاريخ القسط" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__interval_time +msgid "Interval Time" +msgstr "الوقت الفاصل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_is_follower +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_is_follower +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_is_follower +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_is_follower +msgid "Is Follower" +msgstr "متابع" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__journal +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Journal" +msgstr "دفتر اليومية" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_category____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_payslip_monthly_report____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_payslip_monthly_report_xlsx____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_pdf____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_pdf_docx____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_report_exp_payroll_custom_report_payroll_bank_xlsx____last_update +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance____last_update +msgid "Last Modified on" +msgstr "آخر تعديل في" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__write_uid +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__write_uid +msgid "Last Updated by" +msgstr "آخر تحديث بواسطة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_selection_wizard__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__write_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__write_date +msgid "Last Updated on" +msgstr "آخر تحديث في" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__leave_request_case +msgid "Leave Request Case" +msgstr "طلب مغادره الحالة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Legal #" +msgstr "الرقم القومي/رقم الإقامة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__level_id +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__type__level +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_filter_view +#, python-format +msgid "Level" +msgstr "المستوى" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__listed +msgid "List By Rules" +msgstr "كشف" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__loan_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Loans" +msgstr "القروض" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_main_attachment_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_main_attachment_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_main_attachment_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_main_attachment_id +msgid "Main Attachment" +msgstr "المرفق الرئيسي" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__mandate +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__mandate +#, python-format +msgid "Mandate Allowance" +msgstr "إستحقاق إنتــداب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_has_error +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_has_error +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_has_error +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_has_error +msgid "Message Delivery error" +msgstr "خطأ في تسليم الرسائل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_ids +msgid "Messages" +msgstr "الرسائل" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__report_type__mission +msgid "Mission" +msgstr "الإنتدابات" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +msgid "Model" +msgstr "الكائن" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__move_id +msgid "Move" +msgstr "القيد المحاسبي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__move_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__move_id +msgid "Move Number" +msgstr "القيد المحاسبي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__my_activity_date_deadline +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__my_activity_date_deadline +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__my_activity_date_deadline +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "نهاية الوقت المعين للنشاط" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__name +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__name +#, python-format +msgid "Name" +msgstr "الاسم" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "National ID/Iqama ID" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "Net" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "Nets" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__new_degree +msgid "New Degree" +msgstr "درجة جديدة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__new_group +msgid "New Group" +msgstr "مجموعة جديدة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__new_level +msgid "New Level" +msgstr "مستوى جديد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__new_salary_degree +msgid "New Salary Degree" +msgstr "سلم الرواتب الجديد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__new_salary_group +msgid "New Salary Group" +msgstr "مجموعة سلم الرواتب الجديد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__new_salary_level +msgid "New Salary Level" +msgstr "مستوى سلم الرواتب الجديد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__new_salary_scale +msgid "New Salary Scale" +msgstr "سلم الرواتب الجديد" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +msgid "New degree" +msgstr "درجة جديدة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +msgid "New group" +msgstr "مجموعة جديدة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +msgid "New level" +msgstr "مستوى جديد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_date_deadline +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_date_deadline +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_date_deadline +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "الموعد النهائي للنشاط التالي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_summary +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_summary +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_summary +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_summary +msgid "Next Activity Summary" +msgstr "ملخص النشاط التالي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_type_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_type_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_type_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_type_id +msgid "Next Activity Type" +msgstr "نوع النشاط التالي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__next_approve +msgid "Next Required Approval" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__no_details +msgid "No Details" +msgstr "بدون تفاصيل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__no_rule +msgid "No Rules" +msgstr "دون البدلات والخصومات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__discount_absence__no_discount +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__discount_absence__no_discount +#, python-format +msgid "No discount" +msgstr "لا خصم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_res_company__company_hr_no +msgid "Number Of Company For HR" +msgstr "رقم الشركة للموارد البشرية" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__groups_number +msgid "Number Of Groups" +msgstr "عدد المجموعات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__level_num +msgid "Number Of Levels" +msgstr "عدد المستويات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_needaction_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_needaction_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_needaction_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_needaction_counter +msgid "Number of Actions" +msgstr "عدد الإجراءات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__degree_number +msgid "Number of Degrees" +msgstr "عدد الدرجات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_has_error_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_has_error_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_has_error_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_has_error_counter +msgid "Number of errors" +msgstr "عدد الاخطاء" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_needaction_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_needaction_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_needaction_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "عدد الرسائل التي تتطلب إجراء" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_has_error_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_has_error_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_has_error_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "عدد الرسائل الحادث بها خطأ في التسليم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__message_unread_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__message_unread_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__message_unread_counter +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__message_unread_counter +msgid "Number of unread messages" +msgstr "عدد الرسائل الجديدة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__official_mission_id +msgid "Official Mission" +msgstr "مهمة عمل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_degree +msgid "Old Degree" +msgstr "درجة قديمة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_degree_2 +msgid "Old Degree 2" +msgstr "الدرجة القديمة 2" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_group +msgid "Old Group" +msgstr "مجموعة قديمة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_group_2 +msgid "Old Group 2" +msgstr "مجموعه قديمه 2" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_level +msgid "Old Level" +msgstr "مستوى قديم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_level_2 +msgid "Old Level 2" +msgstr " 2 مستوى قديم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__old_scale +msgid "Old Scale" +msgstr "رتبة قديمة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +msgid "Other Benefits" +msgstr "بدلات اخرى" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Other Earnings" +msgstr "بدلات أخري" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__other_allowances +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Others Allowance" +msgstr "البدلات الاخرى" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__out_rule +msgid "Out of Payroll" +msgstr "خارجي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__over_time_id +msgid "OverTime" +msgstr "اجر اضافي" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__report_type__overtime +msgid "Overtime" +msgstr "الأجر الإضافي" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__overtime +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__overtime +#, python-format +msgid "Overtime Allowance" +msgstr "استحقاق اجر اضــافي" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_monthly_report +msgid "Page:" +msgstr "الصفحة:" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__paid +msgid "Paid" +msgstr "تم دفعها" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__pay_date +msgid "Pay Date" +msgstr "تاريخ الدفع" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_payslip +msgid "Pay Slip" +msgstr "ظرف المرتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__journal_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__journal_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__payment_method +msgid "Payment Method" +msgstr "طرق السداد" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__transfer_type__payroll +msgid "Payroll" +msgstr "المرتبــــــــات" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_report_exp_payroll_custom_report_payroll_bank_pdf +msgid "Payroll Bank PDF report" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.actions.report,name:exp_payroll_custom.bank_payslip_report +#: model:ir.actions.report,name:exp_payroll_custom.report_payroll_bank_xlsx +msgid "Payroll Bank Report" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_xslx_report_form +msgid "Payroll Items" +msgstr "الاستحقاقات والخصومات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__payroll_month +msgid "Payroll Month" +msgstr "تاريخ حساب الراتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__payslip_allowance +msgid "Payslip Allowance" +msgstr "بدل كشوف" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "دفعات ظرف المرتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__payslip_deduction +msgid "Payslip Deduction" +msgstr "خصومات المرتب" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_payslip_line +msgid "Payslip Line" +msgstr "خط قسيمة الدفع" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_loans__payslip_loan +msgid "Payslip Loan" +msgstr "قرض الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_report_exp_payroll_custom_payslip_monthly_report +msgid "Payslip Monthly Report" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.actions.report,name:exp_payroll_custom.act_payslip_monthly_report +msgid "Payslip PDF Report" +msgstr "تقرير المرتبات PDF" + +#. module: exp_payroll_custom +#: model:ir.actions.report,name:exp_payroll_custom.payslip_monthly_report_xlsx +msgid "Payslip Xlsx Report" +msgstr "طباعة Excel" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +msgid "Payslips" +msgstr "قسائم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.actions.report,name:exp_payroll_custom.hr_payslip_run_report_pdf +msgid "Payslips Batches Report" +msgstr "تقرير رواتب الموظفين" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_payslip_monthly_report +msgid "Payslips Monthly Report With Loans" +msgstr "تقرير الرواتب الشهري بالسلف" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.payslip_xslx_report_action +#: model:ir.ui.menu,name:exp_payroll_custom.payslip_xslx_report_menu +msgid "Payslips Report" +msgstr "تقرير المرتبات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__penalty_id +msgid "Penalty Name" +msgstr "المخالفات والجزاءات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__transfer_type__per_bank +#, python-format +msgid "Per Bank" +msgstr "قيد لكل بنك" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__transfer_type__one_by_one +#, python-format +msgid "Per Employee" +msgstr "قيد لكل موظف" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__transfer_type__per_analytic_account +#, python-format +msgid "Per Analytic Account" +msgstr "قيد بالحساب التحليلي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__percentage +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__percentage +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__percentage +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.inherited_view_hr_payslip_line_tree +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Percentage" +msgstr "نسبة حساب الراتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__percentage +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__percentage +msgid "Percentage%" +msgstr "النسبة المئوية٪" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Period" +msgstr "فترة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "" +"Please be sure that the employee is linked to contract and contract linked " +"with journal" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__entry_type__posted +msgid "Post" +msgstr "مرحل" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_xslx_report_form +msgid "Print Excel" +msgstr "طباعة Excel" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_xslx_report_form +msgid "Print PDF" +msgstr "طباعة PDF" + +#. module: exp_payroll_custom +#: model:ir.actions.report,name:exp_payroll_custom.payroll_bank_wiz_report_docx +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +msgid "Print Text" +msgstr "طباعة Text" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Promotion Date" +msgstr "تاريخ الترقية" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Promotion Degree" +msgstr "الدرجة الجديدة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Promotion Group" +msgstr "المجموعة الجديدة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +msgid "Promotion Level" +msgstr "المستوى الجديد" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_employee_promotions_inherit +msgid "Promotions" +msgstr "الترقيـات" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.employee_promotions_action_hr_employee +msgid "Promotions Action" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__promotions_count +msgid "Promotions Count" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Re-Draft" +msgstr "إعادة مسوده" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__reason +msgid "Reason" +msgstr "السبب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__reason +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__reason +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__reason +msgid "Reason/Justification" +msgstr "السببظ المبرر" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Recompute" +msgstr "إعادة حساب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "Refuse" +msgstr "رفض" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__employee_promotions__state__refuse +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__state__refused +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__lines_ids_reward__reward_state__refused +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_form_view +#, python-format +msgid "Refused" +msgstr "مرفوض" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__salary_advance__state__reject +msgid "Rejected" +msgstr "مرفوض" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__related_benefits_discounts +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__related_benefits_discounts +msgid "Related Benefits and Discount" +msgstr "الاستحقاقات و الخصومات ذات الصلة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +msgid "Related Salary Configuration" +msgstr "تكوين الراتب ذات الصلة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__salary_type__related_degrees +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__salary_type__related_degrees +#, python-format +msgid "Related with Degrees" +msgstr "ذات الصلة بالدرجات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__salary_type__related_groups +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__salary_type__related_groups +#, python-format +msgid "Related with Groups" +msgstr "ذات صلة بالمجموعات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__salary_type__related_levels +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__salary_type__related_levels +#, python-format +msgid "Related with Levels" +msgstr "ذات الصلة بالمستويات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__related_qualifications +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__related_qualifications +msgid "Related with qualifications" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.ui.menu,name:exp_payroll_custom.menu_hr_payroll_report +msgid "Reporting" +msgstr "تقارير المرتبات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/salary_advance.py:0 +#, python-format +msgid "Request can be done after \"%s\" Days From previous month salary" +msgstr "يمكن أن يتم الطلب بعد أيام \"%s\" من راتب الشهر السابق" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__required_condition +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__required_condition +msgid "Required Condition" +msgstr "الشرط المطلوب" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_res_company +msgid "Res Company SMS Configuration" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__activity_user_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__activity_user_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__activity_user_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__activity_user_id +msgid "Responsible User" +msgstr "المستخدم المسؤول" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__retirement_age +msgid "Retirement Age" +msgstr "سن التقاعد" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Return" +msgstr "إرجاع" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__reward_id +msgid "Reward" +msgstr "مكافأة/بدل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__line_ids_reward +msgid "Reward Line" +msgstr "خط المكافآت" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__reward_once +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__reward_once +msgid "Reward Once Yearly" +msgstr "مكافأة مرة واحدة سنويًا" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__allowance_reason +msgid "Reward Reason" +msgstr "سبب المكافأة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__reward_state +msgid "Reward State" +msgstr "حالة المكافأة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__reward_type +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__reward_type +msgid "Reward Type" +msgstr "نوع المكافأة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__benefits_discounts +msgid "Rewards/Benefits" +msgstr "مكافأة/بدل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__rule_credit_account_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__rule_credit_account_id +msgid "Rule Credit Account" +msgstr "قاعدة حساب الائتمان" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__rule_debit_account_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__rule_debit_account_id +msgid "Rule Debit Account" +msgstr "قاعدة حساب الخصم" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__rule_ids +msgid "Rules" +msgstr "القواعد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__rules_type +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__rules_type +msgid "Rules Type" +msgstr "النــوع" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid "SECPTY," +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_has_sms_error +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_has_sms_error +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_has_sms_error +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_has_sms_error +msgid "SMS Delivery error" +msgstr "خطأ في تسليم الرسائل القصيرة" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "SN" +msgstr "الرقم المسلسل" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__report_type__salary +msgid "Salary" +msgstr "رواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__salary +msgid "Salary / Percentage" +msgstr "الراتب / النسبة المئوية" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__salary +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__salary +#, python-format +msgid "Salary Allowance" +msgstr "الراتب الاســـاسي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__salary_amount_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__salary_amount_ids +msgid "Salary Amount" +msgstr "مبلغ الراتب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Salary Bank Payslip Report" +msgstr "تقرير البنك للرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__degree_ids +msgid "Salary Basice" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__salary_degree +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__salary_degree +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__salary_degree +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_contract_salary_form_view +msgid "Salary Degree" +msgstr "درجة الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__group_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__degree_ids +msgid "Salary Degrees" +msgstr "الدرجات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__salary_group +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__salary_group +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__salary_group +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_contract_salary_form_view +msgid "Salary Group" +msgstr "مجموعة الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__group_ids +msgid "Salary Groups" +msgstr "المجموعات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__journal_id +msgid "Salary Journal" +msgstr "يومية الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__salary_level +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__salary_level +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__salary_level +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_contract_salary_form_view +msgid "Salary Level" +msgstr "مستوى الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__level_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__level_ids +msgid "Salary Levels" +msgstr "المستويات" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_salary_rule +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__salary_rule_id +msgid "Salary Rule" +msgstr "قاعدة المرتب" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_salary_rule_category +msgid "Salary Rule Category" +msgstr "فئة قاعدة المرتب" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.salary_scale_record_action +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__salary_scale +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__salary_scale_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__salary_scale +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_re_contract__salary_scale +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__salary_scale +#: model:ir.ui.menu,name:exp_payroll_custom.salary_scale_main_menu +#: model:ir.ui.menu,name:exp_payroll_custom.salary_scale_menu +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_employee_salary_scale_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Salary Scale" +msgstr "سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__salary_scale_degree +msgid "Salary Scale Degree" +msgstr "درجة سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.salary_scale_level_group_record_action +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__salary_scale_group_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__salary_scale_group +msgid "Salary Scale Group" +msgstr "مجموعة سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.salary_scale_level_action +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__salary_scale_level_id +#: model:ir.model.fields,field_description:exp_payroll_custom.field_related_salary_amount__salary_scale_level +msgid "Salary Scale Level" +msgstr "مستوى سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.salary_scale_level_degree_record_action +msgid "Salary Scale Level Degree" +msgstr "درجه مستوي سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__salary_scale_level_degrees_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_form_view +msgid "Salary Scale Level Degrees" +msgstr "درجات مستوي سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__salary_scale_level_groups_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_form_view +msgid "Salary Scale Level Groups" +msgstr "مجموعات مستوى سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__salary_scale_levels_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_form_view +msgid "Salary Scale Levels" +msgstr "مستويات سلم الرواتب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "Salary Slip of %s for %s" +msgstr "قسيمة راتب %s لشهر %s" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_payroll_structure +msgid "Salary Structure" +msgstr "سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__salary_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payslip_monthly_report__salary_ids +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.bank_payroll_report_form +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.payslip_xslx_report_form +msgid "Salary Structures" +msgstr "هيكل الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__salary_type +msgid "Salary Type" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "" +"Salary is less than 0 this month for the following employees \n" +" %s" +msgstr "" +" الراتب أقل من صفر هذا الشهر للموظفين الاتية اسماهم \n" +" %s" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee__salary_scale +msgid "Salary scale" +msgstr "سلم الرواتب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale.py:0 +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__type__scale +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_filter_view +#, python-format +msgid "Scale" +msgstr "سلم" + +#. module: exp_payroll_custom +#: model:ir.ui.menu,name:exp_payroll_custom.salary_scale_level_group_menu +msgid "Scale Group" +msgstr "مجموعة سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.ui.menu,name:exp_payroll_custom.salary_scale_level_menu +msgid "Scale Levels" +msgstr "مستوي سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.ui.menu,name:exp_payroll_custom.salary_scale_level_degree_menu +msgid "Scale degree" +msgstr "درجه سلم الرواتب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_filter_view +msgid "Search Salary Scale" +msgstr "البحث عن سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__bank_type +msgid "Select Bank" +msgstr "حدد البنك" + +#. module: exp_payroll_custom +#: model:ir.actions.act_window,name:exp_payroll_custom.action_employee_selection_wizard +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_employee_selection_wizard_form +msgid "Select Employees" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#: code:addons/exp_payroll_custom/wizard/payslip_monthly_report.py:0 +#, python-format +msgid "Sorry No Data To Be Printed" +msgstr "عفواً لا توجد بيانات لتتم طباعتها" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "Sorry, Can Not Request without The Employees" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "" +"Sorry, Salary has already been calculated This Month for Employees \n" +" %s" +msgstr "" +" للأسف, تم حساب الراتب لهذا الشهر مسبقآ للموظفين الاتية اسماهم \n" +" %s" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "" +"Sorry, The Employee %s Cannot be Taking More Than Once Reward In The Same " +"Year %s" +msgstr "للأسف, الموظف %s لايمكنه أخذ هذه المكافأة اكثر من مرة لنفس سنـــة %s" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "" +"Sorry, The Previous month's salary has Not been calculated for Employees \n" +" %s" +msgstr "" +"للأسف, لم يتم احتساب الراتب للشهر السابق للموظفين الاتية اسماهم \n" +" %s" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "Sorry, The Pyroll period Must be During the same Month" +msgstr "للأسف، فترة الراتب يجب أن تكون خلال نفس الشهر" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__special +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__special +msgid "Special" +msgstr "خــاص" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "Specific Allowance and deduction Report" +msgstr "تقرير استحقاق خصم معين" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__start_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__start_date +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__start_date +msgid "Start Date" +msgstr "تاريخ البداية" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__state +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_advantage_tree_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_promotions_tree_view +msgid "State" +msgstr "الحالة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_run__state +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__state +msgid "Status" +msgstr "الحالة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__activity_state +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__activity_state +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__activity_state +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"الحالة على أساس الأنشطة\n" +"المتأخرة: تاريخ الاستحقاق مر\n" +"اليوم: تاريخ النشاط هو اليوم\n" +"المخطط: الأنشطة المستقبلية." + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__struct_id +msgid "Structure" +msgstr "الهيكل" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_employee_reward__state__submitted +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__lines_ids_reward__reward_state__submitted +msgid "Submit" +msgstr "انتظار الموارد البشرية" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__salary_advance__state__submit +msgid "Submitted" +msgstr "ارسال" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__exceed_condition +msgid "The Advance is greater than the maximum percentage in salary structure" +msgstr "القيمة أكبر من النسبة المئوية القصوى في سلم الرواتب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "The Amount Must Be Greater Than Zero" +msgstr "يجب أن يكون المبلغ أكبر من الصفر" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#, python-format +msgid "The Amount Must be Greater Than Zero The Employee %s" +msgstr "المبلغ يجب ان يكون اكبر من الصفر للموظف %s" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#: code:addons/exp_payroll_custom/models/hr_salary_scale_level_degree.py:0 +#, python-format +msgid "" +"The Basic Salary Is Greater Than Group Gread Max Or less than Gread Min" +msgstr "" +" الراتب الاساسي اكبر من الحدى الاعلى او اقل من الحد الادني لمجموعة سلم " +"الرواتب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#, python-format +msgid "The Date Form Must be Less than Date To" +msgstr "تاريخ البداية يجب ان يكون قبل تاريخ النهاية للاستحقاق او الاخصم" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "The Employee %s Amount Must Be Greater Than Zero" +msgstr "يجب ان يكون مبلغ الموظف %s اكبر من الصفر" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale_level_group.py:0 +#, python-format +msgid "The Gread Max Is Greater Than Gread Min" +msgstr "الحد الاعلى يجب ان يكون اكبر من الحد الادنى" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_scale_level_group.py:0 +#, python-format +msgid "The Gread Max Or Gread Min is not Negative" +msgstr "الحد الاعلى او الاقل يجب ان لا يكون سالب" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#, python-format +msgid "The Salary Rule is Not Deduction" +msgstr "يجب ان يكون تصنيف قاعدة الرواتب خصمآ فقط" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "The amount you put is greater than fact value of this Salary rule" +msgstr "المبلغ الذي وضعته أكبر من القيمة الحقيقية لقاعدة الرواتب هذه" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#, python-format +msgid "" +"The amount you put is greater than fact value of this Salary rule %s (%s)." +msgstr " (%s) %s المبلغ الذي وضعته أكبر من القيمة الحقيقية لقاعدة الرواتب هذه" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/wizard/employee_selection_wizard.py:0 +#, python-format +msgid "The following employees are already in reward lines: %s, " +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#, python-format +msgid "There is no total for rule : %s" +msgstr "ليس هنالك مبلغ لقاعدة الرواتب : %s" + +#. module: exp_payroll_custom +#: model:res.groups,comment:exp_payroll_custom.group_hr_payroll_contributor +msgid "This user is a participant in payroll related processes." +msgstr "هذا المستخدم مشارك في العمليات المتعلقة بكشوف المرتبات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_cost_report +#, python-format +msgid "Total" +msgstr "الاجمالي" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#, python-format +msgid "Total " +msgstr "الاجمالي" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__total_allowance +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__total_allowances +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_report_pdf_template +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Total Allowance" +msgstr "إجمالي الراتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__total_deduction +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__total_deductions +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_report_pdf_template +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Total Deduction" +msgstr "اجمالي الخصومات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__total_loans +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Total Loans" +msgstr "اجمالي القروض" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__total_net +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__total_sum +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_report_pdf_template +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +msgid "Total Net" +msgstr "صــافي الراتب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Total Salary" +msgstr "إجمالي الراتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__report_type__training +msgid "Training" +msgstr "التدريب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "Transaction Reference" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip__state__transfered +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_run__state__transfered +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Transfer" +msgstr "ترحيل القيد" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__transfer_type +msgid "Transfer Type" +msgstr "طريقة الدفع" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__transfer_type +#: model:ir.model.fields,field_description:exp_payroll_custom.field_lines_ids_reward__transfer_type +msgid "Transfer type" +msgstr "طريقة الدفع" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_contract__transport_allowance +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip__trans_allowances +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payslip_line__rules_type__transport +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule__rules_type__transport +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +#, python-format +msgid "Transport Allowance" +msgstr "بدل النقل" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/payslip_monthly_report.py:0 +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__type +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__type +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_category__rule_type +#, python-format +msgid "Type" +msgstr "النوع" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__salary_type +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__salary_type +msgid "Type Scale" +msgstr "نوع سلم الرواتب" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__activity_exception_decoration +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__activity_exception_decoration +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__activity_exception_decoration +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "نوع النشاط الاستثنائي المسجل." + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__entry_type__unposted +msgid "Un Post" +msgstr "غير مرحل" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_unread +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_unread +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_unread +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_unread +msgid "Unread Messages" +msgstr "الرسائل الجديدة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__message_unread_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__message_unread_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__message_unread_counter +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__message_unread_counter +msgid "Unread Messages Counter" +msgstr "عدد الرسائل الجديدة" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__salary_advance__state__waiting_approval +msgid "Waiting Approval" +msgstr "في انتظار الموافقة" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_contract_advantage__website_message_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_employee_promotions__website_message_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_employee_reward__website_message_ids +#: model:ir.model.fields,field_description:exp_payroll_custom.field_salary_advance__website_message_ids +msgid "Website Messages" +msgstr "رسائل الموقع" + +#. module: exp_payroll_custom +#: model:ir.model.fields,help:exp_payroll_custom.field_contract_advantage__website_message_ids +#: model:ir.model.fields,help:exp_payroll_custom.field_employee_promotions__website_message_ids +#: model:ir.model.fields,help:exp_payroll_custom.field_hr_employee_reward__website_message_ids +#: model:ir.model.fields,help:exp_payroll_custom.field_salary_advance__website_message_ids +msgid "Website communication history" +msgstr "سجل تواصل الموقع" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.model_payslip_form_view +msgid "Withdraw" +msgstr "تراجع" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#, python-format +msgid "Wrong percentage base or quantity defined for salary rule %s (%s)." +msgstr "أساس النسبة المئوية الخطأ أو القيمة المحددة لقاعدة المرتب %s (%s)." + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#, python-format +msgid "Wrong python code defined for salary rule %s (%s)." +msgstr "تم تحديد قيمة خاطئة لقاعدة الراتب %s (%s)." + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_salary_rules.py:0 +#, python-format +msgid "Wrong quantity defined for salary rule %s (%s)." +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#, python-format +msgid "" +"You Can Not add Same Allowance/Deduction at The Same Employee %s For The " +"Same Month!" +msgstr " لايمكن إضافة نفس الاستحقاق/الخصم لنفس الموظف %s في نفس الشهر" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "You can not Return Payslips Patch Has account move %s" +msgstr "لايمكن ارجاع حساب الراتب بعد ترحيل حزمة الرواتب بالقيد %s" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "You can not Return account move %s in state not draft" +msgstr "لايمكن ارجاع حساب الراتب و القيد %s في حالة غير مسوده" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "You can not cancel account move \"%s\" in state not draft" +msgstr "لا يمكن حذف الغاء القيد \"%s\" في حالة غير المسوده" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_promotions.py:0 +#, python-format +msgid "You can not delete employee promotions for \"%s\" in state not in draft." +msgstr "لا يمكن حذف السجل \"%s\" في حالة غير المسوده" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "You can not delete record in state not in draft" +msgstr "لا يمكن حذف السجل في حالة غير المسوده" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_promotions.py:0 +#, python-format +msgid "You can not promotions Employee has Penalty %s in this Period." +msgstr "لا يمكن ترقية الموظف لديه عقوبة \"%s\" في هذه الفترة" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_contract.py:0 +#, python-format +msgid "You cannot delete The Salary rule %s For the Employee %s is Not Draft" +msgstr "للأسف, قاعدة الراتب %s للموظف %s لايمكن حذفها في حالة غير مسوده" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +#, python-format +msgid "You cannot delete a payslip which is not draft!" +msgstr "لا يمكن حذف السجل في حالة غير المسوده" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_contract_advantage +msgid "contract.advantage" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_employee_reward +msgid "hr.employee.reward" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_hr_re_contract +msgid "hr.re.contract" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_lines_ids_reward +msgid "lines.ids.reward" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_payslip_loans +msgid "payslip.loans" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.employee_reward_form_view +msgid "recalculate" +msgstr "إعادة حساب" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_related_salary_amount +msgid "related.salary.amount" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_report_exp_payroll_custom_payslip_monthly_report_xlsx +msgid "report.exp_payroll_custom.payslip_monthly_report_xlsx" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_report_exp_payroll_custom_report_payroll_bank_pdf +msgid "report.exp_payroll_custom.report_payroll_bank_pdf" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_report_exp_payroll_custom_report_payroll_bank_pdf_docx +msgid "report.exp_payroll_custom.report_payroll_bank_pdf_docx" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_report_exp_payroll_custom_report_payroll_bank_xlsx +msgid "report.exp_payroll_custom.report_payroll_bank_xlsx" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf_docx +msgid "salary of" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model,name:exp_payroll_custom.model_salary_advance +msgid "salary.advance" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "إجمالي المبلغ" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "إسم الموظف" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__employee_promotions__state__approved +msgid "الإعتماد النهائي" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "البنك" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "الحالة" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "الخصومات" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "الراتب الأساسي" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_salary_rule_category__rule_type__net +msgid "الصافي" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__contract_advantage__state__refused +msgid "مرفوض" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "العملة" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "العنوان" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "المبلغ" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "المستفيد / اسم الموظف" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "الهوية" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__payroll_bank_wiz__bank_type__sap +msgid "SAP Bank" +msgstr "بنك ساب" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "بدلات أخرى" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "دخل آخر" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_payroll_bank_wiz__report_type +msgid "Report Type" +msgstr "نوع التقرير" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "رقم الحساب" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "رقم الموظف" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "رمز البنك" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__hr_payroll_structure__transfer_type__all +msgid "قيد لكل الموظفين" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__contract_advantage__state__confirm +msgid "مؤكد" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "مرجع الدفع" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__contract_advantage__state__draft +#: model:ir.model.fields.selection,name:exp_payroll_custom.selection__employee_promotions__state__draft +msgid "مسوده" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/bank_pdf_report.py:0 +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "مسير البنك للتدريب" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/bank_pdf_report.py:0 +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "مسير البنك للحوافز" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/bank_pdf_report.py:0 +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "مسير البنك للرواتب" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/bank_pdf_report.py:0 +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "مسير البنك للعمل الإضافي" +msgstr "" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/report/bank_pdf_report.py:0 +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +#, python-format +msgid "مسير البنك لمهام العمل" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "هوية المستفيد/ المرجع" +msgstr "" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.report_payroll_bank_pdf +msgid "وصف الدفع" +msgstr "" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:employee_requests.field_hr_payslip__employee_no +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_inherit_tree +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.view_hr_payslip_filter_inherit +msgid "Employee Number" +msgstr "رقم الموظف" + + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +msgid "Sorry The Allowance %s is Not account Set" +msgstr "للأسف إسم الإستحقاق %s ليس لديه إعدادات حسابات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +msgid "Sorry The Deduction %s is Not account Set" +msgstr "للأسف إسم الخصم %s ليس لديه إعدادات حسابات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +msgid "Sorry The Loan %s is Not account Set" +msgstr "للأسف إسم السلفة %s ليس لديها إعدادات حسابات" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/employee_reward.py:0 +#, python-format +msgid "Add Employees to Reward" +msgstr "إضافة موظفين للمكافأة و مستحقات الموظفين" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_account__emp_type_id +msgid "Employee Type" +msgstr "نوع الموظف" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule_account__debit_account_id +msgid "Debit Account" +msgstr "الحساب المدين" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_salary_rule_view_form_inherit +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__transfer_by_emp_type +msgid "Transfer By Employee Type" +msgstr "الترحيل حسب نوع الموظف" + +#. module: exp_payroll_custom +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.hr_payslip_run_inherited_form_view +msgid "Holiday Period" +msgstr "فترة الإجازات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payroll_structure__analytic_account_id +#: model_terms:ir.ui.view,arch_db:exp_payroll_custom.salary_scale_form_view +msgid "Analytic Account" +msgstr "الحساب التحليلي" + +#. module: exp_payroll_custom +#: code:addons/exp_payroll_custom/models/hr_advance_payslip.py:0 +msgid "Sorry The Contract Employee %s is Not Journal Set" +msgstr "للأسف، لم يتم اعداد دفتر اليومية لعقد الموظف %s." + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__reduce_with_leaves +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__reduce_with_leaves +msgid "Reduce With Leaves" +msgstr "تخفيض البدل بسبب الإجازات" + +#. module: exp_payroll_custom +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_payslip_line__min_leave_days_to_deduct +#: model:ir.model.fields,field_description:exp_payroll_custom.field_hr_salary_rule__min_leave_days_to_deduct +msgid "Min Leave Days To Deduct" +msgstr "الحد الأدنى لأيام الإجازة لتطبيق الخصم" + +#. module: exp_payroll_custom +#: model:res.groups,name:exp_payroll_custom.group_payroll_finance_review +msgid "Finance Review" +msgstr "مراجعة المالية" + +#. module: exp_payroll_custom +#: model:res.groups,name:exp_payroll_custom.group_payroll_expense_manger +msgid "Expense Manager" +msgstr "رئيس المصروفات" + diff --git a/odex30_base/exp_payroll_custom/models/__init__.py b/odex30_base/exp_payroll_custom/models/__init__.py new file mode 100644 index 0000000..d77eb53 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from . import hr_salary_rules +from . import hr_salary_scale +from . import hr_salary_scale_level_group +from . import hr_salary_scale_level +from . import hr_salary_scale_level_degree +from . import hr_recontract +from . import hr_employee +from . import hr_contract +from . import salary_advance +from . import hr_advance_payslip +from . import employee_promotions +from . import employee_reward +from . import company_costum + diff --git a/odex30_base/exp_payroll_custom/models/company_costum.py b/odex30_base/exp_payroll_custom/models/company_costum.py new file mode 100644 index 0000000..16b7949 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/company_costum.py @@ -0,0 +1,8 @@ +from odoo import api, fields, models + + +class CompanyCustom(models.Model): + _inherit = 'res.company' + + company_hr_no = fields.Char(string="Number Of Company For HR") + company_pay_no = fields.Char(string="Company Pay Number") diff --git a/odex30_base/exp_payroll_custom/models/employee_promotions.py b/odex30_base/exp_payroll_custom/models/employee_promotions.py new file mode 100644 index 0000000..d5a7f6d --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/employee_promotions.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime + +from odoo import models, fields, api, _, exceptions + + +class EmployeePromotions(models.Model): + _name = 'employee.promotions' + _inherit = ['mail.thread', 'mail.activity.mixin'] + _rec_name = 'employee_id' + _description = 'Employee Promotions' + + date = fields.Date(required=True) + comment = fields.Text() + state = fields.Selection(selection=[('draft', _('Draft')), + ('confirm', _('HR Officer')), + ('hr_manager', _('HR Manager')), + ('approved', _('Approved')), ('refuse', 'Refused')], + default='draft', tracking=True) + + # relational fields + employee_id = fields.Many2one('hr.employee', domain=[('state', '=', 'open')]) + old_scale = fields.Many2one('hr.payroll.structure') + old_level = fields.Many2one('hr.payroll.structure') + old_group = fields.Many2one('hr.payroll.structure') + old_degree = fields.Many2one('hr.payroll.structure') + old_level_2 = fields.Many2one('hr.payroll.structure') + old_group_2 = fields.Many2one('hr.payroll.structure') + old_degree_2 = fields.Many2one('hr.payroll.structure') + new_level = fields.Many2one('hr.payroll.structure', domain=[('id', 'in', [])]) + new_group = fields.Many2one('hr.payroll.structure', domain=[('id', 'in', [])]) + new_degree = fields.Many2one('hr.payroll.structure', domain=[('id', 'in', [])]) + company_id = fields.Many2one('res.company', string='Company',default=lambda self: self.env.company) + + @api.onchange('employee_id') + def store_level_group_and_degree_values(self): + if self.employee_id: + self.old_scale = self.employee_id.salary_scale + self.old_level = self.employee_id.salary_level + self.old_group = self.employee_id.salary_group + self.old_degree = self.employee_id.salary_degree + self.old_level_2 = self.employee_id.salary_level + self.old_group_2 = self.employee_id.salary_group + self.old_degree_2 = self.employee_id.salary_degree + + # dynamic domain to get new level and new group domain + + @api.onchange('old_scale') + def _get_new_level_and_new_group_domain(self): + for item in self: + if item.old_scale: + level_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.old_scale.id), ('type', '=', 'level')]) + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.old_scale.id), ('type', '=', 'group')]) + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.old_scale.id), ('type', '=', 'degree')]) + domain = {'new_level': [('id', 'in', level_ids.ids)], + 'new_group': [('id', 'in', group_ids.ids)], + 'new_degree': [('id', 'in', degree_ids.ids)]} + return {'domain': domain} + else: + domain = {'new_level': [('id', 'in', [])], + 'new_group': [('id', 'in', [])], + 'new_degree': [('id', 'in', [])]} + return {'domain': domain} + + # filter depend on salary_level + + @api.onchange('new_level') + def onchange_salary_level(self): + for item in self: + if item.new_level: + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_level_id', '=', item.new_level.id), ('type', '=', 'group')]) + return {'domain': {'new_group': [('id', 'in', group_ids.ids)], + 'new_degree': [('id', 'in', [])]}} + else: + return {'domain': {'new_group': [('id', 'in', [])], + 'new_degree': [('id', 'in', [])]}} + + # dynamic domain to get new degree domain + + @api.onchange('new_group') + def _get_new_degree_domain(self): + for item in self: + if item.new_group: + # item.new_degree = False + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_group_id', '=', item.new_group.id), ('type', '=', 'degree')]) + domain = {'new_degree': [('id', 'in', degree_ids.ids)]} + return {'domain': domain} + else: + domain = {'new_degree': [('id', 'in', [])]} + return {'domain': domain} + + def confirm(self): + module_obj = self.env['ir.module.module'].sudo() + modules = module_obj.search([('state', '=', 'installed'), ('name', '=', 'hr_disciplinary_tracking')]) + year_promotion = datetime.strptime(str(self.date), '%Y-%m-%d').strftime('%y') + if modules: + employee_penalty = self.env['hr.penalty.register'].search([('employee_id', '=', self.employee_id.id), + ('state', 'not in', ['draft', 'refuse'])]) + if employee_penalty: + for penalty in employee_penalty: + year_penalty = datetime.strptime(str(penalty.date), '%Y-%m-%d').strftime('%y') + if year_promotion == year_penalty: + for punish in penalty.punishment_id: + if punish.type == "deprivation": + raise exceptions.Warning(_('You can not promotions Employee has Penalty %s in this ' + 'Period.') % punish.name) + self.state = 'confirm' + + def hr_manager(self): + self.confirm() + self.state = 'hr_manager' + + def approved(self): + for rec in self: + rec.employee_id.contract_id.write({ + 'salary_level': rec.new_level.id, + 'salary_group': rec.new_group.id, + 'salary_degree': rec.new_degree.id, + 'salary': rec.new_degree.base_salary, + 'salary_insurnce': rec.new_degree.base_salary, + }) + rec.state = 'approved' + + def act_refuse(self): + self.state = 'refuse' + + # change state to draft + def re_draft(self): + for rec in self: + rec.employee_id.contract_id.write({ + 'salary_level': rec.old_level_2.id, + 'salary_group': rec.old_group_2.id, + 'salary_degree': rec.old_degree_2.id, + 'salary': rec.old_degree_2.base_salary, + 'salary_insurnce': rec.old_degree_2.base_salary, + }) + + rec.state = 'draft' + + @api.model + def create(self, values): + result = super(EmployeePromotions, self).create(values) + result.old_level_2 = result.old_level + result.old_group_2 = result.old_group + result.old_degree_2 = result.old_degree + return result + + def unlink(self): + for item in self: + if item.state != 'draft': + raise exceptions.Warning(_( + 'You can not delete employee promotions for "%s" in state not in draft.') % item.employee_id.name) + return super(EmployeePromotions, self).unlink() + + +class HrEmployee(models.Model): + _inherit = 'hr.employee' + + promotions_count = fields.Integer(compute='get_employee_promotions') + + def get_employee_promotions(self): + for item in self: + promotions = item.sudo().env['employee.promotions'].search([ + ('state', '=', 'approved'), ('employee_id', '=', item.id)]) + item.sudo().promotions_count = len(promotions) + return promotions diff --git a/odex30_base/exp_payroll_custom/models/employee_reward.py b/odex30_base/exp_payroll_custom/models/employee_reward.py new file mode 100644 index 0000000..3ba2cf5 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/employee_reward.py @@ -0,0 +1,477 @@ +# -*- coding: utf-8 -*- + +import calendar +from datetime import date, datetime + +from odoo import models, fields, api, _, exceptions + + +class EmployeeReward(models.Model): + _name = 'hr.employee.reward' + _rec_name = 'allowance_name' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + line_ids_reward = fields.One2many(comodel_name='lines.ids.reward', inverse_name='employee_reward_id', + string="Reward Line") + date = fields.Date(default=lambda self: fields.Date.today(), string="Date Request", + required=True, tracking=True) + date_from = fields.Date(string="Date From", tracking=True) + date_to = fields.Date(string="Date To", tracking=True) + allowance_reason = fields.Text(string="Reward Reason", required=True) + allowance_name = fields.Many2one('hr.salary.rule', string="Allowance Name") + percentage = fields.Float(string="Percentage%", default=100) + amount = fields.Float(string="Amount") + account_id = fields.Many2one('account.account') + journal_id = fields.Many2one('account.journal', string='Payment Method') + next_approve = fields.Text(string="Next Required Approval", compute="_get_nxt_approve") + reward_type = fields.Selection( + [('allowance', 'Allowance'), ('amount', 'Amount')], default='allowance') + transfer_type = fields.Selection( + [('accounting', 'Accounting'), ('payroll', 'Payroll')], default='accounting') + state = fields.Selection( + [('draft', 'Draft'), ('submitted', 'Submit'), ('hrm', 'HRM Approval'), + ('done', 'GM Approval'), ('refused', 'Refused')], default='draft', tracking=True) + benefits_discounts = fields.Many2one(comodel_name='hr.salary.rule', string='Rewards/Benefits') + + check_appraisal = fields.Boolean(string='Appraisal‏ Percentage', default=False) + reward_once = fields.Boolean(string='Reward Once Yearly', default=False) + + company_id = fields.Many2one('res.company', string='Company', + default=lambda self: self.env.company) + + def action_add_employees(self): + self.ensure_one() + ctx = dict(self.env.context) + if not self.id: + ctx.update({ + 'default_reward_vals': { + 'name': self.name, + } + }) + else: + ctx['default_employee_reward_id'] = self.id + + if self.percentage: + ctx['default_percentage'] = self.percentage + + ctx['default_employee_id'] = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) + + return { + 'name': _('Add Employees to Reward'), + 'view_mode': 'form', + 'res_model': 'employee.selection.wizard', + 'type': 'ir.actions.act_window', + 'target': 'new', + 'context': ctx, + } + + @api.onchange('amount') + def chick_amount_positive(self): + for item in self: + if item.amount < 0: + raise exceptions.Warning(_('The Amount Must Be Greater Than Zero')) + + + @api.depends('state') + def _get_nxt_approve(self): + for record in self: + if record.state == "draft": + record.next_approve = "Submit" + for line in record.line_ids_reward: + line.reward_state = "draft" + elif record.state == "submitted": + record.next_approve = "HRM Approval" + for line in record.line_ids_reward: + line.reward_state = "submitted" + elif record.state == "hrm": + record.next_approve = "GM Approval" + for line in record.line_ids_reward: + line.reward_state = "hrm" + elif record.state == "done": + for line in record.line_ids_reward: + line.reward_state = "done" + elif record.state == "refused": + for line in record.line_ids_reward: + line.reward_state = "refused" + + def action_submit(self): + if not self.line_ids_reward: + raise exceptions.Warning(_('Sorry, Can Not Request without The Employees')) + self.state = "submitted" + for line in self.line_ids_reward: + line.check_reward_once() + line.reward_state = "submitted" + + def recalculate(self): + for line in self.line_ids_reward: + # line.amount = line.amount + # line.percentage = line.percentage + line._compute_calculate_amount() + + def action_hrm(self): + self.state = "hrm" + for line in self.line_ids_reward: + line.check_reward_once() + line.reward_state = "hrm" + + def action_done(self): + if self.transfer_type == 'accounting': + for item in self: + for record in item.line_ids_reward: + debit_line_vals = { + 'name': record.employee_id.name, + 'debit': record.amount, + 'account_id': item.account_id.id, + 'partner_id': record.employee_id.user_id.partner_id.id, + 'analytic_account_id': record.employee_id.department_id.analytic_account_id.id + } + credit_line_vals = { + 'name': record.employee_id.name, + 'credit': record.amount, + 'account_id': item.journal_id.default_account_id.id + #'partner_id': record.employee_id.user_id.partner_id.id + } + if not record.move_id: + move = record.env['account.move'].create({ + 'state': 'draft', + 'journal_id': item.journal_id.id, + 'date': item.date, + 'ref': record.employee_id.name, + 'line_ids': [(0, 0, debit_line_vals), (0, 0, credit_line_vals)], + 'res_model': 'hr.employee.reward', + 'res_id': self.id + }) + + record.move_id = move.id + self.state = "done" + for line in self.line_ids_reward: + line.reward_state = "done" + if self.transfer_type == 'payroll': + last_day_of_current_month = date.today().replace( + day=calendar.monthrange(date.today().year, date.today().month)[1]) + first_day_of_current_month = date.today().replace(day=1) + for item in self: + for record in item.line_ids_reward: + if record.employee_id.contract_id: + + advantage_arc = record.env['contract.advantage'].create({ + 'benefits_discounts': item.benefits_discounts.id, + 'type': 'customize', + 'date_from': item.date_from, + 'date_to': item.date_to, + 'amount': record.amount, + 'reward_id': True, + 'employee_id': record.employee_id.id, + 'contract_advantage_id': record.employee_id.contract_id.id, + 'out_rule': True, + 'state': 'confirm', + 'comments': item.allowance_reason}) + record.advantage_id = advantage_arc.id + else: + raise exceptions.Warning(_( + 'Employee "%s" has no contract Please create contract to add line to advantages') + % record.employee_id.name) + + self.state = "done" + for line in self.line_ids_reward: + line.reward_state = "done" + + def action_refuse(self): + self.state = "refused" + for line in self.line_ids_reward: + line.reward_state = "refused" + + def re_draft(self): + # when redraft cancel the created account move + if self.transfer_type == 'payroll': + for record in self.line_ids_reward: + record.advantage_id.draft() + record.advantage_id.unlink() + record.reward_state = "draft" + self.state = "draft" + + if self.transfer_type == 'accounting': + if self.transfer_type == 'accounting': + if self.line_ids_reward and self.line_ids_reward[0].move_id: + move_id_not_draft = False + for line in self.line_ids_reward: + if line.move_id.state == 'posted': + move_id_not_draft_name = line.move_id.name + move_id_not_draft = True + if move_id_not_draft: + raise exceptions.Warning(_( + 'You can not cancel account move "%s" in state not draft') % move_id_not_draft_name) + else: + for record in self.line_ids_reward: + # record.move_id.write({'state': 'canceled'}) + record.move_id.unlink() + record.write({'move_id': False, }) + #record.account_id = False + #record.journal_id = False + record.reward_state = "draft" + self.write({'state': 'draft'}) + #self.account_id = False + #self.journal_id = False + else: + self.write({ + 'state': 'draft', + #'account_id': False, + #'journal_id': False + }) + for record in self.line_ids_reward: + record.write({ + 'move_id': False, + #'account_id': False, + #'journal_id': False, + 'reward_state': 'draft' + }) + + def unlink(self): + for i in self: + if i.state != 'draft': + raise exceptions.Warning(_('You can not delete record in state not in draft')) + i.line_ids_reward.unlink() + return super(EmployeeReward, self).unlink() + + @api.onchange('transfer_type', 'account_id', 'journal_id', 'line_ids_reward') + def onchange_transfer_type(self): + if self.transfer_type == 'payroll': + self.account_id = False + self.journal_id = False + for line in self.line_ids_reward: + line.account_id = False + line.journal_id = False + if self.transfer_type == 'accounting': + for line in self.line_ids_reward: + if self.state == 'hrm': + if not line.account_id: + line.account_id = self.account_id + if not line.journal_id: + line.journal_id = self.journal_id + else: + line.account_id = False + line.journal_id = False + + @api.onchange('account_id') + def onchange_account_id(self): + for line in self.line_ids_reward: + self.recalculate() + line.account_id = self.account_id + + @api.onchange('journal_id') + def onchange_journal_id(self): + for line in self.line_ids_reward: + self.recalculate() + line.journal_id = self.journal_id + + +class HrEmployee(models.Model): + _name = 'lines.ids.reward' + + def _domain_get_employee(self): + return [('id', '!=', self.employee_id.id), ('state', '=', 'open')] + + employee_reward_id = fields.Many2one('hr.employee.reward', string='Employee', required=True) + employee_id = fields.Many2one('hr.employee', string='Employee', required=True, + domain=lambda self: self._domain_get_employee()) + amount = fields.Float(string="Amount", compute='_compute_calculate_amount', store=True) + account_id = fields.Many2one('account.account', string='Account') + journal_id = fields.Many2one('account.journal', string='Payment Method') + percentage = fields.Float(string="Percentage%") + move_id = fields.Many2one('account.move') + contract_advantage_id = fields.Many2one('hr.contract') + reward_state = fields.Selection( + [('draft', 'Draft'), ('submitted', 'Submit'), ('hrm', 'HRM Approval'), + ('done', 'GM Approval'), ('refused', 'Refused')], default='draft') + + amount_base = fields.Float(string="Amount Base", store=True) + check_appraisal = fields.Boolean('Appraisal‏ Percentage', related='employee_reward_id.check_appraisal', store=True) + transfer_type = fields.Selection(related='employee_reward_id.transfer_type', string='Transfer type') + + reward_once = fields.Boolean(related='employee_reward_id.reward_once', store=True) + date = fields.Date(related='employee_reward_id.date', string='Reward Once Yearly', tracking=True, store=True) + + advantage_id = fields.Many2one(comodel_name='contract.advantage', string='Allowance Employee') + + reward_type = fields.Selection(related='employee_reward_id.reward_type', string='Reward Type', store=True) + + @api.onchange('amount_base') + def chick_amount_base_positive(self): + for item in self: + if item.amount_base < 0: + raise exceptions.Warning(_('The Employee %s Amount Must Be Greater Than Zero')% item.employee_id.name) + + + # Select employee once in reward Line + @api.onchange('employee_id') + def select_employee_once(self): + employee_ids = self.env['hr.employee'].search([('state', '=', 'open')]).ids + + for line in self.employee_reward_id.line_ids_reward: + if line.employee_id: + if line.employee_id.id in employee_ids: + employee_ids.remove(line.employee_id.id) + + return {'domain': {'employee_id': [('id', 'in', employee_ids)]}} + + def get_salary_rules_account(self, record_id, amount, items): + record = self.env['hr.salary.rule.line'].create({ + 'salary_rule_id': record_id.id, + 'amount': amount, + }) + items.append(record.id) + + def compute_salary_rule(self, record_id, item, items): + for record in self: + if not record.employee_id.id: continue + contract = self.env['hr.contract'].search([('employee_id', '=', record.employee_id.id)]) + localdict = dict(employee=record.employee_id.id, contract=contract) + amount, total = 0.0, 0.0 + + if item.amount_select == 'fix': + if contract.advantages: + for con in contract.advantages: + if item.id == con.benefits_discounts.id: + if con.type == 'exception': + if con.amount > item._compute_rule(localdict)[0] or con.amount == \ + item._compute_rule(localdict)[0]: + pass + elif con.amount < item._compute_rule(localdict)[0]: + total = item._compute_rule(localdict)[0] - con.amount + elif con.type == 'customize': + total = con.amount + amount = 0 + amount += total + else: + amount = 0 + amount += item._compute_rule(localdict)[0] + else: + amount += item._compute_rule(localdict)[0] + + elif item.amount_select == 'percentage': + if contract and contract.advantages: + for con in contract.advantages: + if item.id == con.benefits_discounts.id: + if con.type == 'exception': + if con.amount > item._compute_rule(localdict)[0] or con.amount == \ + item._compute_rule(localdict)[0]: + pass + elif con.amount < item._compute_rule(localdict)[0]: + total = item._compute_rule(localdict)[0] - con.amount + elif con.type == 'customize': + total = con.amount + amount = 0 + amount += total + break + else: + if amount: + pass + else: + amount += item._compute_rule(localdict)[0] + else: + amount += item._compute_rule(localdict)[0] + + else: + if contract.advantages: + for con in contract.advantages: + if item.id == con.benefits_discounts.id: + if con.type == 'exception': + if con.amount > item._compute_rule(localdict)[0] or con.amount == \ + item._compute_rule(localdict)[0]: + pass + elif con.amount < item._compute_rule(localdict)[0]: + total = item._compute_rule(localdict)[0] - con.amount + elif con.type == 'customize': + total = con.amount + amount = 0 + amount += total + else: + if amount: + pass + else: + amount = 0 + amount += item._compute_rule(localdict)[0] + else: + amount += item._compute_rule(localdict)[0] + + return amount + + @api.depends('percentage', 'employee_reward_id', 'employee_id', 'account_id', 'journal_id','amount_base') + def _compute_calculate_amount(self): + for line in self: + if line.check_appraisal == True: + self.get_percentage_appraisal() + percentage = line.percentage + if line.employee_reward_id.reward_type == 'allowance': + if line.employee_reward_id.allowance_name: + items = [] + for item in line.employee_reward_id.allowance_name: + total = 0.0 + record_id = self.env['hr.salary.rule'].search([('id', '=', item.id)]) + total = self.compute_salary_rule(record_id, item, items) or 0 + amount = (total * percentage) / 100 + line.amount = amount + amount_base = total + line.amount_base = amount_base + elif line.employee_reward_id.reward_type == 'amount': + amount_up = line.employee_reward_id.amount + if amount_up > 0: + amount = (amount_up * percentage) / 100 + line.amount = amount + amount_base = amount_up + line.amount_base = amount_base + else: + line.amount = (line.amount_base * percentage) / 100 + + else: + line.amount = 0 + + @api.model + def default_get(self, fields): + res = super(HrEmployee, self).default_get(fields) + if self._context.get('percentage'): + res['percentage'] = self._context.get('percentage') + if self._context.get('account_id') and self._context.get('journal_id'): + res['account_id'] = self._context.get('account_id') + res['journal_id'] = self._context.get('journal_id') + return res + + # get percentage from performance appraisal + def get_percentage_appraisal(self): + Module = self.env['ir.module.module'].sudo() + modules = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_hr_appraisal')]) + + for line in self: + if modules: + emp_appraisal = self.env['hr.employee.appraisal'].search([('employee_id', '=', line.employee_id.id), + ('state', '!=', 'draft'), + ('appraisal_type', '=', 'performance')], + order='appraisal_date desc', limit=1) + level_achieved = emp_appraisal.level_achieved + if line.check_appraisal: + if emp_appraisal: + line.percentage = level_achieved + else: + line.percentage = 0 + + # To Cannot Take More Than Once Reward In The Same Year + + @api.constrains('employee_id', 'date', 'reward_once') + def check_reward_once(self): + for rec in self: + if rec.date: + current_year = datetime.strptime(str(rec.date), "%Y-%m-%d").date().year + last_reward = rec.search([('id', '!=', rec.id), ('employee_id', '=', rec.employee_id.id), + ('reward_state', 'not in', ('draft', 'refused')), + ('reward_once', '=', True)], order='date desc', limit=1).date + if last_reward: + last_year = datetime.strptime(str(last_reward), "%Y-%m-%d").date().year + if current_year == last_year and rec.reward_once: + raise exceptions.Warning(_('Sorry, The Employee %s Cannot be Taking More Than Once Reward ' + 'In The Same Year %s') % (rec.employee_id.name, current_year)) + + def unlink(self): + for i in self: + if i.reward_state != 'draft': + raise exceptions.Warning(_('You can not delete record in state not in draft')) + return super(HrEmployee, self).unlink() diff --git a/odex30_base/exp_payroll_custom/models/hr_advance_payslip.py b/odex30_base/exp_payroll_custom/models/hr_advance_payslip.py new file mode 100644 index 0000000..7997c7c --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_advance_payslip.py @@ -0,0 +1,3570 @@ +# -*- coding: utf-8 -*- + +import itertools as it +import time +from datetime import datetime, timedelta +from operator import itemgetter + +import babel +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models, tools, _, exceptions +from odoo.exceptions import UserError + +# solve Python uses "round half to even" by default +from decimal import Decimal, ROUND_HALF_UP + +# New object for loans lines in payslip +class PayslipLoans(models.Model): + _name = 'payslip.loans' + + name = fields.Char(string='Name') + code = fields.Char(string='Code') + amount = fields.Float(string='Amount') + date = fields.Date(string='Installment Date') + paid = fields.Boolean() + + # Relational field + payslip_loan = fields.Many2one('hr.payslip', ondelete='cascade') + account_id = fields.Many2one('account.account') + + +class SalaryRuleInput(models.Model): + _inherit = 'hr.payslip' + + total_allowances = fields.Float(string='Total Allowance', compute='compute_totals') + total_deductions = fields.Float(string='Total Deduction', compute='compute_totals') + total_loans = fields.Float(string='Total Loans', compute='compute_totals') + total_sum = fields.Float(string='Total Net', compute='compute_totals') + state = fields.Selection(selection_add=[('computed', 'Computed'), ('confirmed', 'Confirmed'), + ('transfered', 'Transfer'), ('close', 'Close')], tracking=True) + level_id = fields.Many2one('hr.payroll.structure', string='Level', readonly=1) + group_id = fields.Many2one('hr.payroll.structure', string='Group', readonly=1) + degree_id = fields.Many2one('hr.payroll.structure', string='Degree', readonly=1) + + # Relational fields + allowance_ids = fields.One2many('hr.payslip.line', 'payslip_allowance', string='Allowances', index=True) + deduction_ids = fields.One2many('hr.payslip.line', 'payslip_deduction', string='Deductions', index=True) + loan_ids = fields.One2many('payslip.loans', 'payslip_loan', string='Loans', index=True) + employee_id = fields.Many2one('hr.employee', string='Employee', required=True, readonly=True, + states={'draft': [('readonly', False)]}, domain="[('state','=','open')]", index=True) + contract_id = fields.Many2one('hr.contract', string='Contract', readonly=True, related='employee_id.contract_id') + struct_id = fields.Many2one('hr.payroll.structure', string='Structure', readonly=True, + related='employee_id.contract_id.salary_scale', + help='Defines the rules that have to be applied to this payslip, accordingly ' + 'to the contract chosen. If you let empty the field contract, this field isn\'t ' + 'mandatory anymore and thus the rules applied will be all the rules set on the ' + 'structure of all contracts of the employee valid for the chosen period') + percentage = fields.Float(string='Percentage', default=100) + move_id = fields.Many2one('account.move', string="Move Number") + # bank_id = fields.Many2one(related='employee_id.bank_account_id.bank_id') + + basic_allowances = fields.Float(string='Basic Salary', compute='compute_allowances_') + house_allowances = fields.Float(string='House Allowance', compute='compute_allowances_') + trans_allowances = fields.Float(string='Transport Allowance', compute='compute_allowances_') + other_allowances = fields.Float(string='Others Allowance', compute='compute_allowances_') + + employee_insurnce = fields.Float(string='Employee Insurnce', compute='compute_allowances_') + company_insurnce = fields.Float(string='Company Insurnce', compute='compute_allowances_') + + employee_no = fields.Char(related='employee_id.emp_no', readonly=True,string='Employee Number', store=True) + holiday_start_date = fields.Date(string="Holiday Start Date") + holiday_end_date = fields.Date(string="Holiday End Date") + + + def compute_allowances_(self): + for item in self: + item.basic_allowances = item.house_allowances = item.trans_allowances = item.employee_insurnce = item.company_insurnce = 0.0 + for line in item.allowance_ids: + if line.salary_rule_id.rules_type == 'salary': + item.basic_allowances = line.total + elif line.salary_rule_id.rules_type == 'house': + item.house_allowances = line.total + elif line.salary_rule_id.rules_type == 'transport': + item.trans_allowances = line.total + item.other_allowances = item.total_allowances - ( + item.basic_allowances + item.house_allowances + item.trans_allowances) + + for line in item.deduction_ids: + if line.salary_rule_id.rules_type == 'insurnce': + item.employee_insurnce = line.total + # TO Dooo + '''if item.employee_insurnce and line.employee_id.contract_id.is_gosi_deducted=='yes': + total_insurnce = item.employee_insurnce*100/10 + item.company_insurnce = total_insurnce*12/100 + else: + item.company_insurnce = (item.basic_allowances + item.house_allowances)*2/100''' + + def confirm(self): + self.write({'state': 'confirmed'}) + + def payslip_close(self): + self.write({'state': 'close'}) + + def withdraw(self): + payslip = self.env['hr.payslip'].search([('number', '=', self.number)]) + loans = self.env['hr.loan.salary.advance'].search([('employee_id', '=', self.employee_id.id)]) + if self.number == payslip.number: + if self.loan_ids: + for loan in self.loan_ids: + loan.paid = False + if loans: + for i in loans: + if i.id == loan.loan_id.id: + for l in i.deduction_lines: + if loan.date == l.installment_date and loan.paid is False: + l.paid = False + #i.remaining_loan_amount += l.installment_amount + i.get_remaining_loan_amount() + + # check remaining loan and change state to pay + if i.state == 'closed' and i.remaining_loan_amount > 0.0: + i.state = 'pay' + elif i.remaining_loan_amount == 0.0 and i.gm_propos_amount > 0.0: + i.state = 'closed' + + for line in payslip.worked_days_line_ids: + if line.name != 'Working days for this month': + line.unlink() + self.write({'allowance_ids': [(5,)], 'deduction_ids': [(5,)], 'loan_ids': [(5,)]}) + self.write({'state': 'draft'}) + + def return_button(self): + if self.contract_id.advantages: + for advantage_rule in self.contract_id.advantages: + advantage_rule.write({'done': False}) + if self.move_id: + if self.move_id.state == 'posted': + raise exceptions.Warning(_('You can not Return account move %s in state not draft') % self.move_id.name) + else: + self.move_id.unlink() + self.move_id = False + + if self.payslip_run_id: + if self.payslip_run_id.move_id: + raise exceptions.Warning( + _('You can not Return Payslips Patch Has account move %s') % self.payslip_run_id.move_id.name) + + self.write({'state': 'computed'}) + + def transfer(self): + total_list = [] + amount, amount1, amount2 = 0.0, 0.0, 0.0 + total_allow, total_ded, total_loan = 0.0, 0.0, 0.0 + + #if self.struct_id.transfer_type == 'one_by_one': + if self.struct_id.transfer_type == 'one_by_one' or self.struct_id.transfer_type == 'all' : + for l in self.allowance_ids: + amount_allow = l.total + account = l.salary_rule_id.rule_debit_account_id + total_list.append({ + 'name': l.name, + 'account_id': account.id, + 'debit': amount_allow, + 'partner_id': self.employee_id.user_id.partner_id.id, + 'analytic_account_id': self.employee_id.contract_id.analytic_account_id.id, + }) + amount += amount_allow + total_allow += amount + + for ded in self.deduction_ids: + amount_ded = -ded.total + account = ded.salary_rule_id.rule_credit_account_id + total_list.append({ + 'name': ded.name, + 'account_id': account.id, + 'credit': amount_ded, + 'partner_id': self.employee_id.user_id.partner_id.id + }) + amount1 += amount_ded + total_ded += amount1 + + for lo in self.loan_ids: + amount_loans = -lo.amount + total_list.append({ + 'name': lo.name + ' (' + lo.code + ')', + 'account_id': lo.account_id.id, + 'credit': amount_loans, + 'partner_id': self.employee_id.user_id.partner_id.id + }) + amount2 += amount_loans + total_loan += amount2 + + # create line for total of all allowance, deduction, loans of one employee + total = total_allow - total_ded - total_loan + total_list.append({ + 'name': self.name, + 'account_id': self.contract_id.journal_id.default_account_id.id, + 'partner_id': self.employee_id.user_id.partner_id.id, + 'credit': total, + }) + if not self.contract_id.journal_id.id: + raise UserError( + _("Please be sure that the employee is linked to contract and contract linked with journal")) + move = self.env['account.move'].create({ + 'journal_id': self.contract_id.journal_id.id, + 'date': self.date_to, + 'ref': self.name, + 'line_ids': [(0, 0, item) for item in total_list] + }) + self.move_id = move.id + else: + if self.employee_id.payment_method == 'bank': + journal = self.env['account.journal'].search([('type', '=', self.employee_id.payment_method)], limit=1) + + if not journal: + raise UserError('Error', ' There is no journal For that Bank..' + ' Please define a sale journal') + else: + journal = self.contract_id.journal_id + + for l in self.allowance_ids: + amount_allow = l.total + account = l.salary_rule_id.rule_debit_account_id + total_list.append({ + 'name': l.name, + 'account_id': account.id, + 'debit': amount_allow, + 'partner_id': self.employee_id.user_id.partner_id.id + }) + amount += amount_allow + total_allow += amount + + for ded in self.deduction_ids: + amount_ded = -ded.total + account = ded.salary_rule_id.rule_credit_account_id + total_list.append({ + 'name': ded.name, + 'account_id': account.id, + 'credit': amount_ded, + 'partner_id': self.employee_id.user_id.partner_id.id + }) + amount1 += amount_ded + total_ded += amount1 + + for lo in self.loan_ids: + amount_loans = -lo.amount + total_list.append({ + 'name': lo.name + ' (' + lo.code + ')', + 'account_id': lo.account_id.id, + 'credit': amount_loans, + 'partner_id': self.employee_id.user_id.partner_id.id + }) + total_loan += amount_loans + + # create line for total of all allowance, deduction, loans of one employee + total = total_allow - total_ded - total_loan + total_list.append({ + 'name': "Total", + 'account_id': journal.id, + 'partner_id': self.employee_id.user_id.partner_id.id, + 'credit': total, + }) + if not self.move_id: + move = self.env['account.move'].create({ + 'journal_id': journal.id, + # 'date': fields.Date.context_today(self), + 'date': self.date_to, + 'ref': self.name, + 'line_ids': [(0, 0, item) for item in total_list] + }) + self.move_id = move.id + self.write({'state': 'transfered'}) + + def compute_totals(self): + for item in self: + item.total_allowances = item.total_deductions = item.total_loans = 0.0 + for line in item.allowance_ids: + allow_tot = line.total + item.total_allowances += allow_tot + for line in item.deduction_ids: + ded_tot = line.total + item.total_deductions += ded_tot + for line in item.loan_ids: + item.total_loans += line.amount + item.total_sum = item.total_allowances + item.total_deductions + item.total_loans + + def get_inputs(self, contract_ids, date_from, date_to): + """This Compute the other inputs to employee payslip.""" + + res = super(SalaryRuleInput, self).get_inputs(contract_ids, date_from, date_to) + contract_obj = self.env['hr.contract'] + emp_id = contract_obj.browse(contract_ids[0].id).employee_id + adv_salary = self.env['salary.advance'].search([('employee_id', '=', emp_id.id)]) + for adv_obj in adv_salary: + current_date = datetime.strptime(str(date_from), '%Y-%m-%d').date().month + date = adv_obj.date + existing_date = datetime.strptime(str(date), '%Y-%m-%d').date().month + if current_date == existing_date: + state = adv_obj.state + amount = adv_obj.advance + for result in res: + if state == 'approve' and amount != 0 and result.get('code') == 'SAR': + result['amount'] = amount + return res + + # Override function compute sheet in employee payslips + + def compute_sheet(self): + for payslip in self: + payslip_loans = [] + amount = 0 + flag = False + number = payslip.number or self.env['ir.sequence'].next_by_code('salary.slip') + # delete old payslip lines + payslip.line_ids.unlink() + # set the list of contract for which the rules have to be applied + # if we don't give the contract, then the rules to apply should be for all current contracts of the employee + contract_ids = payslip.contract_id.ids or \ + self.get_contract(payslip.employee_id, payslip.date_from, payslip.date_to) + # lines = [(0, 0, line) for line in self._get_payslip_lines(contract_ids, payslip.id)] + + # Holidays # + holidays = self.env['hr.holidays'].search([('employee_id', '=', payslip.employee_id.id), + ('state', '=', 'validate1')]).filtered( + lambda item: item.employee_id.state == 'open') + + contract = self.env['hr.contract'].search([('employee_id', '=', payslip.employee_id.id)]) + localdict = dict(employee=payslip.employee_id.id, contract=contract) + + number_of_days = 0 + + for line in payslip.worked_days_line_ids: + if line.name == 'Unpaid Holidays For this month': + line.unlink() + elif line.name == 'Paid Holidays By percentage': + line.unlink() + elif line.name == 'Additional Paid Holidays': + line.unlink() + elif line.name == 'Exclusion or Reconcile Paid Holidays': + line.unlink() + + for holiday in holidays: + if holiday.date_from and holiday.date_to: + holiday_date_from = datetime.strptime(str(holiday.date_from), '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d') + holiday_date_to = datetime.strptime(str(holiday.date_to), '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d') + payslip_date_from = str(payslip.date_from) + payslip_date_to = str(payslip.date_to) + if payslip.holiday_start_date and payslip.holiday_end_date: + payslip_date_from = str(payslip.holiday_start_date) + payslip_date_to = str(payslip.holiday_end_date) + + if holiday.holiday_status_id.payslip_type == 'unpaid': + if payslip_date_to >= holiday_date_to and holiday_date_from >= payslip_date_from: + payroll_start = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + pyroll_end = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + number_of_days = holiday.number_of_days_temp + if number_of_days >=28: + if pyroll_days ==28: + number_of_days= number_of_days + 2 + if pyroll_days ==29: + number_of_days= number_of_days + 1 + if pyroll_days ==31: + number_of_days= number_of_days - 1 + if holiday.number_of_days_temp >= 0: # if holiday.number_of_days_temp <= 0: + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Unpaid Holidays For this month", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'unpaid', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + elif holiday_date_from >= payslip_date_from and payslip_date_to <= holiday_date_to and holiday_date_from <= payslip_date_to: + payroll_start = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + pyroll_end = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + + start_date = datetime.strptime(str(holiday.date_from), "%Y-%m-%d").date() + end_date = datetime.strptime(str(payslip.date_to), "%Y-%m-%d").date() + number_of_days = relativedelta(end_date, start_date).days + 1 + #if number_of_days >=28: + if pyroll_days ==28: + number_of_days= number_of_days + 2 + if pyroll_days ==29: + number_of_days= number_of_days + 1 + if pyroll_days ==31: + number_of_days= number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Unpaid Holidays For this month", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'unpaid', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + elif payslip_date_from >= holiday_date_from and payslip_date_to >= holiday_date_to and holiday_date_to >= payslip_date_from : + payroll_start = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + pyroll_end = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + + start_date = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + end_date = datetime.strptime(str(holiday.date_to), "%Y-%m-%d").date() + number_of_days = relativedelta(end_date, start_date).days +1 + if number_of_days >=28: + if pyroll_days ==28: + number_of_days= number_of_days + 2 + if pyroll_days ==29: + number_of_days= number_of_days + 1 + if pyroll_days ==31: + number_of_days= number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Unpaid Holidays For this month", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'unpaid', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id + })] + else: + if payslip_date_to <= holiday_date_to and holiday_date_from <= payslip_date_from: + #if payslip_date_to <= holiday_date_to: + start_date = datetime.strptime(str(payslip.date_from), "%Y-%m-%d").date() + end_date = datetime.strptime(str(payslip.date_to), "%Y-%m-%d").date() + number_of_days = relativedelta(end_date, start_date).days + 1 + if number_of_days ==28: + number_of_days= number_of_days + 2 + if number_of_days ==29: + number_of_days= number_of_days + 1 + if number_of_days ==31: + number_of_days= number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Unpaid Holidays For this month", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'unpaid', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + + elif holiday.holiday_status_id.payslip_type == 'percentage': + #if payslip.date_from >= holiday.date_from and payslip.date_to >= holiday.date_to: + if payslip_date_from >= holiday_date_from and payslip_date_to >= holiday_date_to: + start_date = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + end_date = datetime.strptime(holiday_date_to, "%Y-%m-%d").date() + timedelta( + days=1) + number_of_days = relativedelta(end_date, start_date).days + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Paid Holidays By percentage", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'percentage', + 'number_of_days': number_of_days, + 'number_of_hours': holiday.holiday_status_id.percentage, + 'contract_id': payslip.contract_id.id})] + elif payslip_date_to >= holiday_date_to and holiday_date_from >= payslip_date_from: + if holiday.number_of_days_temp >= 0: # holiday.number_of_days_temp <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Paid Holidays By percentage", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'percentage', + 'number_of_days': holiday.number_of_days_temp, + 'number_of_hours': holiday.holiday_status_id.percentage, + 'contract_id': payslip.contract_id.id})] + elif holiday_date_from >= payslip_date_from and payslip_date_to <= holiday_date_to: + start_date = datetime.strptime(holiday_date_from, "%Y-%m-%d").date() + end_date = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + number_of_days = relativedelta(end_date, start_date).days + 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Paid Holidays By percentage", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'percentage', + 'number_of_days': number_of_days, + 'number_of_hours': holiday.holiday_status_id.percentage, + 'contract_id': payslip.contract_id.id})] + else: + if payslip_date_to <= holiday_date_to: + start_date = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + end_date = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + timedelta( + days=1) + number_of_days = relativedelta(end_date, start_date).days + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Paid Holidays By percentage", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'percentage', + 'number_of_days': number_of_days, + 'number_of_hours': holiday.holiday_status_id.percentage, + 'contract_id': payslip.contract_id.id})] + elif holiday.holiday_status_id.payslip_type == 'addition': + if payslip_date_to >= holiday_date_to and holiday_date_from >= payslip_date_from: + payroll_start = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + pyroll_end = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + number_of_days = holiday.number_of_days_temp + if number_of_days >= 28: + if pyroll_days == 28: + number_of_days = number_of_days + 2 + if pyroll_days == 29: + number_of_days = number_of_days + 1 + if pyroll_days == 31: + number_of_days = number_of_days - 1 + if holiday.number_of_days_temp >= 0: # if holiday.number_of_days_temp <= 0: + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Additional Paid Holidays", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'addition', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + elif holiday_date_from >= payslip_date_from and payslip_date_to <= holiday_date_to and holiday_date_from <= payslip_date_to: + payroll_start = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + pyroll_end = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + + start_date = datetime.strptime(str(holiday.date_from), "%Y-%m-%d").date() + end_date = datetime.strptime(str(payslip.date_to), "%Y-%m-%d").date() + number_of_days = relativedelta(end_date, start_date).days + 1 + # if number_of_days >=28: + if pyroll_days == 28: + number_of_days = number_of_days + 2 + if pyroll_days == 29: + number_of_days = number_of_days + 1 + if pyroll_days == 31: + number_of_days = number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Additional Paid Holidays", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'addition', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + elif payslip_date_from >= holiday_date_from and payslip_date_to >= holiday_date_to and holiday_date_to >= payslip_date_from: + payroll_start = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + pyroll_end = datetime.strptime(payslip_date_to, "%Y-%m-%d").date() + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + + start_date = datetime.strptime(payslip_date_from, "%Y-%m-%d").date() + end_date = datetime.strptime(str(holiday.date_to), "%Y-%m-%d %H:%M:%S").date() + number_of_days = relativedelta(end_date, start_date).days + 1 + if number_of_days >= 28: + if pyroll_days == 28: + number_of_days = number_of_days + 2 + if pyroll_days == 29: + number_of_days = number_of_days + 1 + if pyroll_days == 31: + number_of_days = number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Additional Paid Holidays", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'addition', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id + })] + else: + if payslip_date_to <= holiday_date_to and holiday_date_from <= payslip_date_from: + # if payslip_date_to <= holiday_date_to: + start_date = datetime.strptime(str(payslip.date_from), "%Y-%m-%d").date() + end_date = datetime.strptime(str(payslip.date_to), "%Y-%m-%d").date() + number_of_days = relativedelta(end_date, start_date).days + 1 + if number_of_days == 28: + number_of_days = number_of_days + 2 + if number_of_days == 29: + number_of_days = number_of_days + 1 + if number_of_days == 31: + number_of_days = number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Additional Paid Holidays", + 'sequence': 1, + 'payslip_id': payslip.id, + 'code': 'addition', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + + if payslip.worked_days_line_ids: + if number_of_days < 0 or holiday.number_of_days_temp < 0: + pass + else: + for line in holiday.holiday_status_id.salary_rules_ids: + if line.amount_select == 'fix': + for allowance in payslip.allowance_ids: + if line.name == allowance.name: + if line._compute_rule(localdict)[0] != allowance.amount: + amount = allowance.amount + else: + amount = line._compute_rule(localdict)[0] + else: + amount = line._compute_rule(localdict)[0] + for deduction in payslip.deduction_ids: + if line.name == deduction.name: + if line._compute_rule(localdict)[0] != deduction.amount: + amount = (-deduction.amount) + else: + amount = line._compute_rule(localdict)[0] + elif line.amount_select == 'percentage': + if line.related_benefits_discounts: + for li in line.related_benefits_discounts: + for allowance in payslip.allowance_ids: + if line.name == allowance.name: + if li._compute_rule(localdict)[0] != allowance.amount: + amount = allowance.amount + else: + amount = li._compute_rule(localdict)[0] + + for deduction in payslip.deduction_ids: + if line.name == deduction.name: + if li._compute_rule(localdict)[0] != deduction.amount: + amount = (-deduction.amount) + else: + amount = li._compute_rule(localdict)[0] + else: + for allowance in payslip.allowance_ids: + if line.name == allowance.name: + if line._compute_rule(localdict)[0] != allowance.amount: + amount = allowance.amount + else: + amount = line._compute_rule(localdict)[0] + + for deduction in payslip.deduction_ids: + if line.name == deduction.name: + if line._compute_rule(localdict)[0] != deduction.amount: + amount = (-deduction.amount) + else: + amount = line._compute_rule(localdict)[0] + + # Update 29/07/2019 + for allow in payslip.allowance_ids: + if line.name == allow.name: + allow.update({ + 'name': line.name, + 'code': line.code, + 'category_id': line.category_id.id, + 'contract_id': payslip.contract_id.id, + 'slip_id': payslip.id, + 'quantity': 1, + 'rate': 100, + 'salary_rule_id': line.id, + 'leave_request_case': True, + 'amount': amount + }) + for ded in payslip.deduction_ids: + if line.name == ded.name: + ded.update({ + 'name': line.name, + 'code': line.code, + 'category_id': line.category_id.id, + 'contract_id': payslip.contract_id.id, + 'slip_id': payslip.id, + 'quantity': 1, + 'rate': 100, + 'salary_rule_id': line.id, + 'leave_request_case': True, + 'amount': -amount + }) + elif holiday.holiday_status_id.payslip_type == 'exclusion' or ( + holiday.holiday_status_id.payslip_type == 'reconcile' and holiday.reconcile_leave is True): + + start_holiday = datetime.strptime(str(holiday_date_from), "%Y-%m-%d %H:%M:%S").date() + end_holiday = datetime.strptime(str(holiday_date_to), "%Y-%m-%d %H:%M:%S").date() + payroll_start = datetime.strptime(str(payslip_date_from), "%Y-%m-%d").date() + pyroll_end = datetime.strptime(str(payslip_date_to), "%Y-%m-%d").date() + + if payslip.date_from >= start_holiday and payslip_date_to >= end_holiday and end_holiday >= payslip_date_from: + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + number_of_days = relativedelta(end_holiday, payroll_start).days+ 1 + if pyroll_days == 28: + number_of_days = number_of_days + 2 + if pyroll_days == 29: + number_of_days = number_of_days + 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Exclusion or Reconcile Paid Holidays", + 'sequence': 2, + 'payslip_id': payslip.id, + 'code': 'exclusion', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + elif payslip.date_to >= end_holiday and start_holiday >= payslip.date_from: + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + + number_of_days = relativedelta(pyroll_end, start_holiday).days + 1 + if pyroll_days == 28: + number_of_days = number_of_days + 2 + if pyroll_days == 29: + number_of_days = number_of_days + 1 + if pyroll_days == 31: + number_of_days = number_of_days - 1 + + if holiday.number_of_days_temp >= 0: # holiday.number_of_days_temp <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Exclusion or Reconcile Paid Holidays", + 'sequence': 2, + 'payslip_id': payslip.id, + 'code': 'exclusion', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + elif start_holiday >= payslip.date_from and payslip.date_to <= end_holiday and start_holiday <= payslip.date_to: + pyroll_days = relativedelta(pyroll_end, payroll_start).days + 1 + number_of_days = relativedelta(pyroll_end, start_holiday).days + 1 + + if pyroll_days == 28: + number_of_days = number_of_days + 2 + if pyroll_days == 29: + number_of_days = number_of_days + 1 + if pyroll_days == 31: + number_of_days = number_of_days - 1 + + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Exclusion or Reconcile Paid Holidays", + 'sequence': 2, + 'payslip_id': payslip.id, + 'code': 'exclusion', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + else: + if payslip.date_to <= end_holiday and start_holiday <= payslip.date_from: + number_of_days = relativedelta(pyroll_end, payroll_start).days + 1 + if number_of_days == 28: + number_of_days = number_of_days + 2 + if number_of_days == 29: + number_of_days = number_of_days + 1 + if number_of_days ==31: + number_of_days= number_of_days - 1 + if number_of_days >= 0: # number_of_days <= 0 + payslip.worked_days_line_ids = [(0, 0, { + 'name': "Exclusion or Reconcile Paid Holidays", + 'sequence': 2, + 'payslip_id': payslip.id, + 'code': 'exclusion', + 'number_of_days': number_of_days, + 'number_of_hours': 0.0, + 'contract_id': payslip.contract_id.id})] + if payslip.worked_days_line_ids: + for line in holiday.holiday_status_id.salary_rules_ids: + if line.amount_select == 'fix': + for allowance in payslip.allowance_ids: + if line.name == allowance.name: + if line._compute_rule(localdict)[0] != allowance.amount: + amount = allowance.amount + else: + amount = line._compute_rule(localdict)[0] + else: + amount = line._compute_rule(localdict)[0] + + for deduction in payslip.deduction_ids: + if line.name == deduction.name: + if line._compute_rule(localdict)[0] != deduction.amount: + amount = (-deduction.amount) + else: + amount = line._compute_rule(localdict)[0] + elif line.amount_select == 'percentage': + if line.related_benefits_discounts: + for li in line.related_benefits_discounts: + for allowance in payslip.allowance_ids: + if line.name == allowance.name: + if li._compute_rule(localdict)[0] != allowance.amount: + amount = allowance.amount + else: + amount = li._compute_rule(localdict)[0] + + for deduction in payslip.deduction_ids: + if line.name == deduction.name: + if li._compute_rule(localdict)[0] != deduction.amount: + amount = (-deduction.amount) + else: + amount = li._compute_rule(localdict)[0] + else: + for allowance in payslip.allowance_ids: + if line.name == allowance.name: + if line._compute_rule(localdict)[0] != allowance.amount: + amount = allowance.amount + else: + amount = line._compute_rule(localdict)[0] + + for deduction in payslip.deduction_ids: + if line.name == deduction.name: + if line._compute_rule(localdict)[0] != deduction.amount: + amount = (-deduction.amount) + else: + amount = line._compute_rule(localdict)[0] + + for allow in payslip.allowance_ids: + if line.name == allow.name: + allow.update({ + 'name': line.name, + 'code': line.code, + 'category_id': line.category_id.id, + 'contract_id': payslip.contract_id.id, + 'slip_id': payslip.id, + 'quantity': 1, + 'rate': 100, + 'salary_rule_id': line.id, + 'leave_request_case': True, + 'amount': amount + }) + for ded in payslip.deduction_ids: + if line.name == ded.name: + ded.update({ + 'name': line.name, + 'code': line.code, + 'category_id': line.category_id.id, + 'contract_id': payslip.contract_id.id, + 'slip_id': payslip.id, + 'quantity': 1, + 'rate': 100, + 'salary_rule_id': line.id, + 'leave_request_case': True, + 'amount': -amount + }) + + lines = [(0, 0, line) for line in self._get_payslip_lines(contract_ids, payslip.id)] + payslip.write({'line_ids': lines, 'number': number, 'level_id': payslip.contract_id.salary_level.id, + 'group_id': payslip.contract_id.salary_group.id, + 'degree_id': payslip.contract_id.salary_degree.id, }) + + for line in payslip.line_ids: + if line.category_id.rule_type == 'allowance' or line.category_id.rule_type == 'deduction': + flag = True + + if flag: + allowances = payslip.line_ids.filtered( + lambda a: a.amount != 0 and a.rate != 0 and a.category_id.rule_type == 'allowance') + payslip.allowance_ids = [(6, 0, allowances.ids)] + deductions = payslip.line_ids.filtered( + lambda a: a.amount != 0 and a.rate != 0 and a.category_id.rule_type == 'deduction') + for d in deductions: + if d.amount > 0: + d.amount = -d.amount + else: + d.amount = d.amount + payslip.deduction_ids = [(6, 0, deductions.ids)] + + # Loans # + loans = self.env['hr.loan.salary.advance'].search([('employee_id', '=', payslip.employee_id.id), + ('request_type.refund_from', '=', 'salary'), + ('state', '=', 'pay')]).filtered( + lambda item: item.employee_id.state == 'open') + if loans: + for loan in loans: + for l in loan.deduction_lines: + if not l.paid and ( + str(l.installment_date) <= str(payslip.date_from) or str(l.installment_date) <= str( + payslip.date_to)): + employee_loan_id = payslip.loan_ids.filtered( + lambda item: item.name == loan.request_type.name) + if not employee_loan_id: + payslip_loans.append({ + 'name': loan.request_type.name, + 'code': loan.code, + 'amount': round((-l.installment_amount), 2), + 'date': l.installment_date, + 'account_id': loan.request_type.account_id.id, + 'loan_id': loan.id + }) + l.paid = True + l.payment_date = payslip.date_to + else: + payslip.loan_ids = [(0, 0, loan_item) for loan_item in payslip_loans] + + # check remaining loan and change state to closed + if loan.remaining_loan_amount <= 0.0 < loan.gm_propos_amount: + loan.state = 'closed' + + payslip.loan_ids = [(0, 0, loan_item) for loan_item in payslip_loans] + payslip.allowance_ids._compute_total() + payslip.deduction_ids._compute_total() + for pay in payslip: + if pay.total_sum < 0: + raise exceptions.Warning(_("Salary is less than 0 this month for the following employees \n %s") % ( + pay.employee_id.name)) + + self.write({'state': 'computed'}) + return True + + # Override function get_payslip_lines + @api.model + def _get_payslip_lines(self, contract_ids, payslip_id): + def _sum_salary_rule_category(localdict, category, amount): + if category.parent_id: + localdict = _sum_salary_rule_category(localdict, category.parent_id, amount) + localdict['categories'].dict[category.code] = category.code in localdict['categories'].dict and \ + localdict['categories'].dict[category.code] + amount or amount + return localdict + + class BrowsableObject(object): + def __init__(self, employee_id, dict, env): + self.employee_id = employee_id + self.dict = dict + self.env = env + + def __getattr__(self, attr): + return attr in self.dict and self.dict.__getitem__(attr) or 0.0 + + class InputLine(BrowsableObject): + """a class that will be used into the python code, mainly for usability purposes""" + + def sum(self, code, from_date, to_date=None): + if to_date is None: + to_date = fields.Date.today() + self.env.cr.execute(""" + SELECT sum(amount) as sum + FROM hr_payslip as hp, hr_payslip_input as pi + WHERE hp.employee_id = %s AND hp.state = 'done' + AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""", + (self.employee_id, from_date, to_date, code)) + return self.env.cr.fetchone()[0] or 0.0 + + class WorkedDays(BrowsableObject): + """a class that will be used into the python code, mainly for usability purposes""" + + def _sum(self, code, from_date, to_date=None): + if to_date is None: + to_date = fields.Date.today() + self.env.cr.execute(""" + SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours + FROM hr_payslip as hp, hr_payslip_worked_days as pi + WHERE hp.employee_id = %s AND hp.state = 'done' + AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""", + (self.employee_id, from_date, to_date, code)) + return self.env.cr.fetchone() + + def sum(self, code, from_date, to_date=None): + res = self._sum(code, from_date, to_date) + return res and res[0] or 0.0 + + def sum_hours(self, code, from_date, to_date=None): + res = self._sum(code, from_date, to_date) + return res and res[1] or 0.0 + + class Payslips(BrowsableObject): + """a class that will be used into the python code, mainly for usability purposes""" + + def sum(self, code, from_date, to_date=None): + if to_date is None: + to_date = fields.Date.today() + self.env.cr.execute("""SELECT sum(case when hp.credit_note = False then (pl.total) else (-pl.total) end) + FROM hr_payslip as hp, hr_payslip_line as pl + WHERE hp.employee_id = %s AND hp.state = 'done' + AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""", + (self.employee_id, from_date, to_date, code)) + res = self.env.cr.fetchone() + return res and res[0] or 0.0 + + # we keep a dict with the result because a value can be overwritten by another rule with the same code + result_dict, rules_dict, worked_days_dict, inputs_dict = {}, {}, {}, {} + blacklist = [] + payslip = self.env['hr.payslip'].browse(payslip_id) + for worked_days_line in payslip.worked_days_line_ids: + worked_days_dict[worked_days_line.code] = worked_days_line + for input_line in payslip.input_line_ids: + inputs_dict[input_line.code] = input_line + + categories = BrowsableObject(payslip.employee_id.id, {}, self.env) + inputs = InputLine(payslip.employee_id.id, inputs_dict, self.env) + worked_days = WorkedDays(payslip.employee_id.id, worked_days_dict, self.env) + payslips = Payslips(payslip.employee_id.id, payslip, self.env) + rules = BrowsableObject(payslip.employee_id.id, rules_dict, self.env) + + baselocaldict = {'categories': categories, 'rules': rules, 'payslip': payslips, 'worked_days': worked_days, + 'inputs': inputs} + # get the ids of the structures on the contracts and their parent id as well + contracts = self.env['hr.contract'].browse(contract_ids) + if len(contracts) == 1 and payslip.struct_id: + structure_ids = list(set(payslip.contract_id.salary_scale.ids)) + else: + structure_ids = contracts.get_all_structures() + # get the rules of the structure and their children + rule_ids = self.env['hr.payroll.structure'].browse(structure_ids).get_all_rules() + # run the rules by sequence + sorted_rule_ids = [id for id, sequence in sorted(rule_ids, key=lambda x: x[1])] + sorted_rules = self.env['hr.salary.rule'].browse(sorted_rule_ids) + + for contract in contracts: + employee = contract.employee_id + localdict = dict(baselocaldict, payslip=payslip, employee=employee, contract=contract) + if contract.contractor_type.salary_type == 'scale': + for rule in sorted_rules: + key1 = rule.code + '-' + str(contract.id) + localdict['result'] = None + localdict['result_qty'] = 1.0 + localdict['result_rate'] = 100 + # check if the rule can be applied + if rule._satisfy_condition(localdict) and rule.id not in blacklist: + if payslip.contract_id.advantages: + for advantage_rule in payslip.contract_id.advantages: + date1 = datetime.strptime(str(advantage_rule.date_from), "%Y-%m-%d") + key = str(advantage_rule.benefits_discounts.code) + '-' + str(contract.id) + if advantage_rule.date_to: + date2 = datetime.strptime(str(advantage_rule.date_to), "%Y-%m-%d") + if (datetime.strptime(str(payslip.date_from), "%Y-%m-%d") >= date1 + and date2 >= datetime.strptime(str(payslip.date_to), "%Y-%m-%d")) \ + or date2 >= datetime.strptime(str(payslip.date_from), "%Y-%m-%d") >= date1 \ + or date2 >= datetime.strptime(str(payslip.date_to), "%Y-%m-%d") >= date1 \ + or (datetime.strptime(str(payslip.date_to), "%Y-%m-%d") >= date1 + and date2 >= datetime.strptime(str(payslip.date_from), "%Y-%m-%d")): + + if advantage_rule.benefits_discounts and advantage_rule.benefits_discounts.name not in sorted_rules: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[ + rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule(localdict)[0] + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + advantage_rule.done = False + else: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + if date2 <= datetime.strptime(str(payslip.date_from), "%Y-%m-%d"): + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + if date1 <= datetime.strptime(str(payslip.date_from), + "%Y-%m-%d") or date1 <= datetime.strptime( + str(payslip.date_to), "%Y-%m-%d"): + if advantage_rule.benefits_discounts.name not in sorted_rules: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + # To Doooooooooooooo #if + # if rule.name == advantage_rule.benefits_discounts.name: + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule(localdict)[ + 0] + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + advantage_rule.write({'done': True}) + else: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + blacklist += [id for id, seq in rule._recursive_search_of_rules()] + + if payslip.contract_id.salary_level.benefits_discounts_ids: + level_structure_ids = list(set(payslip.contract_id.salary_level.ids)) + level_rule_ids = self.env['hr.payroll.structure'].browse(level_structure_ids).get_all_rules() + level_sorted_rule_ids = [id for id, sequence in sorted(level_rule_ids, key=lambda x: x[1])] + level_sorted_rules = self.env['hr.salary.rule'].browse(level_sorted_rule_ids) + for rule in level_sorted_rules: + key = rule.code + '-' + str(contract.id) + localdict['result'] = None + localdict['result_qty'] = 1.0 + localdict['result_rate'] = 100 + # check if the rule can be applied + if rule._satisfy_condition(localdict) and rule.id not in blacklist: + amount, qty, rate = rule._compute_rule(localdict) + if qty == 0: + qty = 1 + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + + for advantage_rule in payslip.contract_id.advantages: + if advantage_rule.benefits_discounts.name == rule.name: + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + if advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule(localdict)[0] + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + blacklist += [id for id, seq in rule._recursive_search_of_rules()] + + if payslip.contract_id.salary_group.benefits_discounts_ids: + group_structure_ids = list(set(payslip.contract_id.salary_group.ids)) + group_rule_ids = self.env['hr.payroll.structure'].browse(group_structure_ids).get_all_rules() + group_sorted_rule_ids = [id for id, sequence in sorted(group_rule_ids, key=lambda x: x[1])] + group_sorted_rules = self.env['hr.salary.rule'].browse(group_sorted_rule_ids) + for rule in group_sorted_rules: + key = rule.code + '-' + str(contract.id) + localdict['result'] = None + localdict['result_qty'] = 1.0 + localdict['result_rate'] = 100 + # check if the rule can be applied + if rule._satisfy_condition(localdict) and rule.id not in blacklist: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + + for advantage_rule in payslip.contract_id.advantages: + if advantage_rule.benefits_discounts.name == rule.name: + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + if advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule(localdict)[0] + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + blacklist += [id for id, seq in rule._recursive_search_of_rules()] + + else: + advantage_list = [] + for advantage_rule in payslip.contract_id.advantages: + advantage_list.append(advantage_rule.benefits_discounts.id) + for rule in sorted_rules: + key1 = rule.code + '-' + str(contract.id) + localdict['result'] = None + localdict['result_qty'] = 1.0 + localdict['result_rate'] = 100 + # check if the rule can be applied + if rule._satisfy_condition(localdict) and rule.id not in blacklist: + if payslip.contract_id.advantages: + for advantage_rule in payslip.contract_id.advantages: + key = advantage_rule.benefits_discounts.code + '-' + str(contract.id) + date1 = datetime.strptime(str(advantage_rule.date_from), "%Y-%m-%d") + if advantage_rule.benefits_discounts.name == rule.name: + if advantage_rule.date_to: + date2 = datetime.strptime(str(advantage_rule.date_to), "%Y-%m-%d") + if (datetime.strptime(str(payslip.date_from), + "%Y-%m-%d") >= date1 and date2 >= datetime.strptime( + str(payslip.date_to), "%Y-%m-%d")) \ + or date2 >= datetime.strptime(str(payslip.date_from), + "%Y-%m-%d") >= date1 \ + or date2 >= datetime.strptime(str(payslip.date_to), "%Y-%m-%d") >= date1 \ + or (datetime.strptime(str(payslip.date_to), + "%Y-%m-%d") >= date1 and date2 >= datetime.strptime( + str(payslip.date_from), "%Y-%m-%d")): + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule(localdict)[ + 0] # update 21/04/2019 + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + if date1 <= datetime.strptime(str(payslip.date_from), "%Y-%m-%d"): + if advantage_rule.benefits_discounts.name in sorted_rules: + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule(localdict)[ + 0] + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + if advantage_rule.benefits_discounts.name not in sorted_rules: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[ + rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif advantage_rule.type == 'exception': + total = \ + advantage_rule.benefits_discounts._compute_rule(localdict)[ + 0] + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[ + advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + amount, qty, rate = rule._compute_rule(localdict) + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + + else: + if rule.id not in advantage_list: + # compute the amount of the rule + amount, qty, rate = rule._compute_rule(localdict) + # check if there is already a rule computed with that code + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + # set/overwrite the amount computed for this rule in the localdict + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + + if advantage_rule.type == 'customize': + amount = advantage_rule.amount + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif advantage_rule.type == 'exception': + total = advantage_rule.benefits_discounts._compute_rule( + localdict)[0] # update 21/04/2019 + if total == advantage_rule.amount or total == 0.0: + amount, qty, rate = 0.0, 0, 0.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[ + advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + elif total <= advantage_rule.amount: + raise UserError( + _( + 'The amount you put is greater than fact value of this Salary rule')) + else: + amount = total - advantage_rule.amount # update 21/04/2019 + qty, rate = 1, 100.0 + previous_amount = advantage_rule.benefits_discounts.code in localdict and \ + localdict[advantage_rule.benefits_discounts.code] or 0.0 + tot_rule = amount * qty * rate / 100.0 + localdict[advantage_rule.benefits_discounts.code] = tot_rule + rules_dict[advantage_rule.benefits_discounts.code] = advantage_rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, + advantage_rule.benefits_discounts.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + result_dict[key] = { + 'salary_rule_id': advantage_rule.benefits_discounts.id, + 'contract_id': contract.id, + 'name': advantage_rule.benefits_discounts.name, + 'code': advantage_rule.benefits_discounts.code, + 'category_id': advantage_rule.benefits_discounts.category_id.id, + 'amount_select': advantage_rule.benefits_discounts.amount_select, + 'amount_fix': advantage_rule.benefits_discounts.amount_fix, + 'amount_python_compute': advantage_rule.benefits_discounts.amount_python_compute, + 'amount_percentage': advantage_rule.benefits_discounts.amount_percentage, + 'register_id': advantage_rule.benefits_discounts.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + advantage_rule.write({'done': True}) + else: + # compute the amount of the rule + amount, qty, rate = rule._compute_rule(localdict) + # check if there is already a rule computed with that code + previous_amount = rule.code in localdict and localdict[rule.code] or 0.0 + # set/overwrite the amount computed for this rule in the localdict + tot_rule = amount * qty * rate / 100.0 + localdict[rule.code] = tot_rule + rules_dict[rule.code] = rule + # sum the amount for its salary category + localdict = _sum_salary_rule_category(localdict, rule.category_id, + tot_rule - previous_amount) + # create/overwrite the rule in the temporary results + if amount != 0: + result_dict[key1] = { + 'salary_rule_id': rule.id, + 'contract_id': contract.id, + 'name': rule.name, + 'code': rule.code, + 'category_id': rule.category_id.id, + 'amount_select': rule.amount_select, + 'amount_fix': rule.amount_fix, + 'amount_python_compute': rule.amount_python_compute, + 'amount_percentage': rule.amount_percentage, + 'register_id': rule.register_id.id, + 'amount': amount, + 'employee_id': contract.employee_id.id, + 'quantity': qty, + 'rate': rate} + else: + blacklist += [id for id, seq in rule._recursive_search_of_rules()] + + return list(result_dict.values()) + + +class HrPayslipLine(models.Model): + _inherit = 'hr.payslip.line' + + leave_request_case = fields.Boolean() + + # Relational fields + payslip_allowance = fields.Many2one('hr.payslip', ondelete='cascade', index=True) + payslip_deduction = fields.Many2one('hr.payslip', ondelete='cascade', index=True) + related_benefits_discounts = fields.Many2many(comodel_name='hr.salary.rule', + relation='payslip_line_benefit_discount_rel', + column1='rule_id', column2='sub_rule_id', + string='Related Benefits and Discount') + percentage = fields.Float(string='Percentage', related='slip_id.percentage') + + # override compute function in payslip lines + @api.depends('quantity', 'amount', 'rate') + def _compute_total(self): + # payslip = self.env['hr.payslip'].search([('id', '=', self.slip_id.id)]) + #print("payslip######################### ", self) + start_time = time.time() + for line in self: + #print('--------------------------hr.payslip.line-------------------', line.id) + if line.slip_id.worked_days_line_ids: + total_days = 0 + per = 0 + days_number = sum(line.number_of_days for line in line.slip_id.worked_days_line_ids) + for wo in line.slip_id.worked_days_line_ids: + + ####################################################### Holidays Unpaid ###################################### + if wo.name == "Unpaid Holidays For this month": + work_days = wo.number_of_days + if total_days: + line.slip_id.total_allowances = 0.0 + line.slip_id.total_deductions = 0.0 + total_days_after_holiday = total_days - work_days + total_days = total_days_after_holiday + + else: + total_days_after_holiday = 30 - work_days + total_days = total_days_after_holiday + + if line.salary_rule_id.reduce_with_leaves == True and line.salary_rule_id.min_leave_days_to_deduct <= days_number: + line.total = round(((line.amount / 30) * total_days_after_holiday) * line.percentage / 100,2) + else: + line.total = round(((line.amount) * line.percentage / 100),2) + # line.total = ((line.amount / 30) * total_days_after_holiday) * line.percentage / 100 + #################################################### Holidays percentage ####################################### + elif wo.name == "Paid Holidays By percentage": + work_days = wo.number_of_days + percentage = wo.number_of_hours + if total_days: + line.slip_id.total_allowances = 0.0 + line.slip_id.total_deductions = 0.0 + total_days_after_holiday = total_days - work_days + total_days = total_days_after_holiday + + else: + total_days_after_holiday = 30 - work_days + total_days = total_days_after_holiday + allow = (line.amount / 30) * work_days + per += (allow * percentage / 100) + actual_allow_tot = (line.amount / 30) * total_days_after_holiday + if line.salary_rule_id.reduce_with_leaves == True and line.salary_rule_id.min_leave_days_to_deduct <= days_number: + line.total = round((actual_allow_tot + per) * line.percentage / 100,2) + else: + line.total = round(((line.amount) * line.percentage / 100),2) + # line.total = (actual_allow_tot + per) * line.percentage / 100 + ##################################################### Holidays Additional ######################################## + elif wo.name == "Additional Paid Holidays": + work_days = wo.number_of_days + if total_days: + line.slip_id.total_allowances = 0.0 + line.slip_id.total_deductions = 0.0 + total_days_after_holiday = total_days - work_days + total_days = total_days_after_holiday + + + else: + total_days_after_holiday = 30 - work_days + total_days = total_days_after_holiday + if line.leave_request_case or line.salary_rule_id.reduce_with_leaves == False or line.salary_rule_id.min_leave_days_to_deduct > days_number: + line.total = round((line.amount) * line.percentage / 100,2) + else: + line.total = round(((line.amount / 30) * total_days_after_holiday) * line.percentage / 100,2) + ################################################### Holidays Reconcile and Exclusion ############################### + elif wo.name == "Exclusion or Reconcile Paid Holidays": + work_days = wo.number_of_days + if total_days: + line.slip_id.total_allowances = 0.0 + line.slip_id.total_deductions = 0.0 + total_days_after_holiday = total_days - work_days + total_days = total_days_after_holiday + + else: + total_days_after_holiday = 30 - work_days + total_days = total_days_after_holiday + if not line.leave_request_case or line.salary_rule_id.reduce_with_leaves == False or line.salary_rule_id.min_leave_days_to_deduct > days_number: + line.total = round((line.amount) * line.percentage / 100,2) + else: + line.total = round(((line.amount / 30) * total_days_after_holiday) * line.percentage / 100,2) + ################################################### Working days for this month ###################################### + else: + work_days = wo.number_of_days + total_days = work_days + if line.leave_request_case or line.salary_rule_id.reduce_with_leaves == False or line.salary_rule_id.min_leave_days_to_deduct > days_number: + line.total = round((line.amount) * line.percentage / 100,2) + else: + line.total = round(((line.amount / 30) * work_days) * line.percentage / 100,2) + ################################################### End IF Then else ################################################# + else: + total = Decimal(line.amount * line.percentage / 100) + rounded = total.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) + line.total = round(rounded,2) + #print("compute_shee_computee_total payslips_Run %s" % (time.time() - start_time)) + + +class HrPayslipRun(models.Model): + _inherit = 'hr.payslip.run' + + required_condition = fields.Boolean(string='Required Condition', compute='compute_type') + state = fields.Selection(selection_add=[('computed', 'Computed'), + ('confirmed', 'Confirmed'), + ('transfered', 'Transfer'), ('close', 'Close')], tracking=True) + + # Relational fields + salary_scale = fields.Many2one('hr.payroll.structure', string='Salary Scale') + employee_ids = fields.Many2many('hr.employee', 'hr_employee_payslip_rel', 'payslip_id', 'employee_id', 'Employees', + index=True,domain="[('id', 'in', employee_value_ids)]") + department_ids = fields.Many2many('hr.department', 'hr_department_payslip_rel', + 'payslip_id', 'department_id', 'Departments') + journal_id = fields.Many2one('account.journal', 'Salary Journal') + + percentage = fields.Float(string='Percentage', default=100) + move_id = fields.Many2one('account.move', string="Move Number") + company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company) + employee_value_ids = fields.Many2many(comodel_name="hr.employee", compute="_compute_allowed_value_ids") + holiday_start_date = fields.Date(string="Holiday Start Date") + holiday_end_date = fields.Date(string="Holiday End Date") + + + @api.onchange('date_start') + def check_date_start(self): + if self.date_start : + self.date_end = fields.Date.from_string(self.date_start)+relativedelta(months=+1, day=1, days=-1) + + @api.onchange('date_end') + def check_date_end(self): + if self.date_start and self.date_end and self.date_start > self.date_end: + raise UserError(_('Date From must be less than or equal Date To')) + month_current_from = datetime.strptime(str(self.date_start), '%Y-%m-%d').strftime('%m') + year_current_from = datetime.strptime(str(self.date_start), '%Y-%m-%d').strftime('%y') + month_current_to = datetime.strptime(str(self.date_end), '%Y-%m-%d').strftime('%m') + year_current_to = datetime.strptime(str(self.date_end), '%Y-%m-%d').strftime('%y') + if month_current_from != month_current_to or year_current_from != year_current_to: + raise exceptions.Warning(_('Sorry, The Pyroll period Must be During the same Month')) + + @api.depends('salary_scale.transfer_type') + def compute_type(self): + if self.salary_scale.transfer_type == 'all' or self.salary_scale.transfer_type == 'per_analytic_account': + self.required_condition = True + else: + self.required_condition = False + + # domain the employee to compute salary + @api.depends('salary_scale','department_ids','date_start') + def _compute_allowed_value_ids(self): + for item in self: + date_from = str(item.date_start) + date_to = str(item.date_end) + if item.salary_scale: + if item.department_ids: + depart = item.department_ids._origin + #for dep in item.department_ids._origin: + employee_contracts = self.env['hr.contract'].search( + [('salary_scale', '=', item.salary_scale.id), + ('state', '=', 'program_directory')]).filtered( + lambda item: item.employee_id.department_id in depart and item.employee_id.state == 'open' + and str(item.employee_id.first_hiring_date) <= date_to) + item.employee_value_ids = employee_contracts.mapped("employee_id") + else: + employee_contracts = self.env['hr.contract'].search( + [('salary_scale', '=', item.salary_scale.id), ('state', '=', 'program_directory')]).filtered( + lambda item: item.employee_id.state == 'open' and str(item.employee_id.first_hiring_date) <= date_to) + + item.employee_value_ids = employee_contracts.mapped("employee_id") + else: + item.employee_value_ids = False + + @api.onchange('salary_scale') + def _onchange_salary_scale_id(self): + for item in self: + if item.salary_scale: + item.employee_ids = False + item.department_ids = False + + @api.onchange('department_ids','date_start') + def _onchange_department_ids(self): + for item in self: + item.employee_ids = False + + '''@api.onchange('salary_scale') + def onchange_salary_scale_id(self): + for item in self: + if item.salary_scale: + item.employee_ids = False + item.department_ids = False + if item.department_ids: + for dep in item.department_ids: + employee_contracts = self.env['hr.contract'].search( + [('salary_scale', '=', item.salary_scale.id), + ('state', '=', 'program_directory')]).filtered( + lambda item: item.employee_id.department_id == dep and item.employee_id.state == 'open') + emps = [] + for contract in employee_contracts: + emps.append(contract.employee_id.id) + return {'domain': {'employee_ids': [('id', 'in', emps)]}} + else: + employee_contracts = self.env['hr.contract'].search( + [('salary_scale', '=', item.salary_scale.id), ('state', '=', 'program_directory')]).filtered( + lambda item: item.employee_id.state == 'open') + emps = [] + for contract in employee_contracts: + emps.append(contract.employee_id.id) + return {'domain': {'employee_ids': [('id', 'in', emps)]}} + else: + return {'domain': {'employee_ids': [('id', 'in', [])]}}''' + + # Override function compute sheet in Payslip Batches + + + def compute_sheet(self): + payslips = self.env['hr.payslip'] + [data] = self.read() + from_date = str(self.date_start) + to_date = str(self.date_end) + holiday_start_date = str(self.holiday_start_date) if self.holiday_start_date else False + holiday_end_date = str(self.holiday_end_date) if self.holiday_end_date else False + start_time = time.time() + + #worked_days, emps, dictionary = [], [], [] + worked_days, emps, dictionary,emp_last_payslip,emp_priv_payslip = [], [], [], [],[] + + if datetime.strptime(str(from_date), "%Y-%m-%d").date().month == datetime.strptime(str(to_date),"%Y-%m-%d").date().month: + month_date = datetime.strptime(str(from_date), "%Y-%m-%d").date() + + if self.department_ids: + for dep in self.department_ids: + employee_contracts = self.env['hr.contract'].search( + [('salary_scale', '=', self.salary_scale.id), ('state', '=', 'program_directory')]).filtered( + lambda item: item.employee_id.department_id == dep and item.employee_id.state == 'open' + and str(item.employee_id.first_hiring_date) <= to_date) + for contract in employee_contracts: + emps.append(contract.employee_id.id) + else: + employee_contracts = self.env['hr.contract'].search( + [('salary_scale', '=', self.salary_scale.id), ('state', '=', 'program_directory')]).filtered( + lambda item: item.employee_id.state == 'open' and str(item.employee_id.first_hiring_date) <= to_date) + for contract in employee_contracts: + emps.append(contract.employee_id.id) + + emps = self.env['hr.employee'].browse(emps) + employees = self.env['hr.employee'].browse(data['employee_ids']) + + ################### Employee run payslip one in month and previously month ####### + last_payslips = self.env['hr.payslip'].search([('date_from', '>=', self.date_start),('date_from', '<=', self.date_end), + ('payslip_run_id', '!=', self.id)]) + if last_payslips: + for priv_pay in last_payslips: + if employees: + for emp in employees: + if emp.id == priv_pay.employee_id.id: + emp_last_payslip.append(emp.name) + else : + for emp in emps: + if emp.id == priv_pay.employee_id.id: + emp_last_payslip.append(emp.name) + if emp_last_payslip: + raise exceptions.Warning( + _("Sorry, Salary has already been calculated This Month for Employees \n %s") % (emp_last_payslip)) + ######################################################################################## + last_month = self.date_start - relativedelta(months=1) + end_last_month = self.date_start - relativedelta(days=1) + priv_payslips_month = self.env['hr.payslip'].search([('date_from', '>=', last_month),('date_from', '<=', end_last_month), + ('payslip_run_id', '!=', self.id)]).employee_id + old_payslips = self.env['hr.payslip'].search([('date_from', '<', self.date_start), + ('payslip_run_id', '!=', self.id)]).employee_id + + if employees: + diff_employee= employees-priv_payslips_month + else: + diff_employee= emps-priv_payslips_month + + #if diff_employee and diff_employee in old_payslips : + if diff_employee : + for emp in diff_employee: + if emp in old_payslips: + emp_priv_payslip.append(emp.name) + if emp_priv_payslip: + raise exceptions.Warning( + _("Sorry, The Previous month's salary has Not been calculated for Employees \n %s") % (emp_priv_payslip)) + + ################### End ################################################################ + if employees: + locale = self.env.context.get('lang') or 'en_US' + for employee in employees: + slip_data = self.env['hr.payslip'].onchange_employee_id(from_date, to_date, employee.id, + contract_id=False) + # pays = self.env['hr.payslip'].search([('contract_id', '=', employee.contract_id.id)]) + ################### Rename Slip and Date####### + ttyme = datetime.fromtimestamp(time.mktime(time.strptime(str(from_date), "%Y-%m-%d"))) + slip_data['value'].update({ + 'name': _('Salary Slip of %s for %s') % ( + employee.name, tools.ustr(babel.dates.format_date(date=ttyme, format='MMMM-y',locale=locale))), + 'company_id': employee.company_id.id, + }) + ########### + employee_slip_line = self.slip_ids.filtered(lambda item: item.employee_id.id == employee.id) + if to_date >= str(employee.first_hiring_date) >= from_date: + contract_start_date = datetime.strptime(str(employee.first_hiring_date), "%Y-%m-%d").date() + if to_date >= str(employee.leaving_date) >= str(employee.first_hiring_date): + contract_end_date = datetime.strptime(str(employee.leaving_date), + "%Y-%m-%d").date() + timedelta( + days=1) + duration = relativedelta(contract_end_date, contract_start_date).days + hours = (float((contract_end_date - contract_start_date).seconds) / 86400) * 24 + if not employee_slip_line: + res = { + 'employee_id': employee.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': employee.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': worked_days, + 'date_from': str(employee.first_hiring_date) or from_date, + 'date_to': employee.leaving_date or to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': employee.company_id.id, + 'percentage': self.percentage} + + item_payslip = self.env['hr.payslip'].create(res) + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != employee and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + if item_payslip: + days = {'name': "Working days for this month", + 'sequence': 1, + 'payslip_id': item_payslip.id, + 'code': 2, + 'number_of_days': duration, + 'number_of_hours': hours, + 'contract_id': employee.contract_id.id} + worked_days += self.env['hr.payslip.worked_days'].create(days) + else: + item_payslip = employee_slip_line + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != employee and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + else: + + from calendar import monthrange + month_range = monthrange(datetime.now().date().year, month_date.month)[1] + contract_end_date = datetime.strptime(str(to_date), "%Y-%m-%d").date() + + if month_range == 30 and contract_end_date.day == 30: + duration = relativedelta(contract_end_date, contract_start_date).days + 1 + + elif month_range > 30 and contract_end_date.day > 30: + duration = relativedelta(contract_end_date, contract_start_date).days + if duration == 0: + duration = duration + 1 + elif month_range == 28 and contract_end_date.day == 28: + duration = relativedelta(contract_end_date, contract_start_date).days + 3 + + elif month_range == 29 and contract_end_date.day == 29: + duration = relativedelta(contract_end_date, contract_start_date).days + 2 + + else: + duration = relativedelta(contract_end_date, contract_start_date).days + 1 + + hours = (float((contract_end_date - contract_start_date).seconds) / 86400) * 24 + if not employee_slip_line: + + + res = { + 'employee_id': employee.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': employee.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + # 'worked_days_line_ids': worked_days , + 'date_from': str(employee.first_hiring_date) or from_date, + 'date_to': to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': employee.company_id.id, + 'percentage': self.percentage} + + item_payslip = self.env['hr.payslip'].create(res) + payslips += item_payslip + + + # if pays: + # for p in pays: + # if p.employee_id != employee and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + + if item_payslip: + # print("22222222222222222222222222222222222222222222222", employee.english_name) + days = {'name': "Working days for this month", + 'sequence': 1, + 'payslip_id': item_payslip.id, + 'code': 2, + 'number_of_days': duration, + 'number_of_hours': hours, + 'contract_id': employee.contract_id.id} + worked_days += self.env['hr.payslip.worked_days'].create(days) + else: + item_payslip = employee_slip_line + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != employee and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + + elif to_date >= str(employee.leaving_date) >= from_date: + contract_start_date = datetime.strptime(str(from_date), "%Y-%m-%d").date() + contract_end_date = datetime.strptime(str(employee.leaving_date), "%Y-%m-%d").date() + timedelta( + days=1) + duration = relativedelta(contract_end_date, contract_start_date).days + hours = (float((contract_end_date - contract_start_date).seconds) / 86400) * 24 + employee_slip_line = self.slip_ids.filtered(lambda item: item.employee_id.id == employee.id) + if not employee_slip_line: + res = { + 'employee_id': employee.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': employee.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': worked_days, + 'date_from': from_date, + 'date_to': employee.leaving_date or to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': employee.company_id.id, + 'percentage': self.percentage} + + item_payslip = self.env['hr.payslip'].create(res) + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != employee and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + if item_payslip: + days = {'name': "Working days for this month", + 'sequence': 1, + 'payslip_id': item_payslip.id, + 'code': 2, + 'number_of_days': duration, + 'number_of_hours': hours, + 'contract_id': employee.contract_id.id} + worked_days += self.env['hr.payslip.worked_days'].create(days) + else: + item_payslip = employee_slip_line + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != employee and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + else: + if not employee_slip_line and str(employee.first_hiring_date) < from_date: + if employee.leaving_date: + if str(employee.leaving_date) <= from_date: + pass + else: + res = { + 'employee_id': employee.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': employee.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': [(0, 0, x) for x in + slip_data['value'].get('worked_days_line_ids')], + 'date_from': from_date, + 'date_to': to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': employee.company_id.id, + 'percentage': self.percentage} + + payslips += self.env['hr.payslip'].create(res) + else: + res = { + 'employee_id': employee.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': employee.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': [(0, 0, x) for x in + slip_data['value'].get('worked_days_line_ids')], + 'date_from': from_date, + 'date_to': to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': employee.company_id.id, + 'percentage': self.percentage} + + payslips += self.env['hr.payslip'].create(res) + else: + if employee.leaving_date and str(employee.first_hiring_date) < from_date: + if str(employee.leaving_date) <= from_date: + pass + else: + payslips += employee_slip_line + else: + payslips += employee_slip_line + else: + for emp in emps: + slip_data = self.env['hr.payslip'].onchange_employee_id(from_date, to_date, emp.id, contract_id=False) + # pays = self.env['hr.payslip'].search( + # [('contract_id', '=', emp.contract_id.id), ('date_from', '>=', self.date_start)]) + ################### Rename Slip and Date####### + ttyme = datetime.fromtimestamp(time.mktime(time.strptime(str(from_date), "%Y-%m-%d"))) + slip_data['value'].update({ + 'name': _('Salary Slip of %s for %s') % ( + emp.name, tools.ustr(babel.dates.format_date(date=ttyme, format='MMMM-y'))), + 'company_id': emp.company_id.id, + }) + ########### + employee_slip_line = self.slip_ids.filtered(lambda item: item.employee_id.id == emp.id) + if to_date >= str(emp.first_hiring_date) >= from_date: + if to_date >= str(emp.leaving_date) >= str(emp.first_hiring_date): + contract_start_date = datetime.strptime(str(emp.first_hiring_date), "%Y-%m-%d").date() + contract_end_date = datetime.strptime(str(emp.leaving_date), "%Y-%m-%d").date() + timedelta( + days=1) + duration = relativedelta(contract_end_date, contract_start_date).days + hours = (float((contract_end_date - contract_start_date).seconds) / 86400) * 24 + if not employee_slip_line: + res = { + 'employee_id': emp.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': emp.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + # 'worked_days_line_ids': worked_days, + 'date_from': emp.first_hiring_date or from_date, + 'date_to': emp.leaving_date or to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': emp.company_id.id, + 'percentage': self.percentage} + item_payslip = self.env['hr.payslip'].create(res) + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + if item_payslip: + days = {'name': "Working days for this month", + 'sequence': 1, + 'payslip_id': item_payslip.id, + 'code': 2, + 'number_of_days': duration, + 'number_of_hours': hours, + 'contract_id': emp.contract_id.id} + worked_days += self.env['hr.payslip.worked_days'].create(days) + else: + item_payslip = employee_slip_line + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + else: + contract_start_date = datetime.strptime(str(emp.first_hiring_date), "%Y-%m-%d").date() + from calendar import monthrange + month_range = monthrange(datetime.now().date().year, month_date.month)[1] + contract_end_date = datetime.strptime(str(to_date), "%Y-%m-%d").date() + + if month_range == 30 and contract_end_date.day == 30: + duration = relativedelta(contract_end_date, contract_start_date).days + 1 + elif month_range > 30 and contract_end_date.day > 30: + duration = relativedelta(contract_end_date, contract_start_date).days + if duration == 0: + duration = duration + 1 + elif month_range == 28 and contract_end_date.day == 28: + duration = relativedelta(contract_end_date, contract_start_date).days + 3 + elif month_range == 29 and contract_end_date.day == 29: + duration = relativedelta(contract_end_date, contract_start_date).days + 2 + else: + duration = relativedelta(contract_end_date, contract_start_date).days + 1 + hours = (float((contract_end_date - contract_start_date).seconds) / 86400) * 24 + if not employee_slip_line: + res = { + 'employee_id': emp.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': emp.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + # 'worked_days_line_ids': worked_days, + 'date_from': emp.first_hiring_date or from_date, + 'date_to': to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': emp.company_id.id, + 'percentage': self.percentage} + item_payslip = self.env['hr.payslip'].create(res) + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + if item_payslip: + days = {'name': "Working days for this month", + 'sequence': 1, + 'payslip_id': item_payslip.id, + 'code': 2, + 'number_of_days': duration, + 'number_of_hours': hours, + 'contract_id': emp.contract_id.id} + worked_days += self.env['hr.payslip.worked_days'].create(days) + else: + item_payslip = employee_slip_line + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + + elif to_date >= str(emp.leaving_date) >= from_date: + contract_start_date = datetime.strptime(str(from_date), "%Y-%m-%d").date() + contract_end_date = datetime.strptime(str(emp.leaving_date), "%Y-%m-%d").date() + timedelta(days=1) + duration = relativedelta(contract_end_date, contract_start_date).days + hours = (float((contract_end_date - contract_start_date).seconds) / 86400) * 24 + if not employee_slip_line: + res = { + 'employee_id': emp.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': emp.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': worked_days, + 'date_from': from_date, + 'date_to': emp.leaving_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': emp.company_id.id, + 'percentage': self.percentage} + item_payslip = self.env['hr.payslip'].create(res) + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + if item_payslip: + days = {'name': "Working days for this month", + 'sequence': 1, + 'payslip_id': item_payslip.id, + 'code': 2, + 'number_of_days': duration, + 'number_of_hours': hours, + 'contract_id': emp.contract_id.id} + worked_days += self.env['hr.payslip.worked_days'].create(days) + else: + item_payslip = employee_slip_line + payslips += item_payslip + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += item_payslip + # else: + # payslips += item_payslip + else: + if not employee_slip_line and str(emp.first_hiring_date) < from_date: + if emp.leaving_date: + if str(emp.leaving_date) <= from_date: + pass + else: + res = { + 'employee_id': emp.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': emp.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': [(0, 0, x) for x in + slip_data['value'].get('worked_days_line_ids')], + 'date_from': from_date, + 'date_to': to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': emp.company_id.id, + 'percentage': self.percentage} + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += self.env['hr.payslip'].create(res) + # else: + payslips += self.env['hr.payslip'].create(res) + else: + res = { + 'employee_id': emp.id, + 'name': slip_data['value'].get('name'), + 'struct_id': self.salary_scale.id, + 'contract_id': emp.contract_id.id, + 'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids')], + 'worked_days_line_ids': [(0, 0, x) for x in + slip_data['value'].get('worked_days_line_ids')], + 'date_from': from_date, + 'date_to': to_date, + 'holiday_start_date': holiday_start_date, + 'holiday_end_date': holiday_end_date, + 'credit_note': self.credit_note, + 'company_id': emp.company_id.id, + 'percentage': self.percentage} + # if pays: + # for p in pays: + # if p.employee_id != emp and p not in payslips: + # payslips += self.env['hr.payslip'].create(res) + # else: + payslips += self.env['hr.payslip'].create(res) + else: + if emp.leaving_date and str(emp.first_hiring_date) < from_date: + if str(emp.leaving_date) <= from_date: + pass + else: + payslips += employee_slip_line + else: + payslips += employee_slip_line + + payslips.compute_sheet() + self.env['hr.payslip.line']._compute_total() + for pay in payslips: + if pay.total_sum < 0: + dictionary.append(pay.employee_id.name) + if dictionary: + raise exceptions.Warning( + _("Salary is less than 0 this month for the following employees \n %s") % (dictionary)) + self.slip_ids = payslips + self.write({'state': 'computed'}) + + def withdraw(self): + for line in self.slip_ids: + payslip = self.env['hr.payslip'].search([('number', '=', line.number)]) + loans = self.env['hr.loan.salary.advance'].search([('employee_id', '=', line.employee_id.id)]) + if line.number == payslip.number: + if line.loan_ids: + for loan in line.loan_ids: + loan.paid = False + if loans: + for i in loans: + if i.id == loan.loan_id.id: + for l in i.deduction_lines: + if loan.date == l.installment_date and loan.paid is False: + l.paid = False + l.payment_date = False + #i.remaining_loan_amount += l.installment_amount + i.get_remaining_loan_amount() + + # check remaining loan and change state to pay + if i.state == 'closed' and i.remaining_loan_amount > 0.0: + i.state = 'pay' + elif i.remaining_loan_amount == 0.0 and i.gm_propos_amount > 0.0: + i.state = 'closed' + for record in payslip: + record.write({'state': 'draft'}) + record.unlink() + self.write({'slip_ids': [(5,)]}) + self.write({'state': 'draft'}) + + def unlink(self): + if any(self.filtered(lambda payslip: payslip.state != 'draft')): + raise UserError(_('You cannot delete a payslip which is not draft!')) + return super(HrPayslipRun, self).unlink() + + def return_button(self): + for line in self.slip_ids: + payslip = self.env['hr.payslip'].search([('state', '=', line.state)]) + if payslip: + if self.move_id: + if self.move_id.state == 'posted': + raise exceptions.Warning( + _('You can not Return account move %s in state not draft') % self.move_id.name) + else: + self.move_id.unlink() + self.move_id = False + + if line.move_id: + if line.move_id.state == 'posted': + raise exceptions.Warning( + _('You can not Return account move %s in state not draft') % line.move_id.name) + else: + line.move_id.unlink() + line.move_id = False + + line.write({'state': 'computed'}) + self.write({'state': 'computed'}) + + def confirm(self): + for line in self.slip_ids: + payslip = self.env['hr.payslip'].search([('state', '=', line.state)]) + if payslip: + line.write({'state': 'confirmed'}) + self.write({'state': 'confirmed'}) + + def merge_lists(self, l1, key, key2): + grupos = it.groupby(sorted(l1, key=itemgetter(key,key2)), key=itemgetter(key, key2)) + res = [] + for v, items in grupos: + new_items = list(items) + analytic_account_id = None + if round(sum(dicc['debit'] for dicc in new_items),2) > 0: + analytic_account_id = self.salary_scale.analytic_account_id.id + res.append({ + 'name': v[0], + 'account_id': v[1], + 'debit': round(sum(dicc['debit'] for dicc in new_items),2), + 'credit': round(sum(dicc2['credit'] for dicc2 in new_items),2), + 'analytic_account_id': analytic_account_id , + }) + return res + + def new_merge_lists(self, l1, key1, key2, key3): + # groups = it.groupby(sorted(l1, key=itemgetter(key)), key=itemgetter(key, key2, key3)) + groups = it.groupby(sorted(l1, key=itemgetter(key1, key2, key3)), key=itemgetter(key1, key2, key3)) + res = [] + for v, items in groups: + new_items = list(items) + res.append({ + 'name': v[0], + 'account_id': v[1], + 'analytic_account_id': v[2], + 'debit': sum(dicc['debit'] for dicc in new_items), + 'credit': sum(dicc2['credit'] for dicc2 in new_items) + }) + return res + + def transfer(self): + list_of_vals = [] + + if self.salary_scale.transfer_type == 'all': + total_of_list = [] + for line in self.slip_ids: + emp_type = line.employee_id.employee_type_id.id + total_allow, total_ded, total_loan = 0.0, 0.0, 0.0 + total_list = [] + total_loan_list = [] + move_vals = dict() + journal = self.journal_id + if list_of_vals: + for item in list_of_vals: + if item.get('move') == journal.id: + move_vals = item + break + for l in line.allowance_ids: + amount_allow = l.total + # account = l.salary_rule_id.rule_debit_account_id + account = l.salary_rule_id.get_debit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Allowance %s is Not account Set') % l.name) + total_list.append({ + 'name': l.name, + 'debit': amount_allow, + 'journal_id': journal.id, + 'credit': 0, + 'account_id': account, + }) + total_allow += amount_allow + + for ded in line.deduction_ids: + amount_ded = -ded.total + # account = ded.salary_rule_id.rule_credit_account_id + account = ded.salary_rule_id.get_credit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Deduction %s is Not account Set') % ded.name) + total_list.append({ + 'name': ded.name, + 'credit': amount_ded, + 'journal_id': journal.id, + 'debit': 0, + 'account_id': account, + }) + total_ded += amount_ded + + for lo in line.loan_ids: + amount_loans = -lo.amount + if not lo.account_id: + raise exceptions.Warning( + _('Sorry The Loan %s is Not account Set') % lo.name) + loan_line_vals = { + 'name': lo.name, + 'credit': amount_loans, + 'debit': 0, + 'journal_id': journal.id, + 'account_id': lo.account_id.id, + 'partner_id': line.employee_id.user_id.partner_id.id, + } + total_loan_list.append(loan_line_vals) + total_loan += amount_loans + + # credit_loans_vals = { + # 'name': lo.name, + # 'credit': amount_loans, + # 'journal_id': journal.id, + # 'debit': 0, + # 'account_id': lo.account_id.id, + # } + # total_list.append(credit_loans_vals) + # create line for total of all allowance, deduction, loans of all employees + total_of_list.append({ + 'name': "Total", + 'journal_id': journal.id, + 'partner_id': line.employee_id.user_id.partner_id.id, + 'account_id': journal.default_account_id.id, + 'credit': round(total_allow, 2) - round(total_ded, 2) - round(total_loan, 2), + 'debit': 0, + }) + if not move_vals: + move_vals.update({'move': journal.id, 'list_ids': total_list, 'loans': total_loan_list}) + list_of_vals.append(move_vals) + else: + new_list = move_vals.get('list_ids') + new_list.extend(total_list) + new_loan_list = move_vals.get('loans') + new_loan_list.extend(total_loan_list) + move_vals.update({'list_ids': new_list, 'loans': new_loan_list}) + + for record in list_of_vals: + new_record_list = record.get('list_ids') + [d for d in total_of_list if + d['journal_id'] == record.get('move')] + new_rec_loan_list = record.get('loans') + + merged_list = self.merge_lists(new_record_list, 'name', 'account_id') + record_final_item = merged_list + new_rec_loan_list + if not self.move_id: + move = self.env['account.move'].create({ + 'state': 'draft', + 'journal_id': record.get('move'), + # 'date': fields.Date.context_today(self), + 'date': self.date_end, + 'ref': self.name, + 'line_ids': [(0, 0, item) for item in record_final_item], + 'res_model': 'hr.payslip.run', + 'res_id': self.id + }) + self.move_id = move.id + + + ########################## per_analytic_account ########### + + elif self.salary_scale.transfer_type == 'per_analytic_account': + total_of_list = [] + journal = self.journal_id + department_totals = {} # Dictionary to store department-wise totals + total_allow, total_ded, total_loan = 0.0, 0.0, 0.0 + for line in self.slip_ids: + emp_type = line.employee_id.employee_type_id.id + total_list = [] + total_loan_list = [] + move_vals = dict() + journal = self.journal_id + if list_of_vals: + for item in list_of_vals: + if item.get('move') == journal.id: + move_vals = item + break + for l in line.allowance_ids: + amount_allow = l.total + # account = l.salary_rule_id.rule_debit_account_id + account = l.salary_rule_id.get_debit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Allowance %s is Not account Set') % l.name) + total_list.append({ + 'name': l.name, + 'debit': amount_allow, + 'journal_id': journal.id, + 'credit': 0, + 'account_id': account, + 'analytic_account_id': line.employee_id.department_id.analytic_account_id.id, + }) + total_allow += amount_allow + for ded in line.deduction_ids: + amount_ded = -ded.total + account = ded.salary_rule_id.get_credit_account_id(emp_type) + # account = ded.salary_rule_id.rule_credit_account_id + if not account: + raise exceptions.Warning( + _('Sorry The Deduction %s is Not account Set') % ded.name) + total_list.append({ + 'name': ded.name, + 'credit': amount_ded, + 'journal_id': journal.id, + 'debit': 0, + 'account_id': account, + 'analytic_account_id': None, + }) + total_ded += amount_ded + + for lo in line.loan_ids: + amount_loans = -lo.amount + if not lo.account_id: + raise exceptions.Warning( + _('Sorry The Loan %s is Not account Set') % lo.name) + credit_loans_vals = { + 'name': lo.name, + 'credit': amount_loans, + 'journal_id': journal.id, + 'debit': 0, + 'account_id': lo.account_id.id, + 'analytic_account_id': None, + 'partner_id': line.employee_id.user_id.partner_id.id, + } + total_loan += amount_loans + total_loan_list.append(credit_loans_vals) + + # Get the department of the employee + department = line.employee_id.department_id + + # Add allowance amount to the department total + if department in department_totals: + department_totals[department] += total_allow + else: + department_totals[department] = total_allow + if not move_vals: + move_vals.update({'move': journal.id, 'list_ids': total_list, 'loans': total_loan_list}) + list_of_vals.append(move_vals) + else: + new_list = move_vals.get('list_ids') + new_list.extend(total_list) + new_loan_list = move_vals.get('loans') + new_loan_list.extend(total_loan_list) + move_vals.update({'list_ids': new_list, 'loans': new_loan_list}) + + total_of_list.append({ + 'name': "Total", + 'journal_id': journal.id, + 'account_id': journal.default_account_id.id, + 'credit': total_allow - total_ded - total_loan, + 'debit': 0, + 'analytic_account_id': None, + + }) + + # for department, allowance_total in department_totals.items(): + + # total_of_list.append({ + # 'name': f"Total ({department.analytic_account_id.name})", + # 'journal_id': journal.id, + # 'account_id': journal.default_credit_account_id.id, + # 'credit': allowance_total - total_ded - total_loan, + # 'analytic_account_id': department.analytic_account_id.id, + # 'debit': 0, + # }) + + for record in list_of_vals: + new_record_list = record.get('list_ids') + [d for d in total_of_list if + d['journal_id'] == record.get('move')] + new_rec_loan_list = record.get('loans') + merged_list = self.new_merge_lists(new_record_list, 'name', 'account_id', 'analytic_account_id') + record_final_item = merged_list + new_rec_loan_list + if not self.move_id: + move = self.env['account.move'].create({ + 'state': 'draft', + 'journal_id': record.get('move'), + 'date': self.date_end, + + 'ref': self.name, + 'line_ids': [(0, 0, item) for item in record_final_item], + 'res_model': 'hr.payslip.run', + 'res_id': self.id + }) + self.move_id = move.id + + # import pdb + # pdb.set_trace() + ##################################### + + elif self.salary_scale.transfer_type == 'one_by_one': + for line in self.slip_ids: + emp_type = line.employee_id.employee_type_id.id + total_allow, total_ded, total_loan = 0.0, 0.0, 0.0 + total_list = [] + move_vals = dict() + journal = line.contract_id.journal_id + if not journal: + raise exceptions.Warning( + _('Sorry The Contract Employee %s is Not Journal Set') % line.employee_id.name) + for l in line.allowance_ids: + amount_allow = l.total + # account = l.salary_rule_id.rule_debit_account_id + account = l.salary_rule_id.get_debit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Allowance %s is Not account Set') % l.name) + total_list.append({ + 'name': l.name, + 'debit': amount_allow, + 'partner_id': line.employee_id.user_id.partner_id.id, + 'credit': 0, + 'account_id': account, + 'analytic_account_id': line.employee_id.contract_id.analytic_account_id.id, + }) + total_allow += amount_allow + + for ded in line.deduction_ids: + amount_ded = -ded.total + # account = ded.salary_rule_id.rule_credit_account_id + account = ded.salary_rule_id.get_credit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Deduction %s is Not account Set') % ded.name) + total_list.append({ + 'name': ded.name, + 'credit': amount_ded, + 'partner_id': line.employee_id.user_id.partner_id.id, + 'debit': 0, + 'account_id': account, + }) + total_ded += amount_ded + + for lo in line.loan_ids: + amount_loans = -lo.amount + if not lo.account_id: + raise exceptions.Warning( + _('Sorry The Loan %s is Not account Set') % lo.name) + credit_loans_vals = { + 'name': lo.name, + 'credit': amount_loans, + 'partner_id': line.employee_id.user_id.partner_id.id, + 'debit': 0, + 'account_id': lo.account_id.id, + } + total_loan += amount_loans + total_list.append(credit_loans_vals) + # create line for total of all allowance, deduction, loans of all employees + total = total_allow - total_ded - total_loan + total_list.append({ + 'name': line.name, + 'partner_id': line.employee_id.user_id.partner_id.id, + 'account_id': line.contract_id.journal_id.default_account_id.id, + 'credit': total, + 'debit': 0, + }) + # print('total list', total_list) + if not move_vals: + move_vals.update({'move': journal.id, 'list_ids': total_list}) + list_of_vals.append(move_vals) + else: + new_list = move_vals.get('list_ids') + new_list.extend(total_list) + move_vals.update({'list_ids': new_list}) + for record in list_of_vals: + new_record_list = record.get('list_ids') + # merged_list = self.merge_lists(new_record_list, 'name', 'account_id') + # record_final_item = merged_list + if not line.move_id: + move = self.env['account.move'].create({ + 'state': 'draft', + 'partner_id': line.employee_id.user_id.partner_id.id, + 'journal_id': line.contract_id.journal_id.id, + # 'date': fields.Date.context_today(self), + 'date': self.date_end, + 'ref': line.name, + 'line_ids': [(0, 0, item) for item in new_record_list], + 'res_model': 'hr.payslip.run', + 'res_id': self.id + }) + line.move_id = move.id + + else: + bank_id = '' + for line in self.slip_ids: + emp_type = line.employee_id.employee_type_id.id + total_allow, total_ded, total_loan = 0.0, 0.0, 0.0 + total_list = [] + total_loan_list = [] + move_vals = dict() + if line.employee_id.payment_method == 'bank': + journal = self.env['account.journal'].search([('type', '=', line.employee_id.payment_method), + ], limit=1) + + if not journal: + raise UserError('Error', ' There is no journal For that Bank..' + ' Please define a sale journal') + if list_of_vals: + for item in list_of_vals: + if item.get('move') == journal.id: + move_vals = item + break + for l in line.allowance_ids: + amount_allow = l.total + # account = l.salary_rule_id.rule_debit_account_id + account = l.salary_rule_id.get_debit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Allowance %s is Not account Set') % l.name) + total_list.append({ + 'name': l.name, + 'debit': amount_allow, + 'journal_id': journal.id, + 'credit': 0, + 'account_id': account, + }) + total_allow += amount_allow + + for ded in line.deduction_ids: + amount_ded = -ded.total + # account = ded.salary_rule_id.rule_credit_account_id + account = ded.salary_rule_id.get_credit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Deduction %s is Not account Set') % ded.name) + total_list.append({ + 'name': ded.name, + 'credit': amount_ded, + 'journal_id': journal.id, + 'debit': 0, + 'account_id': account, + }) + total_ded += amount_ded + + for lo in line.loan_ids: + amount_loans = -lo.amount + if not lo.account_id: + raise exceptions.Warning( + _('Sorry The Loan %s is Not account Set') % lo.name) + credit_loans_vals = { + 'name': lo.name, + 'credit': amount_loans, + 'journal_id': journal.id, + 'debit': 0, + 'account_id': lo.account_id.id, + 'partner_id': line.employee_id.user_id.partner_id.id, + } + total_loan += amount_loans + total_loan_list.append(credit_loans_vals) + # create line for total of all allowance, deduction, loans of all employees + total_list.append({ + 'name': "Total", + 'journal_id': journal.id, + 'account_id': journal.default_account_id.id, + 'credit': total_allow - total_ded - total_loan, + 'debit': 0, + }) + if not move_vals: + move_vals.update({'move': journal.id, 'list_ids': total_list, 'loans': total_loan_list}) + list_of_vals.append(move_vals) + else: + new_list = move_vals.get('list_ids') + new_list.extend(total_list) + new_loan_list = move_vals.get('loans') + new_loan_list.extend(total_loan_list) + move_vals.update({'list_ids': new_list, 'loans': new_loan_list}) + bank_id = line.employee_id.bank_account_id.bank_id.name + + elif line.employee_id.payment_method == 'cash': + amount, amount1, amount2 = 0.0, 0.0, 0.0 + + for l in line.allowance_ids: + amount_allow = l.total + # account = l.salary_rule_id.rule_debit_account_id + account = l.salary_rule_id.get_debit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Allowance %s is Not account Set') % l.name) + total_list.append({ + 'name': l.name, + 'account_id': account, + 'debit': amount_allow, + 'credit': 0, + 'partner_id': line.employee_id.user_id.partner_id.id + }) + amount += amount_allow + total_allow += amount + + for ded in line.deduction_ids: + amount_ded = -ded.total + # account = ded.salary_rule_id.rule_credit_account_id + account = ded.salary_rule_id.get_credit_account_id(emp_type) + if not account: + raise exceptions.Warning( + _('Sorry The Deduction %s is Not account Set') % ded.name) + total_list.append({ + 'name': ded.name, + 'account_id': account, + 'credit': amount_ded, + 'debit': 0, + 'partner_id': line.employee_id.user_id.partner_id.id + }) + amount1 += amount_ded + total_ded += amount1 + + for lo in line.loan_ids: + amount_loans = -lo.amount + if not lo.account_id: + raise exceptions.Warning( + _('Sorry The Loan %s is Not account Set') % lo.name) + total_list.append({ + 'name': lo.name, + 'account_id': lo.account_id.id, + 'credit': amount_loans, + 'debit': 0, + 'partner_id': line.employee_id.user_id.partner_id.id + }) + amount2 += amount_loans + total_loan += amount2 + + # create line for total of all allowance, deduction, loans of one employee + total = total_allow - total_ded - total_loan + total_list.append({ + 'name': "Total", + 'account_id': line.contract_id.journal_id.default_account_id.id, + 'partner_id': line.employee_id.user_id.partner_id.id, + 'credit': round(total, 1), + 'debit': 0, + }) + move = self.env['account.move'].create({ + 'journal_id': line.contract_id.journal_id.id, + # 'date': fields.Date.context_today(self), + 'date': self.date_end, + 'ref': "Cash", + 'line_ids': [(0, 0, item) for item in total_list] + }) + + for record in list_of_vals: + new_record_list = record.get('list_ids') + new_rec_loan_list = record.get('loans') + merged_list = self.merge_lists(new_record_list, 'name', 'account_id') + record_final_item = merged_list + new_rec_loan_list + if not line.move_id: + move = self.env['account.move'].create({ + 'state': 'draft', + 'journal_id': record.get('move'), + # 'date': fields.Date.context_today(self), + 'date': self.date_end, + 'ref': bank_id, + 'line_ids': [(0, 0, item) for item in record_final_item], + 'res_model': 'hr.payslip.run', + 'res_id': self.id + }) + line.move_id = move.id + + for line in self.slip_ids: + payslip = self.env['hr.payslip'].search([('state', '=', line.state)]) + if payslip: + line.write({'state': 'transfered'}) + self.write({'state': 'transfered'}) diff --git a/odex30_base/exp_payroll_custom/models/hr_contract.py b/odex30_base/exp_payroll_custom/models/hr_contract.py new file mode 100644 index 0000000..33ff16f --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_contract.py @@ -0,0 +1,349 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class HrContractSalaryScale(models.Model): + _inherit = 'hr.contract' + + salary_level = fields.Many2one(comodel_name='hr.payroll.structure', domain=[('id', 'in', [])]) + salary_scale = fields.Many2one(comodel_name='hr.payroll.structure', domain=[('id', 'in', [])], index=True) + salary_group = fields.Many2one(comodel_name='hr.payroll.structure', domain=[('id', 'in', [])]) + salary_degree = fields.Many2one(comodel_name='hr.payroll.structure', domain=[('id', 'in', [])]) + hide = fields.Boolean(string='Hide', compute="compute_type") + required_condition = fields.Boolean(string='Required Condition', compute='compute_move_type') + total_allowance = fields.Float(string='Total Allowance', compute='compute_function',store=True) + total_deduction = fields.Float(string='Total Deduction', compute='compute_function',store=True) + total_net = fields.Float(string='Total Net', compute='compute_function',store=True) + advantages = fields.One2many('contract.advantage', 'contract_advantage_id', string='Advantages') + house_allowance_temp = fields.Float(string='House Allowance', compute='compute_function',store=True) + transport_allowance = fields.Float(string='Transport Allowance', compute='compute_function',store=True) + + @api.constrains('advantages', 'salary', 'salary_group') + def amount_constrains(self): + for rec in self: + localdict = dict(employee=rec.employee_id.id, contract=rec.env['hr.contract'].search([ + ('employee_id', '=', rec.employee_id.id)])) + if rec.salary_group.gread_max > 0 and rec.salary_group.gread_min > 0: + if rec.salary > rec.salary_group.gread_max or rec.salary < rec.salary_group.gread_min: + raise UserError(_('The Basic Salary Is Greater Than Group Gread Max Or less than Gread Min')) + for item in self.advantages: + item.to_get_contract_id() + if item.benefits_discounts._compute_rule(localdict)[0] < item.amount and item.type == 'exception': + raise UserError(_( + 'The amount you put is greater than fact value of this Salary rule %s (%s).') % ( + item.benefits_discounts.name, item.benefits_discounts.code)) + + @api.depends('salary_scale.transfer_type') + def compute_move_type(self): + self.compute_function() + if self.salary_scale.transfer_type == 'one_by_one': + self.required_condition = True + else: + self.required_condition = False + + @api.depends('salary_scale', 'salary_level', 'salary_group', 'salary_degree','salary','advantages','house_allowance_temp','transport_allowance','total_deduction','salary_insurnce','total_allowance','state') + def compute_function(self): + for item in self: + item.house_allowance_temp = 0 + item.transport_allowance = 0 + item.total_net = 0 + contract = self.env['hr.contract'].search([('employee_id', '=', item.employee_id.id)]) + localdict = dict(employee=item.employee_id.id, contract=contract) + current_date = datetime.now().date() + + # customize type in advantages + allowance_customize_items = item.advantages.filtered( + lambda key: key.type == 'customize' and key.out_rule is False and + key.benefits_discounts.category_id.rule_type == 'allowance' and + (datetime.strptime(str(key.date_to), "%Y-%m-%d").date() if key.date_to else current_date) + >= current_date >= datetime.strptime(str(key.date_from), "%Y-%m-%d").date()) + + allow_sum_custom = sum(x.amount for x in allowance_customize_items) + for x in allowance_customize_items: + if x.benefits_discounts.rules_type == 'house': + item.house_allowance_temp += x.amount + + if x.benefits_discounts.rules_type == 'transport': + item.transport_allowance += x.amount + # allow_custom_ids = [record.benefits_discounts.id for record in allowance_customize_items] + + deduction_customize_items = item.advantages.filtered( + lambda key: key.type == 'customize' and key.out_rule is False and + key.benefits_discounts.category_id.rule_type == 'deduction' and + (datetime.strptime(str(key.date_to), "%Y-%m-%d").date() if key.date_to else current_date) + >= current_date >= datetime.strptime(str(key.date_from), "%Y-%m-%d").date()) + + ded_sum_custom = sum(x.amount for x in deduction_customize_items) + ded_custom_ids = [record.benefits_discounts.id for record in deduction_customize_items] + + # exception type in advantages + exception_items = item.advantages.filtered(lambda key: key.type == 'exception') + total_rule_result, sum_except, sum_customize_expect = 0.0, 0.0, 0.0 + + for x in exception_items: + rule_result = x.benefits_discounts._compute_rule(localdict)[0] + if x.date_from >= str(current_date): + total_rule_result = rule_result + elif str(current_date) > x.date_from: + if x.date_to and str(current_date) <= x.date_to: + total_rule_result = rule_result - x.amount + elif x.date_to and str(current_date) >= x.date_to: + total_rule_result = 0 # rule_result + elif not x.date_to: + total_rule_result = rule_result - x.amount + else: + if rule_result > x.amount: + total_rule_result = rule_result - x.amount + + if total_rule_result: + if x.benefits_discounts.category_id.rule_type == 'allowance': + sum_customize_expect += total_rule_result + if x.benefits_discounts.rules_type == 'house': + item.house_allowance_temp += total_rule_result - x.amount + else: + sum_except += total_rule_result + + if exception_items: + exception_items = item.advantages.filtered( + lambda key: (datetime.strptime(str(key.date_to), + "%Y-%m-%d").date().month if key.date_to else current_date.month) + >= current_date.month >= datetime.strptime(str(key.date_from), "%Y-%m-%d").date().month) + + except_ids = [record.benefits_discounts.id for record in exception_items] + + rule_ids = item.salary_scale.rule_ids.filtered( + lambda key: key.id not in ded_custom_ids and key.id not in except_ids) + + level_rule_ids = item.salary_level.benefits_discounts_ids.filtered(lambda key: key.id not in except_ids) + # key.id not in allow_custom_ids and key.id not in ded_custom_ids and + + group_rule_ids = item.salary_group.benefits_discounts_ids.filtered(lambda key: key.id not in except_ids) + # key.id not in allow_custom_ids and key.id not in ded_custom_ids and + + total_allowance = 0 + total_ded = 0 + for line in rule_ids: + if line.category_id.rule_type == 'allowance': + try: + total_allowance += line._compute_rule(localdict)[0] + except: + total_allowance += 0 + + if line.category_id.rule_type == 'deduction': + try: + total_ded += line._compute_rule(localdict)[0] + except: + total_ded += 0 + + + if line.rules_type == 'house': + item.house_allowance_temp += line._compute_rule(localdict)[0] + if line.rules_type == 'transport': + item.transport_allowance += line._compute_rule(localdict)[0] + + item.total_allowance = total_allowance + item.total_deduction = -total_ded + + if item.salary_level: + total_allowance = 0 + total_deduction = 0 + for line in level_rule_ids: + if line.category_id.rule_type == 'allowance': + try: + total_allowance += line._compute_rule(localdict)[0] + except: + total_allowance += 0 + elif line.category_id.rule_type == 'deduction': + try: + total_deduction += line._compute_rule(localdict)[0] + except: + total_deduction += 0 + + item.total_allowance += total_allowance + item.total_deduction += -total_deduction + + if item.salary_group: + total_allowance = 0 + total_deduction = 0 + for line in group_rule_ids: + if line.category_id.rule_type == 'allowance': + total_allowance += line._compute_rule(localdict)[0] + elif line.category_id.rule_type == 'deduction': + total_deduction += line._compute_rule(localdict)[0] + + item.total_allowance += total_allowance + item.total_deduction += -total_deduction + + item.total_allowance += allow_sum_custom + item.total_allowance += sum_customize_expect + item.total_deduction += -ded_sum_custom + item.total_deduction += -sum_except + item.total_net = item.total_allowance + item.total_deduction + + # filter salary_level,salary_group,salary_degree + + @api.onchange('salary_scale') + def onchange_salary_scale(self): + for item in self: + if item.salary_scale: + level_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'level')]) + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'group')]) + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'degree')]) + return {'domain': {'salary_level': [('id', 'in', level_ids.ids)], + 'salary_group': [('id', 'in', group_ids.ids)], + 'salary_degree': [('id', 'in', degree_ids.ids)]}} + else: + item.total_allowance = 0.0 + item.total_deduction = 0.0 + item.total_net = 0.0 + return {'domain': {'salary_level': [('id', 'in', [])], + 'salary_group': [('id', 'in', [])], + 'salary_degree': [('id', 'in', [])]}} + + # filter depend on salary_level + + @api.onchange('salary_level') + def onchange_salary_level(self): + for item in self: + if item.salary_level: + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_level_id', '=', item.salary_level.id), ('type', '=', 'group')]) + return {'domain': {'salary_group': [('id', 'in', group_ids.ids)], + 'salary_degree': [('id', 'in', [])]}} + else: + return {'domain': {'salary_group': [('id', 'in', [])], + 'salary_degree': [('id', 'in', [])]}} + + # filter depend on salary_group + + @api.onchange('salary_group') + def onchange_salary_group(self): + for item in self: + if item.salary_group: + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_group_id', '=', item.salary_group.id), ('type', '=', 'degree')]) + return {'domain': {'salary_degree': [('id', 'in', degree_ids.ids)]}} + else: + return {'domain': {'salary_degree': [('id', 'in', [])]}} + + @api.depends('salary_degree') + def _get_amount(self): + for record in self: + record.transport_allowance_temp = record.transport_allowance * record.wage / 100 \ + if record.transport_allowance_type == 'perc' else record.transport_allowance + record.house_allowance_temp = record.house_allowance * record.wage / 100 \ + if record.house_allowance_type == 'perc' else record.house_allowance + record.communication_allowance_temp = record.communication_allowance * record.wage / 100 \ + if record.communication_allowance_type == 'perc' else record.communication_allowance + record.field_allowance_temp = record.field_allowance * record.wage / 100 \ + if record.field_allowance_type == 'perc' else record.field_allowance + record.special_allowance_temp = record.special_allowance * record.wage / 100 \ + if record.special_allowance_type == 'perc' else record.special_allowance + record.other_allowance_temp = record.other_allowance * record.wage / 100 \ + if record.other_allowance_type == 'perc' else record.other_allowance + + @api.depends('contractor_type.salary_type') + def compute_type(self): + if self.contractor_type.salary_type == 'scale': + self.hide = True + else: + self.hide = False + + +class Advantages(models.Model): + _name = 'contract.advantage' + _rec_name = 'benefits_discounts' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + date_from = fields.Date(string='Date From') + date_to = fields.Date(string='Date To') + amount = fields.Float(string='Amount', tracking=True) + type = fields.Selection(selection=[('customize', _('Customize')), + ('exception', _('Exception'))], string='Type', default="customize") + # to link employee move line from official mission to advantages line in contract + official_mission_id = fields.Boolean(string="Official Mission", default=False) + # To link employee move line from over time to advantages line in contract + over_time_id = fields.Boolean(string="OverTime", default=False) + # To link employee move line from employee reward to advantages line in contract + reward_id = fields.Boolean(string="Reward", default=False) + penalty_id = fields.Boolean(string='Penalty Name', default=False) + + # Relational fields + benefits_discounts = fields.Many2one(comodel_name='hr.salary.rule', string='Benefits/Discounts') + contract_advantage_id = fields.Many2one('hr.contract') + done = fields.Boolean(string="Done in Payroll") + out_rule = fields.Boolean(string="Out of Payroll", default=True) + + employee_id = fields.Many2one('hr.employee', 'Employee Name', domain=[('state', '=', 'open')], tracking=True) + state = fields.Selection(selection=[('draft', _('Draft')), ('confirm', _('Confirmed')), + ('refused', _('Refused'))], + default='draft', tracking=True) + + comments = fields.Text(string='Comments') + payroll_month = fields.Text(string='Payroll Month', tracking=True) + company_id = fields.Many2one('res.company', string='Company', related='employee_id.company_id', store=True) + + @api.constrains('date_from', 'date_to', 'amount') + def _chick_date(self): + for rec in self: + if rec.date_to: + if rec.date_to <= rec.date_from: + raise UserError(_('The Date Form Must be Less than Date To')) + if rec.amount <= 0: + raise UserError(_('The Amount Must be Greater Than Zero The Employee %s')% rec.employee_id.name) + + def confirm(self): + self.state = 'confirm' + + def refused(self): + self.state = 'refused' + + def draft(self): + for rec in self: + rec.state = 'draft' + + @api.onchange('employee_id') + def to_get_contract_id(self): + contract_id = self.employee_id.contract_id + self.employee_id = self.contract_advantage_id.employee_id.id + if contract_id: + self.contract_advantage_id = contract_id.id + self.employee_id = self.contract_advantage_id.employee_id.id + else: + return False + + def unlink(self): + for item in self: + if item.state != 'draft': + raise UserError(_('You cannot delete The Salary rule %s For the Employee %s is Not Draft') % ( + item.benefits_discounts.name, item.employee_id.name)) + # if item.done == True: + # raise UserError(_('Sorry, The Salary rule %s For the Employee %s is Already Computed in Payroll') % ( + # item.benefits_discounts.name, item.employee_id.name)) + return super(Advantages, self).unlink() + + @api.constrains('employee_id', 'date_from', 'date_to', 'benefits_discounts') + def check_rule_dates(self): + """ Function Can Not add Same Advantage at The Same Month + same employee. + """ + for rec in self: + domain = [ + ('date_from', '<=', rec.date_to), + ('date_to', '>=', rec.date_from), + ('employee_id', '=', rec.employee_id.id), + ('id', '!=', rec.id), + ('benefits_discounts', '=', rec.benefits_discounts.id), ] + advantages_id = self.search_count(domain) + if advantages_id: + # for adv in advantages_id: + raise UserError( + _('You Can Not add Same Allowance/Deduction at The Same Employee %s For The Same Month!') + % rec.employee_id.name) diff --git a/odex30_base/exp_payroll_custom/models/hr_employee.py b/odex30_base/exp_payroll_custom/models/hr_employee.py new file mode 100644 index 0000000..e206ac3 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_employee.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields + + +class HrEmployeeSalaryScale(models.Model): + _inherit = 'hr.employee' + + salary_scale = fields.Many2one(related='contract_id.salary_scale', string='Salary scale', store=True) + salary_level = fields.Many2one(related='contract_id.salary_level', string='Salary Level', store=True) + salary_group = fields.Many2one(related='contract_id.salary_group', string='Salary Group', store=True) + salary_degree = fields.Many2one(related='contract_id.salary_degree', string='Salary Degree', store=True) diff --git a/odex30_base/exp_payroll_custom/models/hr_recontract.py b/odex30_base/exp_payroll_custom/models/hr_recontract.py new file mode 100644 index 0000000..0965fad --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_recontract.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api + + +class HrReContract(models.Model): + _inherit = 'hr.re.contract' + + salary_scale = fields.Many2one('hr.payroll.structure', string='Salary Scale', compute='_get_employee_data') + salary_group = fields.Many2one('hr.payroll.structure', string='Salary Group', compute='_get_employee_data') + salary_level = fields.Many2one('hr.payroll.structure', string='Salary Level', compute='_get_employee_data') + salary_degree = fields.Many2one('hr.payroll.structure', string='Salary Degree', compute='_get_employee_data') + new_salary_scale = fields.Many2one(comodel_name='hr.payroll.structure') + new_salary_level = fields.Many2one(comodel_name='hr.payroll.structure') + new_salary_group = fields.Many2one(comodel_name='hr.payroll.structure') + new_salary_degree = fields.Many2one(comodel_name='hr.payroll.structure') + + @api.onchange('new_salary_scale') + def onchange_new_salary_scale(self): + for item in self: + if item.new_salary_scale: + level_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.new_salary_scale.id), ('type', '=', 'level')]) + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.new_salary_scale.id), ('type', '=', 'group')]) + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.new_salary_scale.id), ('type', '=', 'degree')]) + return {'domain': {'new_salary_level': [('id', 'in', level_ids.ids)], + 'new_salary_group': [('id', 'in', group_ids.ids)], + 'new_salary_degree': [('id', 'in', degree_ids.ids)]}} + else: + item.new_salary = 0.0 + return {'domain': {'new_salary_level': [('id', 'in', [])], + 'new_salary_group': [('id', 'in', [])], + 'new_salary_degree': [('id', 'in', [])]}} + + # filter depend on new_salary_level + + @api.onchange('new_salary_level') + def onchange_new_salary_level(self): + for item in self: + if item.new_salary_level: + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_level_id', '=', item.new_salary_level.id), ('type', '=', 'group')]) + return {'domain': {'new_salary_group': [('id', 'in', group_ids.ids)], + 'new_salary_degree': [('id', 'in', [])]}} + else: + return {'domain': {'new_salary_group': [('id', 'in', [])], + 'new_salary_degree': [('id', 'in', [])]}} + + # filter depend on salary_group + + @api.onchange('new_salary_group') + def onchange_salary_group(self): + for item in self: + if item.new_salary_group: + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_group_id', '=', item.new_salary_group.id), ('type', '=', 'degree')]) + return {'domain': {'new_salary_degree': [('id', 'in', degree_ids.ids)]}} + else: + return {'domain': {'new_salary_degree': [('id', 'in', [])]}} + + @api.onchange('new_salary_degree') + def onchange_degree(self): + if self.new_salary_degree: + self.new_salary = self.new_salary_degree.base_salary + + @api.depends('employee_id') + def _get_employee_data(self): + for rec in self: + rec.hire_date = False + rec.contract_id = False + rec.start_date = False + rec.eoc_date = False + rec.job_id = False + rec.department_id = False + rec.salary_scale = False + rec.salary_level = False + rec.salary_group = False + rec.salary_degree = False + if rec.employee_id: + rec.hire_date = rec.employee_id.first_hiring_date + rec.contract_id = rec.employee_id.contract_id.id + rec.start_date = rec.employee_id.contract_id.date_start + rec.eoc_date = rec.employee_id.contract_id.date_end + rec.job_id = rec.employee_id.job_id.id + rec.department_id = rec.employee_id.department_id.id + rec.salary_scale = rec.employee_id.contract_id.salary_scale.id + rec.salary_level = rec.employee_id.contract_id.salary_level.id + rec.salary_group = rec.employee_id.contract_id.salary_group.id + rec.salary_degree = rec.employee_id.contract_id.salary_degree.id + + def _get_default_category(self): + return self.env['hr.salary.rule.category'].search([('code', '=', 'NET')], limit=1) diff --git a/odex30_base/exp_payroll_custom/models/hr_salary_rules.py b/odex30_base/exp_payroll_custom/models/hr_salary_rules.py new file mode 100644 index 0000000..580df76 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_salary_rules.py @@ -0,0 +1,390 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime + +from odoo import api, fields, models, _ + +from odoo.exceptions import UserError,ValidationError + +from odoo.tools.safe_eval import safe_eval + +class HrSalaryRuleAccount(models.Model): + _name = 'hr.salary.rule.account' + _description = 'Salary Rule Account Mapping' + + rule_id = fields.Many2one('hr.salary.rule', string="Salary Rule", required=True, ondelete="cascade") + emp_type_id = fields.Many2one('hr.contract.type', string="Employee Type", required=True) + credit_account_id = fields.Many2one('account.account', string="Credit Account", required=True) + debit_account_id = fields.Many2one('account.account', string="Debit Account", required=True) + + + +class HrSalaryRules(models.Model): + _inherit = 'hr.salary.rule' + + start_date = fields.Date(string='Start Date', default=fields.date.today()) + end_date = fields.Date(string='End Date') + salary_type = fields.Selection([('fixed', _('Fixed for all')), + ('related_levels', _('Related with Levels')), + ('related_groups', _('Related with Groups')), + ('related_degrees', _('Related with Degrees'))], default="fixed", + string='Type Scale') + related_qualifications = fields.Boolean(string='Related with qualifications') + special = fields.Boolean(string='Special') + reduce_with_leaves = fields.Boolean(string='Reduce With Leaves',default=True) + min_leave_days_to_deduct = fields.Integer(string='Min Leave Days To Deduct') + company_id = fields.Many2one(comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + discount_absence = fields.Selection([('by_day', _('By Day')), + ('by_hour', _('By Hour')), + ('no_discount', _('No discount'))], + default="no_discount", string='Discount Absence') + fixed_amount = fields.Integer(string='Fixed Amount') + + # relational fields + related_benefits_discounts = fields.Many2many(comodel_name='hr.salary.rule', + relation='salary_rule_benefit_discount_rel', + column1='rule_id', column2='sub_rule_id', + string='Related Benefits and Discount') + salary_amount_ids = fields.One2many('related.salary.amount', 'salary_rule_id') + rule_credit_account_id = fields.Many2one('account.account') + rule_debit_account_id = fields.Many2one('account.account') + rules_type = fields.Selection([('salary', _('Salary Allowance')), + ('house', _('House Allowance')), + ('overtime', _('Overtime Allowance')), + ('mandate', _('Mandate Allowance')), + ('transport', _('Transport Allowance')), + ('termination', _('End Of Services')), + ('insurnce', _('Insurnce Deduction')), + ('other', _('Other')) + ], string='Rules Type') + + account_ids = fields.One2many('hr.salary.rule.account', 'rule_id') + transfer_by_emp_type = fields.Boolean('Transfer By Employee Type') + + def get_debit_account_id(self, emp_type): + if not self.transfer_by_emp_type: return self.rule_debit_account_id.id + account_mapping = self.sudo().account_ids.filtered(lambda a: a.emp_type_id.id == emp_type) + return account_mapping[0].debit_account_id.id if account_mapping else False + + def get_credit_account_id(self, emp_type): + if not self.transfer_by_emp_type: return self.rule_credit_account_id.id + account_mapping = self.sudo().account_ids.filtered(lambda a: a.emp_type_id.id == emp_type) + return account_mapping[0].credit_account_id.id if account_mapping else False + + @api.constrains('rules_type', 'category_id') + def _check_dates(self): + for rec in self: + if rec.category_id.rule_type != 'deduction' and rec.rules_type == 'insurnce': + raise UserError(_("The Salary Rule is Not Deduction")) + + # Override function compute rule in hr salary rule + + def _compute_rule(self, localdict): + payslip = localdict.get('payslip') + contract = localdict.get('contract') + if self.amount_select == 'percentage': + total_percent, total = 0, 0 + if self.related_benefits_discounts: + for line in self.related_benefits_discounts: + calc_line = line._compute_rule(localdict)[0] + + if line.amount_select == 'fix': + if contract.advantages: + for con in contract.advantages: + if line.id == con.benefits_discounts.id: + if payslip: + if con.date_from > payslip.date_from: + total_percent = calc_line + elif con.date_to is not None and str( + con.date_to) >= payslip.date_to or con.date_to is None: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + total_percent += total + else: + if str(con.date_from) < str(datetime.now().date()): + if con.date_to: + if datetime.strptime(str(con.date_to), "%Y-%m-%d").date().month \ + >= datetime.now().date().month or not con.date_to: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + total_percent += total + else: + total_percent = calc_line + else: + total_percent += calc_line + + elif line.amount_select == 'percentage': + if contract.advantages: + for con in contract.advantages: + if line.id == con.benefits_discounts.id: + if payslip: + if con.date_from > payslip.date_from: + total_percent = calc_line + elif con.date_to is not None and str( + con.date_to) >= payslip.date_to or con.date_to is None: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + total_percent -= calc_line + total_percent += total + else: + if str(con.date_from) < str(datetime.now().date()): + if con.date_to: + if datetime.strptime(str(con.date_to), "%Y-%m-%d").date().month \ + >= datetime.now().date().month or not con.date_to: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + total_percent -= calc_line + total_percent += total + else: + if con.type != 'exception': + total_percent += calc_line + break + else: + total_percent += calc_line + + else: + if contract.advantages: + for con in contract.advantages: + if line.id == con.benefits_discounts.id: + if payslip: + if con.date_from > payslip.date_from: + total_percent = calc_line + elif con.date_to is not None and con.date_to >= payslip.date_to or con.date_to is None: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + total_percent = 0 + total_percent += total + else: + if con.date_from < (datetime.now().date()): + if con.date_to: + if datetime.strptime(str(con.date_to), "%Y-%m-%d").date().month \ + >= datetime.now().date().month or not con.date_to: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + total_percent = 0 + total_percent += total + else: + if datetime.strptime(str(con.date_from), + "%Y-%m-%d").date().month >= datetime.now().date().month: + if con.type == 'exception': + if con.amount > calc_line or con.amount == calc_line: + pass + elif con.amount < calc_line: + total = calc_line - con.amount + elif con.type == 'customize': + total = con.amount + calc_line + total_percent = 0 + total_percent += total + + else: + if not total_percent: + total_percent = calc_line + else: + total_percent += calc_line + if total_percent: + if self.salary_type == 'fixed': + try: + return float(total_percent * self.amount_percentage / 100), \ + float(safe_eval(self.quantity, localdict)), self.amount_percentage + except: + raise UserError( + _('Wrong percentage base or quantity defined for salary rule %s (%s).') % ( + self.name, self.code)) + elif self.salary_type == 'related_levels': + levels_ids = self.salary_amount_ids.filtered( + lambda item: item.salary_scale_level.id == contract.salary_level.id) + if levels_ids: + for l in levels_ids: + try: + return float(l.salary * total_percent / 100), float( + safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError( + _('Wrong quantity defined for salary rule %s (%s).') % ( + self.name, self.code)) + else: + return 0, 0, 0 + elif self.salary_type == 'related_groups': + groups_ids = self.salary_amount_ids.filtered( + lambda item: item.salary_scale_group.id == contract.salary_group.id) + if groups_ids: + for g in groups_ids: + try: + return float(g.salary * total_percent / 100), float( + safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError( + _('Wrong quantity defined for salary rule %s (%s).') % ( + self.name, self.code)) + else: + return 0, 0, 0 + elif self.salary_type == 'related_degrees': + degrees_ids = self.salary_amount_ids.filtered( + lambda item: item.salary_scale_degree.id == contract.salary_degree.id) + if degrees_ids: + for d in degrees_ids: + try: + return float(d.salary * total_percent / 100), float( + safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError( + _('Wrong quantity defined for salary rule %s (%s).') % ( + self.name, self.code)) + else: + return 0, 0, 0 + else: + try: + return 0, 0, 0 + except: + raise UserError(_('There is no total for rule : %s') % self.name) + + elif self.amount_select == 'fix': + if self.salary_type == 'fixed': + try: + return self.fixed_amount, float(safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError(_('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code)) + elif self.salary_type == 'related_levels': + levels_ids = self.salary_amount_ids.filtered( + lambda item: item.salary_scale_level.id == contract.salary_level.id) + if levels_ids: + for l in levels_ids: + try: + return l.salary, float(safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError( + _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code)) + else: + return 0, 0, 0 + elif self.salary_type == 'related_groups': + groups_ids = self.salary_amount_ids.filtered( + lambda item: item.salary_scale_group.id == contract.salary_group.id) + if groups_ids: + for g in groups_ids: + try: + return g.salary, float(safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError( + _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code)) + else: + return 0, 0, 0 + elif self.salary_type == 'related_degrees': + degrees_ids = self.salary_amount_ids.filtered( + lambda item: item.salary_scale_degree.id == contract.salary_degree.id) + if degrees_ids: + for d in degrees_ids: + try: + return d.salary, float(safe_eval(self.quantity, localdict)), 100.0 + except: + raise UserError( + _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code)) + else: + return 0, 0, 0 + else: + raise UserError(_('Error, Select Salary type to calculate rule')) + + else: + try: + safe_eval(self.amount_python_compute, localdict, mode='exec', nocopy=True) + return float(localdict['result']), 'result_qty' in localdict and localdict[ + 'result_qty'] or 1.0, 'result_rate' in localdict and localdict['result_rate'] or 100.0 + except: + raise UserError(_('Wrong python code defined for salary rule %s (%s).') % (self.name, self.code)) + + +class SalaryConfig(models.Model): + _name = 'related.salary.amount' + + salary_scale = fields.Many2one('hr.payroll.structure') + salary_scale_level = fields.Many2one('hr.payroll.structure') + salary_scale_group = fields.Many2one('hr.payroll.structure') + salary_scale_degree = fields.Many2one('hr.payroll.structure') + salary = fields.Float(string='Salary / Percentage') + + # relations fields + salary_rule_id = fields.Many2one(comodel_name='hr.salary.rule') + + # filter salary_level,salary_group,salary_degree + + @api.onchange('salary_scale') + def onchange_salary_scale(self): + for item in self: + if item.salary_scale: + level_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'level')]) + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'group')]) + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'degree')]) + return {'domain': {'salary_scale_level': [('id', 'in', level_ids.ids)], + 'salary_scale_group': [('id', 'in', group_ids.ids)], + 'salary_scale_degree': [('id', 'in', degree_ids.ids)]}} + else: + return {'domain': {'salary_scale_level': [('id', 'in', [])], + 'salary_scale_group': [('id', 'in', [])], + 'salary_scale_degree': [('id', 'in', [])]}} + + # filter depend on salary_level + + @api.onchange('salary_scale_level') + def onchange_salary_level(self): + for item in self: + if item.salary_scale_level: + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_level_id', '=', item.salary_scale_level.id), ('type', '=', 'group')]) + return {'domain': {'salary_scale_group': [('id', 'in', group_ids.ids)], + 'salary_scale_degree': [('id', 'in', [])]}} + else: + return {'domain': {'salary_scale_group': [('id', 'in', [])], + 'salary_scale_degree': [('id', 'in', [])]}} + + # filter depend on salary_group + + @api.onchange('salary_scale_group') + def onchange_salary_group(self): + for item in self: + if item.salary_scale_group: + degree_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_group_id', '=', item.salary_scale_group.id), ('type', '=', 'degree')]) + return {'domain': {'salary_scale_degree': [('id', 'in', degree_ids.ids)]}} + else: + return {'domain': {'salary_scale_degree': [('id', 'in', [])]}} + + +class SalaryRuleCategory(models.Model): + _inherit = 'hr.salary.rule.category' + + rule_type = fields.Selection(selection=[('allowance', _('Allowance')), ('deduction', _('Deduction')), + ('base', _('Base')), ('gross', _('Gross')), + ('net', _('Net')), ('end_of_service', _('End of Service'))], string='Type') diff --git a/odex30_base/exp_payroll_custom/models/hr_salary_scale.py b/odex30_base/exp_payroll_custom/models/hr_salary_scale.py new file mode 100644 index 0000000..5ee9cc2 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_salary_scale.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api, _ + + +class HrSalaryScale(models.Model): + _inherit = 'hr.payroll.structure' + + active = fields.Boolean(string='Active', default=True) + start_date = fields.Date(string='Start Date') + end_date = fields.Date(string='End Date') + level_num = fields.Integer(string='Number Of Levels') + retirement_age = fields.Integer('Retirement Age', default=60) + type = fields.Selection(selection=[('scale', _('Scale')), ('level', _('Level')), + ('group', _('Group')), ('degree', _('Degree'))], default='scale', string='Type') + transfer_type = fields.Selection(selection=[('all', _('All Employee')), + ('per_analytic_account', _('Per Analytic Account')), + ('one_by_one', _('Per Employee')), + ('per_bank', _('Per Bank'))], string='Transfer type') + + # relation fields + salary_scale_levels_ids = fields.One2many('hr.payroll.structure', 'salary_scale_id', + domain=[('type', '=', 'level')], store=True) + salary_scale_level_degrees_ids = fields.One2many('hr.payroll.structure', 'salary_scale_id', + domain=[('type', '=', 'degree')], store=True) + salary_scale_level_groups_ids = fields.One2many('hr.payroll.structure', 'salary_scale_id', + domain=[('type', '=', 'group')], store=True) + + salary_scale_id = fields.Many2one('hr.payroll.structure', string='Salary Scale', index=True) # salary scale + + #Percentage_increase = fields.Float('Percentage Increase %',default=0.05) + + analytic_account_id = fields.Many2one(comodel_name='account.analytic.account') + + # Override Function + + def get_all_rules(self): + """ + @return: returns a list of tuple (id, sequence) of rules that are maybe to apply + """ + all_rules = [] + for struct in self: + if struct.benefits_discounts_ids: + all_rules += struct.benefits_discounts_ids._recursive_search_of_rules() + else: + all_rules += struct.rule_ids._recursive_search_of_rules() + + return all_rules + + # filter salary_level,salary_group + + @api.onchange('salary_scale_id') + def onchange_salary_scale_id(self): + for item in self: + if item.salary_scale_id: + level_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_id', '=', item.salary_scale_id.id), ('type', '=', 'level')]) + item.salary_scale_level_id = [] + item.salary_scale_group_id = [] + return {'domain': {'salary_scale_level_id': [('id', 'in', level_ids.ids)]}} + + # filter depend on salary_level + + @api.onchange('salary_scale_level_id') + def onchange_salary_scale_level_id(self): + for item in self: + if item.salary_scale_level_id: + group_ids = self.env['hr.payroll.structure'].search( + [('salary_scale_level_id', '=', item.salary_scale_level_id.id), ('type', '=', 'group')]) + item.salary_scale_group_id = [] + return {'domain': {'salary_scale_group_id': [('id', 'in', group_ids.ids)]}} diff --git a/odex30_base/exp_payroll_custom/models/hr_salary_scale_level.py b/odex30_base/exp_payroll_custom/models/hr_salary_scale_level.py new file mode 100644 index 0000000..0209db0 --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_salary_scale_level.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields + + +class HrSalaryScaleLevel(models.Model): + _inherit = 'hr.payroll.structure' + + groups_number = fields.Integer(string='Number Of Groups') + salary_scale_id = fields.Many2one('hr.payroll.structure', string='Salary Scale', index=True) + benefits_discounts_ids = fields.Many2many('hr.salary.rule', string='Benefits and discounts') diff --git a/odex30_base/exp_payroll_custom/models/hr_salary_scale_level_degree.py b/odex30_base/exp_payroll_custom/models/hr_salary_scale_level_degree.py new file mode 100644 index 0000000..94f445b --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_salary_scale_level_degree.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class HrSalaryScaleDegree(models.Model): + _inherit = 'hr.payroll.structure' + + base_salary = fields.Float(string='Base Salary') + interval_time = fields.Integer(string='Interval Time') + salary_scale_group_id = fields.Many2one(comodel_name='hr.payroll.structure', index=True) + + @api.constrains('base_salary', 'salary_scale_group_id') + def base_salary_constrains(self): + if self.salary_scale_group_id.gread_max > 0 and self.salary_scale_group_id.gread_min > 0: + if self.base_salary > self.salary_scale_group_id.gread_max or \ + self.base_salary < self.salary_scale_group_id.gread_min: + raise UserError(_('The Basic Salary Is Greater Than Group Gread Max Or less than Gread Min')) diff --git a/odex30_base/exp_payroll_custom/models/hr_salary_scale_level_group.py b/odex30_base/exp_payroll_custom/models/hr_salary_scale_level_group.py new file mode 100644 index 0000000..6a859dc --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/hr_salary_scale_level_group.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class HrSalaryScaleLevel(models.Model): + _inherit = 'hr.payroll.structure' + + degree_number = fields.Integer(string='Number of Degrees') + salary_scale_level_id = fields.Many2one(comodel_name='hr.payroll.structure', string='Salary Scale Level', + index=True) + gread_min = fields.Float(string='Gread Min') + gread_max = fields.Float(string='Gread Max') + + @api.constrains('gread_min', 'gread_max') + def zero_constrains(self): + + if self.gread_max < 0 or self.gread_min < 0: + raise UserError(_('The Gread Max Or Gread Min is not Negative')) + + if self.gread_max < self.gread_min: + raise UserError(_('The Gread Max Is Greater Than Gread Min')) diff --git a/odex30_base/exp_payroll_custom/models/salary_advance.py b/odex30_base/exp_payroll_custom/models/salary_advance.py new file mode 100644 index 0000000..4ff6a3f --- /dev/null +++ b/odex30_base/exp_payroll_custom/models/salary_advance.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +import time +from datetime import datetime + +from odoo import exceptions +from odoo import fields, models, api, _ +from odoo.exceptions import UserError + + + +class SalaryAdvancePayment(models.Model): + _name = "salary.advance" + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='Name', readonly=True, default=lambda self: 'Adv/') + employee_id = fields.Many2one(comodel_name='hr.employee', string='Employee', required=True, index=True) + date = fields.Date(string='Date', required=True, default=lambda self: fields.Date.today()) + reason = fields.Text(string='Reason') + currency_id = fields.Many2one(comodel_name='res.currency', string='Currency', required=True, + default=lambda self: self.env.user.company_id.currency_id) + company_id = fields.Many2one(comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + advance = fields.Float(string='Advance', required=True) + payment_method = fields.Many2one(comodel_name='account.journal', string='Payment Method') + exceed_condition = fields.Boolean(string='Exceed than maximum', + help="The Advance is greater than the maximum percentage in salary structure") + department = fields.Many2one(comodel_name='hr.department', string='Department') + state = fields.Selection(selection=[('draft', 'Draft'), + ('submit', 'Submitted'), + ('waiting_approval', 'Waiting Approval'), + ('approve', 'Approved'), + ('cancel', 'Cancelled'), + ('reject', 'Rejected')], string='Status', default='draft', tracking=True) + debit = fields.Many2one(comodel_name='account.account', string='Debit Account') + credit = fields.Many2one(comodel_name='account.account', string='Credit Account') + journal = fields.Many2one(comodel_name='account.journal', string='Journal') + employee_contract_id = fields.Many2one(comodel_name='hr.contract', string='Contract') + + @api.onchange('employee_id') + def onchange_employee_id(self): + department_id = self.employee_id.department_id.id + domain = [('employee_id', '=', self.employee_id.id)] + return {'value': {'department': department_id}, 'domain': { + 'employee_contract_id': domain, + }} + + @api.onchange('company_id') + def onchange_company_id(self): + company = self.company_id + domain = [('company_id.id', '=', company.id)] + result = { + 'domain': { + 'journal': domain, + }, + } + return result + + def submit_to_manager(self): + self.state = 'submit' + + def cancel(self): + self.state = 'cancel' + + def reject(self): + self.state = 'reject' + + @api.model + def create(self, vals): + vals['name'] = self.env['ir.sequence'].get('salary.advance.seq') or ' ' + res_id = super(SalaryAdvancePayment, self).create(vals) + return res_id + + def approve_request(self): + """This Approve the employee salary advance request. + """ + emp_obj = self.env['hr.employee'] + address = emp_obj.browse([self.employee_id.id]).address_home_id + if not address.id: + raise UserError('Error!', 'Define home address for employee') + salary_advance_search = self.search([('employee_id', '=', self.employee_id.id), ('id', '!=', self.id), + ('state', '=', 'approve')]) + current_month = datetime.strptime(str(self.date), '%Y-%m-%d').date().month + for each_advance in salary_advance_search: + existing_month = datetime.strptime(str(each_advance.date), '%Y-%m-%d').date().month + if current_month == existing_month: + raise UserError('Error!', 'Advance can be requested once in a month') + if not self.employee_contract_id: + raise UserError('Error!', 'Define a contract for the employee') + struct_id = self.employee_contract_id.struct_id + if not struct_id.max_percent or not struct_id.advance_date: + raise UserError('Error!', 'Max percentage or advance days are not provided in Contract') + adv = self.advance + amt = (self.employee_contract_id.struct_id.max_percent * self.employee_contract_id.wage) / 100 + if adv > amt and not self.exceed_condition: + raise UserError('Error!', 'Advance amount is greater than allotted') + + if not self.advance: + raise UserError('Warning', 'You must Enter the Salary Advance amount') + payslip_obj = self.env['hr.payslip'].search([('employee_id', '=', self.employee_id.id), + ('state', '=', 'done'), ('date_from', '<=', self.date), + ('date_to', '>=', self.date)]) + if payslip_obj: + raise UserError('Warning', "This month salary already calculated") + + for slip in self.env['hr.payslip'].search([('employee_id', '=', self.employee_id.id)]): + slip_moth = datetime.strptime(str(slip.date_from), '%Y-%m-%d').date().month + if current_month == slip_moth + 1: + slip_day = datetime.strptime(str(slip.date_from), '%Y-%m-%d').date().day + current_day = datetime.strptime(str(self.date), '%Y-%m-%d').date().day + if current_day - slip_day < struct_id.advance_date: + raise exceptions.Warning( + _('Request can be done after "%s" Days From previous month salary') % struct_id.advance_date) + self.state = 'waiting_approval' + + def approve_request_acc_dept(self): + """This Approve the employee salary advance request from accounting department. + """ + salary_advance_search = self.search([('employee_id', '=', self.employee_id.id), ('id', '!=', self.id), + ('state', '=', 'approve')]) + current_month = datetime.strptime(str(self.date), '%Y-%m-%d').date().month + for each_advance in salary_advance_search: + existing_month = datetime.strptime(str(each_advance.date), '%Y-%m-%d').date().month + if current_month == existing_month: + raise UserError('Error!', 'Advance can be requested once in a month') + if not self.debit or not self.credit or not self.journal: + raise UserError('Warning', "You must enter Debit & Credit account and journal to approve ") + if not self.advance: + raise UserError('Warning', 'You must Enter the Salary Advance amount') + + move_obj = self.env['account.move'] + timenow = time.strftime('%Y-%m-%d') + line_ids = [] + debit_sum = 0.0 + credit_sum = 0.0 + for request in self: + amount = request.advance + request_name = request.employee_id.name + reference = request.name + journal_id = request.journal.id + move = { + 'narration': 'Salary Advance Of ' + request_name, + 'ref': reference, + 'journal_id': journal_id, + 'date': timenow, + 'state': 'posted', + } + + debit_account_id = request.debit.id + credit_account_id = request.credit.id + + if debit_account_id: + debit_line = (0, 0, { + 'name': request_name, + 'account_id': debit_account_id, + 'journal_id': journal_id, + 'date': timenow, + 'debit': amount > 0.0 and amount or 0.0, + 'credit': amount < 0.0 and -amount or 0.0, + 'currency_id': self.currency_id.id, + }) + line_ids.append(debit_line) + debit_sum += debit_line[2]['debit'] - debit_line[2]['credit'] + + if credit_account_id: + credit_line = (0, 0, { + 'name': request_name, + 'account_id': credit_account_id, + 'journal_id': journal_id, + 'date': timenow, + 'debit': amount < 0.0 and -amount or 0.0, + 'credit': amount > 0.0 and amount or 0.0, + 'currency_id': self.currency_id.id, + }) + line_ids.append(credit_line) + credit_sum += credit_line[2]['credit'] - credit_line[2]['debit'] + + move.update({'line_ids': line_ids}) + move_obj.create(move) + self.state = 'approve' + return True diff --git a/odex30_base/exp_payroll_custom/report/__init__.py b/odex30_base/exp_payroll_custom/report/__init__.py new file mode 100644 index 0000000..2aeeff1 --- /dev/null +++ b/odex30_base/exp_payroll_custom/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import payslip_monthly_report +from . import bank_pdf_report diff --git a/odex30_base/exp_payroll_custom/report/bank_pdf_report.py b/odex30_base/exp_payroll_custom/report/bank_pdf_report.py new file mode 100644 index 0000000..dfc0609 --- /dev/null +++ b/odex30_base/exp_payroll_custom/report/bank_pdf_report.py @@ -0,0 +1,2880 @@ +# -*- coding: utf-8 -*- + +from odoo import models, api, _ +from odoo.exceptions import UserError + + +class PayslipBankReport(models.AbstractModel): + _name = 'report.exp_payroll_custom.report_payroll_bank_pdf' + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form'): + raise UserError(_("Form content is missing, this report cannot be printed.")) + emp_ids = data['employees'] + bank_ids = data['banks'] + salary_ids = data['salary'] + date_from = data['date_from'] + date_to = data['date_to'] + employees = self.sudo().env['hr.employee'].browse(emp_ids) + salary = self.sudo().env['hr.payroll.structure'].browse(salary_ids) + banks = self.sudo().env['res.bank'].browse(bank_ids) + no_details = data['no_details'] + report_type = data['report_type'] + entry_type = data['entry_type'] + bank_type = data['bank_type'] + all_bank = self.sudo().env['res.bank'].search([]) + Module = self.env['ir.module.module'].sudo() + branch = Module.search([('state', '=', 'installed'), ('name', '=', 'bi_odoo_multi_branch_hr')]) + + + data = [] + if not no_details: + for bank in banks: + docs = [] + if report_type == 'salary': + if entry_type == 'all': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id) + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + [('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|' + , ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + + elif entry_type == 'unposted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft'), + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('struct_id', 'in', salary_ids), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft'), + ]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft')]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft')]) + + for payslip in payslips: + tot_basic = 0.0 + tot_housing = 0.0 + tot_other = 0.0 + tot_net = 0.0 + tot_ded = 0.0 + + net = 0.0 + basic = 0.0 + housing = 0.0 + other = 0.0 + total = 0.0 + + salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted( + key=lambda v: v.sequence).ids + payslip_line_obj = self.sudo().env['hr.payslip.line'] + payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)]) + if not payslip_lines_ids: + continue + + for payslip_line_rec in payslip_lines_ids: + if payslip_line_rec.salary_rule_id.id in salary_rules: + if payslip_line_rec.salary_rule_id.rules_type == 'salary': + basic += payslip_line_rec.total + elif payslip_line_rec.salary_rule_id.rules_type == 'house': + housing += payslip_line_rec.total + other = payslip.total_allowances - basic - housing + deduction = total - net + tot_net += net + tot_basic += basic + tot_housing += housing + tot_other += other + tot_ded += deduction + + docs.append({ + 'ID': payslip.employee_id.emp_no, + 'Name': payslip.employee_id.name, + 'Account #': payslip.employee_id.bank_account_id.acc_number, + 'Bank': payslip.employee_id.bank_account_id.bank_id.bic, + 'Salary': payslip.total_sum, + 'National': payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else + payslip.employee_id.iqama_number.iqama_id, + 'Basic': basic, + 'Housing': housing, + 'Other': round(other, 2), + 'Deduction': round((payslip.total_deductions + payslip.total_loans), 2), + 'Address': payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': payslip.employee_id.company_id.currency_id.name + }) + elif report_type == 'allowance': + allowances = self.sudo().env['hr.employee.reward'].search( + ['&', ('date', '>=', date_from), ('date', '<=', date_to), ('state', '=', 'done') + ]) + for allowance in allowances: + reward_line_obj = self.sudo().env['lines.ids.reward'] + if entry_type == 'all': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + elif entry_type == 'unposted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('move_id.state', '=', 'draft'), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not reward_lines_ids: + continue + + for reward in reward_lines_ids: + docs.append({ + 'ID': reward.employee_id.emp_no, + 'Name': reward.employee_id.name, + 'Account #': reward.employee_id.bank_account_id.acc_number, + 'Bank': reward.employee_id.bank_account_id.bank_id.bic, + 'Salary': reward.amount, + 'National': reward.employee_id.saudi_number.saudi_id if reward.employee_id.check_nationality == True else + reward.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': reward.employee_id.branch_id.name if branch else reward.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': reward.employee_id.company_id.currency_id.name + }) + elif report_type == 'overtime': + overtime = self.sudo().env['employee.overtime.request'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('transfer_type', '=', 'accounting'), ('state', '=', 'validated') + ]) + for over in overtime: + reward_line_obj = self.sudo().env['line.ids.over.time'] + if entry_type == 'all': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + elif entry_type == 'unposted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not overtime_lines_ids: + continue + + for ove in overtime_lines_ids: + docs.append({ + 'ID': ove.employee_id.emp_no, + 'Name': ove.employee_id.name, + 'Account #': ove.employee_id.bank_account_id.acc_number, + 'Bank': ove.employee_id.bank_account_id.bank_id.bic, + 'Salary': ove.price_hour, + 'National': ove.employee_id.saudi_number.saudi_id if ove.employee_id.check_nationality == True else + ove.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': ove.employee_id.branch_id.name if branch else ove.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': ove.employee_id.company_id.currency_id.name + }) + elif report_type == 'mission': + missions = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'mission'), ('state', '=', 'approve') + ]) + for mission in missions: + mission_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'unposted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not mission_lines_ids: + continue + + for miss in mission_lines_ids: + docs.append({ + 'ID': miss.employee_id.emp_no, + 'Name': miss.employee_id.name, + 'Account #': miss.employee_id.bank_account_id.acc_number, + 'Bank': miss.employee_id.bank_account_id.bank_id.bic, + 'Salary': miss.amount, + 'National': miss.employee_id.saudi_number.saudi_id if miss.employee_id.check_nationality == True else + miss.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': miss.employee_id.branch_id.name if branch else miss.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': miss.employee_id.company_id.currency_id.name + }) + elif report_type == 'training': + trainings = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'training'), ('state', '=', 'approve') + ]) + for training in trainings: + training_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('account_move_id.state', '=', 'posted')]) + elif entry_type == 'unposted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not training_lines_ids: + continue + + for train in training_lines_ids: + docs.append({ + 'ID': train.employee_id.emp_no, + 'Name': train.employee_id.name, + 'Account #': train.employee_id.bank_account_id.acc_number, + 'Bank': train.employee_id.bank_account_id.bank_id.bic, + 'Salary': train.amount, + 'National': train.employee_id.saudi_number.saudi_id if train.employee_id.check_nationality == True else + train.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': train.employee_id.branch_id.name if branch else train.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': train.employee_id.company_id.currency_id.name + }) + + data.append({ + 'docs': docs, + 'bank': bank.name, + 'report_type': report_type, + 'no_details': no_details, + 'bank_type': bank_type + }) + + else: + docs = [] + if report_type == 'salary': + if entry_type == 'all': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + elif entry_type == 'unposted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + + for payslip in payslips: + tot_basic = 0.0 + tot_housing = 0.0 + tot_other = 0.0 + tot_net = 0.0 + tot_ded = 0.0 + net = 0.0 + basic = 0.0 + housing = 0.0 + other = 0.0 + total = 0.0 + salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted( + key=lambda v: v.sequence).ids + payslip_line_obj = self.sudo().env['hr.payslip.line'] + payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)]) + if not payslip_lines_ids: + continue + + for payslip_line_rec in payslip_lines_ids: + if payslip_line_rec.salary_rule_id.id in salary_rules: + if payslip_line_rec.salary_rule_id.rules_type == 'salary': + basic += payslip_line_rec.total + elif payslip_line_rec.salary_rule_id.rules_type == 'house': + housing += payslip_line_rec.total + other = payslip.total_allowances - basic - housing + deduction = total - net + tot_net += net + tot_basic += basic + tot_housing += housing + tot_other += other + tot_ded += deduction + docs.append({ + 'ID': payslip.employee_id.emp_no, + 'Name': payslip.employee_id.name, + 'Account #': payslip.employee_id.bank_account_id.acc_number, + 'Bank': payslip.employee_id.bank_account_id.bank_id.bic, + 'Salary': payslip.total_sum, + 'National': payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else + payslip.employee_id.iqama_number.iqama_id, + 'Basic': basic, + 'Housing': housing, + 'Other': round(other, 2), + 'Deduction': round((payslip.total_deductions + payslip.total_loans), 2), + 'Address': payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': payslip.employee_id.company_id.currency_id.name + }) + elif report_type == 'allowance': + allowances = self.sudo().env['hr.employee.reward'].search( + ['&', ('date', '>=', date_from), ('date', '<=', date_to), ('state', '=', 'done') + ]) + for allowance in allowances: + reward_line_obj = self.sudo().env['lines.ids.reward'] + if entry_type == 'all': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not reward_lines_ids: + continue + + for reward in reward_lines_ids: + docs.append({ + 'ID': reward.employee_id.emp_no, + 'Name': reward.employee_id.name, + 'Account #': reward.employee_id.bank_account_id.acc_number, + 'Bank': reward.employee_id.bank_account_id.bank_id.bic, + 'Salary': reward.amount, + 'National': reward.employee_id.saudi_number.saudi_id if reward.employee_id.check_nationality == True else + reward.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': reward.employee_id.branch_id.name if branch else reward.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': reward.employee_id.company_id.currency_id.name + }) + elif report_type == 'overtime': + overtime = self.sudo().env['employee.overtime.request'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('transfer_type', '=', 'accounting'), ('state', '=', 'validated') + ]) + for over in overtime: + reward_line_obj = self.sudo().env['line.ids.over.time'] + if entry_type == 'all': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'draft')]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not overtime_lines_ids: + continue + + for ove in overtime_lines_ids: + docs.append({ + 'ID': ove.employee_id.emp_no, + 'Name': ove.employee_id.name, + 'Account #': ove.employee_id.bank_account_id.acc_number, + 'Bank': ove.employee_id.bank_account_id.bank_id.bic, + 'Salary': ove.price_hour, + 'National': ove.employee_id.saudi_number.saudi_id if ove.employee_id.check_nationality == True else + ove.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': ove.employee_id.branch_id.name if branch else ove.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency':ove.employee_id.company_id.currency_id.name + }) + elif report_type == 'mission': + missions = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), ('process_type', '=', 'mission'), + ('state', '=', 'approve') + ]) + for mission in missions: + mission_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not mission_lines_ids: + continue + + for miss in mission_lines_ids: + docs.append({ + 'ID': miss.employee_id.emp_no, + 'Name': miss.employee_id.name, + 'Account #': miss.employee_id.bank_account_id.acc_number, + 'Bank': miss.employee_id.bank_account_id.bank_id.bic, + 'Salary': miss.amount, + 'National': miss.employee_id.saudi_number.saudi_id if miss.employee_id.check_nationality == True else + miss.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': miss.employee_id.branch_id.name if branch else miss.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': miss.employee_id.company_id.currency_id.name + }) + elif report_type == 'training': + trainings = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'training'), ('state', '=', 'approve') + ]) + for training in trainings: + training_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('account_move_id.state', '=', 'posted')]) + elif entry_type == 'unposted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'draft')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('account_move_id.state', '=', 'draft')]) + + if not training_lines_ids: + continue + + for train in training_lines_ids: + docs.append({ + 'ID': train.employee_id.emp_no, + 'Name': train.employee_id.name, + 'Account #': train.employee_id.bank_account_id.acc_number, + 'Bank': train.employee_id.bank_account_id.bank_id.bic, + 'Salary': train.amount, + 'National': train.employee_id.saudi_number.saudi_id if train.employee_id.check_nationality == True else + train.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': train.employee_id.branch_id.name if branch else train.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': train.employee_id.company_id.currency_id.name + }) + + data.append({ + 'docs': docs, + 'bank': '', + 'report_type': report_type, + 'no_details': no_details + }) + return { + 'banks': banks, + 'data': data, + 'date_from': date_from, + 'date_to': date_to, + } + +class PayslipBankReport(models.AbstractModel): + _name = 'report.exp_payroll_custom.report_payroll_bank_pdf_docx' + + @api.model + def _get_report_values(self, docids, data=None): + total_docs_count = 0 + total_amount_salary = 0 + number_of_records = 0 + if not data.get('form'): + raise UserError(_("Form content is missing, this report cannot be printed.")) + emp_ids = data['employees'] + bank_ids = data['banks'] + salary_ids = data['salary'] + date_from = data['date_from'] + date_to = data['date_to'] + company_id = data['company_id'] + employees = self.sudo().env['hr.employee'].browse(emp_ids) + salary = self.sudo().env['hr.payroll.structure'].browse(salary_ids) + banks = self.sudo().env['res.bank'].browse(bank_ids) + no_details = data['no_details'] + report_type = data['report_type'] + entry_type = data['entry_type'] + bank_type = data['bank_type'] + all_bank = self.sudo().env['res.bank'].search([]) + Module = self.env['ir.module.module'].sudo() + branch = Module.search([('state', '=', 'installed'), ('name', '=', 'bi_odoo_multi_branch_hr')]) + + + data = [] + if not no_details: + for bank in banks: + docs = [] + if report_type == 'salary': + if entry_type == 'all': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id) + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + [('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|' + , ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + + elif entry_type == 'unposted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft'), + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('struct_id', 'in', salary_ids), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft'), + ]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft')]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft')]) + + for payslip in payslips: + tot_basic = 0.0 + tot_housing = 0.0 + tot_other = 0.0 + tot_net = 0.0 + tot_ded = 0.0 + + net = 0.0 + basic = 0.0 + housing = 0.0 + other = 0.0 + total = 0.0 + Deduction_postive = 0.0 + salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted( + key=lambda v: v.sequence).ids + payslip_line_obj = self.sudo().env['hr.payslip.line'] + payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)]) + if not payslip_lines_ids: + continue + + for payslip_line_rec in payslip_lines_ids: + if payslip_line_rec.salary_rule_id.id in salary_rules: + if payslip_line_rec.salary_rule_id.rules_type == 'salary': + basic += payslip_line_rec.total + elif payslip_line_rec.salary_rule_id.rules_type == 'house': + housing += payslip_line_rec.total + other = payslip.total_allowances - basic - housing + deduction = total - net + tot_net += net + tot_basic += basic + tot_housing += housing + tot_other += other + tot_ded += deduction + + Deduction_postive = round(payslip.total_deductions + payslip.total_loans,2) + if Deduction_postive < 0.0: + Deduction_postive = -round(payslip.total_deductions + payslip.total_loans,2) + + docs.append({ + 'ID': payslip.employee_id.emp_no, + 'Name': payslip.employee_id.english_name, + 'Account #': payslip.employee_id.bank_account_id.acc_number, + 'Bank': payslip.employee_id.bank_account_id.bank_id.bic, + 'Salary': payslip.total_sum, + 'National': payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else + payslip.employee_id.iqama_number.iqama_id, + 'Basic': basic, + 'Housing': housing, + 'Other': round(other, 2), + #'Deduction': round(-(payslip.total_deductions + payslip.total_loans), 2), + 'Deduction': Deduction_postive, + 'Address': payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': payslip.employee_id.company_id.currency_id.name + }) + elif report_type == 'allowance': + allowances = self.sudo().env['hr.employee.reward'].search( + ['&', ('date', '>=', date_from), ('date', '<=', date_to), ('state', '=', 'done') + ]) + for allowance in allowances: + reward_line_obj = self.sudo().env['lines.ids.reward'] + if entry_type == 'all': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + elif entry_type == 'unposted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('move_id.state', '=', 'draft'), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not reward_lines_ids: + continue + + for reward in reward_lines_ids: + docs.append({ + 'ID': reward.employee_id.emp_no, + 'Name': reward.employee_id.english_name, + 'Account #': reward.employee_id.bank_account_id.acc_number, + 'Bank': reward.employee_id.bank_account_id.bank_id.bic, + 'Salary': reward.amount, + 'National': reward.employee_id.saudi_number.saudi_id if reward.employee_id.check_nationality == True else + reward.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': reward.employee_id.branch_id.name if branch else reward.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': reward.employee_id.company_id.currency_id.name + }) + elif report_type == 'overtime': + overtime = self.sudo().env['employee.overtime.request'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('transfer_type', '=', 'accounting'), ('state', '=', 'validated') + ]) + for over in overtime: + reward_line_obj = self.sudo().env['line.ids.over.time'] + if entry_type == 'all': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + elif entry_type == 'unposted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not overtime_lines_ids: + continue + + for ove in overtime_lines_ids: + docs.append({ + 'ID': ove.employee_id.emp_no, + 'Name': ove.employee_id.english_name, + 'Account #': ove.employee_id.bank_account_id.acc_number, + 'Bank': ove.employee_id.bank_account_id.bank_id.bic, + 'Salary': ove.price_hour, + 'National': ove.employee_id.saudi_number.saudi_id if ove.employee_id.check_nationality == True else + ove.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': ove.employee_id.branch_id.name if branch else ove.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': ove.employee_id.company_id.currency_id.name + }) + elif report_type == 'mission': + missions = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'mission'), ('state', '=', 'approve') + ]) + for mission in missions: + mission_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'unposted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not mission_lines_ids: + continue + + for miss in mission_lines_ids: + docs.append({ + 'ID': miss.employee_id.emp_no, + 'Name': miss.employee_id.english_name, + 'Account #': miss.employee_id.bank_account_id.acc_number, + 'Bank': miss.employee_id.bank_account_id.bank_id.bic, + 'Salary': miss.amount, + 'National': miss.employee_id.saudi_number.saudi_id if miss.employee_id.check_nationality == True else + miss.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': miss.employee_id.branch_id.name if branch else miss.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': miss.employee_id.company_id.currency_id.name + }) + elif report_type == 'training': + trainings = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'training'), ('state', '=', 'approve') + ]) + for training in trainings: + training_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('account_move_id.state', '=', 'posted')]) + elif entry_type == 'unposted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not training_lines_ids: + continue + + for train in training_lines_ids: + docs.append({ + 'ID': train.employee_id.emp_no, + 'Name': train.employee_id.english_name, + 'Account #': train.employee_id.bank_account_id.acc_number, + 'Bank': train.employee_id.bank_account_id.bank_id.bic, + 'Salary': train.amount, + 'National': train.employee_id.saudi_number.saudi_id if train.employee_id.check_nationality == True else + train.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': train.employee_id.branch_id.name if branch else train.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': train.employee_id.company_id.currency_id.name + }) + + counter = docs.count + data.append({ + 'docs': docs, + + 'bank': bank.name, + 'report_type': report_type, + 'no_details': no_details, + 'bank_type': bank_type, + 'counter': counter + }) + + else: + docs = [] + if report_type == 'salary': + if entry_type == 'all': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + elif entry_type == 'unposted': + if employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + + elif salary: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + elif salary and employees: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + else: + payslips = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + + for payslip in payslips: + tot_basic = 0.0 + tot_housing = 0.0 + tot_other = 0.0 + tot_net = 0.0 + tot_ded = 0.0 + net = 0.0 + basic = 0.0 + housing = 0.0 + other = 0.0 + total = 0.0 + Deduction_postive = 0.0 + salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted( + key=lambda v: v.sequence).ids + payslip_line_obj = self.sudo().env['hr.payslip.line'] + payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)]) + if not payslip_lines_ids: + continue + + for payslip_line_rec in payslip_lines_ids: + if payslip_line_rec.salary_rule_id.id in salary_rules: + if payslip_line_rec.salary_rule_id.rules_type == 'salary': + basic += payslip_line_rec.total + elif payslip_line_rec.salary_rule_id.rules_type == 'house': + housing += payslip_line_rec.total + other = payslip.total_allowances - basic - housing + deduction = total - net + tot_net += net + tot_basic += basic + tot_housing += housing + tot_other += other + tot_ded += deduction + + Deduction_postive= round(payslip.total_deductions + payslip.total_loans,2) + if Deduction_postive < 0.0: + Deduction_postive = -round(payslip.total_deductions + payslip.total_loans,2) + docs.append({ + 'ID': payslip.employee_id.emp_no, + 'Name': payslip.employee_id.english_name, + 'Account #': payslip.employee_id.bank_account_id.acc_number, + 'Bank': payslip.employee_id.bank_account_id.bank_id.bic, + 'Salary': payslip.total_sum, + 'National': payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else + payslip.employee_id.iqama_number.iqama_id, + 'Basic': basic, + 'Housing': housing, + 'Other': round(other, 2), + #'Deduction': round(-(payslip.total_deductions + payslip.total_loans), 2), + 'Deduction': Deduction_postive, + 'Address': payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': payslip.employee_id.company_id.currency_id.name + }) + elif report_type == 'allowance': + allowances = self.sudo().env['hr.employee.reward'].search( + ['&', ('date', '>=', date_from), ('date', '<=', date_to), ('state', '=', 'done') + ]) + for allowance in allowances: + reward_line_obj = self.sudo().env['lines.ids.reward'] + if entry_type == 'all': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not reward_lines_ids: + continue + + for reward in reward_lines_ids: + docs.append({ + 'ID': reward.employee_id.emp_no, + 'Name': reward.employee_id.english_name, + 'Account #': reward.employee_id.bank_account_id.acc_number, + 'Bank': reward.employee_id.bank_account_id.bank_id.bic, + 'Salary': reward.amount, + 'National': reward.employee_id.saudi_number.saudi_id if reward.employee_id.check_nationality == True else + reward.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': reward.employee_id.branch_id.name if branch else reward.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': reward.employee_id.company_id.currency_id.name + }) + elif report_type == 'overtime': + overtime = self.sudo().env['employee.overtime.request'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('transfer_type', '=', 'accounting'), ('state', '=', 'validated') + ]) + for over in overtime: + reward_line_obj = self.sudo().env['line.ids.over.time'] + if entry_type == 'all': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'draft')]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not overtime_lines_ids: + continue + + for ove in overtime_lines_ids: + docs.append({ + 'ID': ove.employee_id.emp_no, + 'Name': ove.employee_id.english_name, + 'Account #': ove.employee_id.bank_account_id.acc_number, + 'Bank': ove.employee_id.bank_account_id.bank_id.bic, + 'Salary': ove.price_hour, + 'National': ove.employee_id.saudi_number.saudi_id if ove.employee_id.check_nationality == True else + ove.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': ove.employee_id.branch_id.name if branch else ove.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency':ove.employee_id.company_id.currency_id.name + }) + elif report_type == 'mission': + missions = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), ('process_type', '=', 'mission'), + ('state', '=', 'approve') + ]) + for mission in missions: + mission_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not mission_lines_ids: + continue + + for miss in mission_lines_ids: + docs.append({ + 'ID': miss.employee_id.emp_no, + 'Name': miss.employee_id.english_name, + 'Account #': miss.employee_id.bank_account_id.acc_number, + 'Bank': miss.employee_id.bank_account_id.bank_id.bic, + 'Salary': miss.amount, + 'National': miss.employee_id.saudi_number.saudi_id if miss.employee_id.check_nationality == True else + miss.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': miss.employee_id.branch_id.name if branch else miss.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': miss.employee_id.company_id.currency_id.name + }) + elif report_type == 'training': + trainings = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'training'), ('state', '=', 'approve') + ]) + for training in trainings: + training_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('account_move_id.state', '=', 'posted')]) + elif entry_type == 'unposted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'draft')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('account_move_id.state', '=', 'draft')]) + + if not training_lines_ids: + continue + + for train in training_lines_ids: + docs.append({ + 'ID': train.employee_id.emp_no, + 'Name': train.employee_id.english_name, + 'Account #': train.employee_id.bank_account_id.acc_number, + 'Bank': train.employee_id.bank_account_id.bank_id.bic, + 'Salary': train.amount, + 'National': train.employee_id.saudi_number.saudi_id if train.employee_id.check_nationality == True else + train.employee_id.iqama_number.iqama_id, + 'Basic': 0.0, + 'Housing': 0.0, + 'Other': 0.0, + 'Deduction': 0.0, + 'Address': train.employee_id.branch_id.name if branch else train.employee_id.working_location.name, + 'Pay Description': report_type, + 'currency': train.employee_id.company_id.currency_id.name + }) + + # counter = docs.count('ID')+1 + # print("###########################",counter) + data.append({ + 'docs': docs, + 'bank': '', + 'report_type': report_type, + 'no_details': no_details, + # 'counter': counter + }) + total_docs_count = sum(len(entry['docs']) for entry in data) + total_amount_salary = sum(doc['Salary'] for entry in data for doc in entry['docs']) + number_of_records = total_docs_count+2 + return { + 'banks': banks, + 'data': data, + 'date_from': date_from, + 'date_to': date_to, + 'company_id': company_id, + 'counter':total_docs_count, + 'number_of_records':number_of_records, + 'total_amount_salary':total_amount_salary + } + + +class PayrollXlsx(models.AbstractModel): + _name = 'report.exp_payroll_custom.report_payroll_bank_xlsx' + _inherit = 'report.report_xlsx.abstract' + + def generate_xlsx_report(self, workbook, data, payslips): + emp_ids = data['employees'] + bank_ids = data['banks'] + salary_ids = data['salary'] + date_from = data['date_from'] + date_to = data['date_to'] + company_id= data['company_id'] + employees = self.sudo().env['hr.employee'].browse(emp_ids) + salary = self.sudo().env['hr.payroll.structure'].browse(salary_ids) + banks = self.sudo().env['res.bank'].browse(bank_ids) + salary_ids = self.sudo().env['hr.payroll.structure'].browse(salary) + no_details = data['no_details'] + report_type = data['report_type'] + entry_type = data['entry_type'] + bank_type = data['bank_type'] + all_bank = self.sudo().env['res.bank'].search([]) + branch = self.env['ir.module.module'].sudo().search( + [('state', '=', 'installed'), ('name', '=', 'bi_odoo_multi_branch_hr')]) + + company_id = self.env['res.company'].search([('id', '=', company_id)]) + + + sheet = workbook.add_worksheet('Bank Sheet') + format1 = workbook.add_format( {'font_size': 10, 'bottom': True, 'right': True, 'left': True, 'top': True, 'align': 'center','bold': True}) + format2 = workbook.add_format({'font_size': 10, 'bottom': True, 'right': True, 'left': True, 'top': True, 'align': 'center', 'bold': True}) + format3 = workbook.add_format({'bottom': True, 'align': 'center', 'bold': True, }) + format4 = workbook.add_format({'font_size': 10, 'bottom': True, 'right': True, 'left': True, 'top': True, 'align': 'center','bold': True}) + format2.set_align('center') + format2.set_align('vcenter') + format2.set_color('white') + format2.set_bg_color('blue') + format4.set_align('center') + format4.set_align('vcenter') + format4.set_color('white') + format4.set_bg_color('green') + if bank_type == 'riyadh': + if report_type == 'salary': + sheet.merge_range('E3:H3', (_("مسير البنك للرواتب")) + " " + date_from + ' - ' + date_to, format4) + if report_type == 'allowance': + sheet.merge_range('E3:H3', (_("مسير البنك للحوافز")) + " " + date_from + ' - ' + date_to, format4) + if report_type == 'overtime': + sheet.merge_range('E3:H3', (_("مسير البنك للعمل الإضافي")) + " " + date_from + ' - ' + date_to, format4) + if report_type == 'training': + sheet.merge_range('E3:H3', (_("مسير البنك للتدريب")) + " " + date_from + ' - ' + date_to, format4) + if report_type == 'mission': + sheet.merge_range('E3:H3', (_("مسير البنك لمهام العمل")) + " " + date_from + ' - ' + date_to, format4) + else: + if report_type == 'salary': + sheet.merge_range('E3:H3', (_("مسير البنك للرواتب")) + " " + date_from + ' - ' + date_to, format2) + if report_type == 'allowance': + sheet.merge_range('E3:H3', (_("مسير البنك للحوافز")) + " " + date_from + ' - ' + date_to, format2) + if report_type == 'overtime': + sheet.merge_range('E3:H3', (_("مسير البنك للعمل الإضافي")) + " " + date_from + ' - ' + date_to, format2) + if report_type == 'training': + sheet.merge_range('E3:H3', (_("مسير البنك للتدريب")) + " " + date_from + ' - ' + date_to, format2) + if report_type == 'mission': + sheet.merge_range('E3:H3', (_("مسير البنك لمهام العمل")) + " " + date_from + ' - ' + date_to, format2) + + + + + sheet.set_column(2, 11, 20) + row = 2 + if not no_details: + for bank in banks: + row += 3 + if bank_type == 'rajhi': + sheet.write(row - 1, 1, bank.name, format3) + sheet.write(row, 2, 'Bank', format2) + sheet.write(row, 3, 'Account #', format2) + sheet.write(row, 4, 'Employee Name', format2) + sheet.write(row, 5, 'Employee Number', format2) + sheet.write(row, 6, 'Legal #', format2) + sheet.write(row, 7, 'Amount', format2) + sheet.write(row, 8, 'Employee Basic Salary', format2) + sheet.write(row, 9, 'Housing Allowance', format2) + sheet.write(row, 10, 'Other Earnings', format2) + sheet.write(row, 11, 'Deductions', format2) + row += 1 + sheet.write(row, 2, 'البنك', format2) + sheet.write(row, 3, 'رقم الحساب', format2) + sheet.write(row, 4, 'إسم الموظف', format2) + sheet.write(row, 5, 'الرقم الوظيفي', format2) + sheet.write(row, 6, 'رقم الهويه/الإقامة', format2) + sheet.write(row, 7, 'المبلغ', format2) + sheet.write(row, 8, 'الراتب الاساسى', format2) + sheet.write(row, 9, 'بدل السكن', format2) + sheet.write(row, 10, 'دخل اخر', format2) + sheet.write(row, 11, 'الخصومات', format2) + elif bank_type == 'alahli': + sheet.write(row - 1, 1, bank.name, format3) + sheet.write(row, 2, 'Bank', format1) + sheet.write(row, 3, 'Account Number', format1) + sheet.write(row, 4, 'Total Salary', format1) + sheet.write(row, 5, 'Transaction Reference', format1) + sheet.write(row, 6, 'Employee Name', format1) + sheet.write(row, 7, 'National ID/Iqama ID', format1) + sheet.write(row, 8, 'Employee Address', format1) + sheet.write(row, 9, 'Basic Salary', format1) + sheet.write(row, 10, 'Housing Allowance', format1) + sheet.write(row, 11, 'Other Earnings', format1) + sheet.write(row, 12, 'Deductions', format1) + elif bank_type == 'riyadh': + sheet.write(row - 1, 1, bank.name, format3) + sheet.write(row, 2, 'SN', format4) + sheet.write(row, 3, 'هوية المستفيد/ المرجع', format4) + sheet.write(row, 4, 'المستفيد / اسم الموظف', format4) + sheet.write(row, 5, 'رقم الحساب ', format4) + sheet.write(row, 6, 'رمز البنك', format4) + sheet.write(row, 7, 'إجمالي المبلغ', format4) + sheet.write(row, 8, 'الراتب الأساسي', format4) + sheet.write(row, 9, 'بدل السكن', format4) + sheet.write(row, 10, 'دخل آخر', format4) + sheet.write(row, 11, 'الخصومات', format4) + sheet.write(row, 12, 'العنوان', format4) + sheet.write(row, 13, 'العملة', format4) + sheet.write(row, 14, 'الحالة', format4) + sheet.write(row, 15, 'وصف الدفع', format4) + sheet.write(row, 16, 'مرجع الدفع', format4) + if report_type == 'salary': + if entry_type == 'all': + if employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id) + ]) + + elif salary: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif salary and employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + else: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + [('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + + elif salary: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|' + , ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + elif salary and employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + else: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + + elif entry_type == 'unposted': + if employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft'), + ]) + + elif salary: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('struct_id', 'in', salary_ids), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft'), + ]) + elif salary and employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', '=', bank.id), '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft')]) + else: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id.bank_account_id.bank_id', '=', bank.id), + '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft')]) + + salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted( + key=lambda v: v.sequence).ids + payslip_line_obj = self.sudo().env['hr.payslip.line'] + sn = 0o001 + for payslip in payslip_ids: + basic = 0.0 + housing = 0.0 + payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)]) + if not payslip_lines_ids: + continue + for payslip_line_rec in payslip_lines_ids: + if payslip_line_rec.salary_rule_id.id in salary_rules: + if payslip_line_rec.salary_rule_id.rules_type == 'salary': + basic += payslip_line_rec.total + elif payslip_line_rec.salary_rule_id.rules_type == 'house': + housing += payslip_line_rec.total + other = round((payslip.total_allowances - basic - housing), 2) + data_list = [payslip.employee_id.emp_no, + payslip.employee_id.name or ' ', + payslip.employee_id.bank_account_id.acc_number or ' ', + payslip.employee_id.bank_account_id.bank_id.bic, + payslip.total_sum, + payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else payslip.employee_id.iqama_number.iqama_id, + basic, housing, other, round((payslip.total_deductions + payslip.total_loans), 2), + payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[5], format1) + sheet.write(row, 13, data_list[11], format1) + sheet.write(row, 14, data_list[12], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + elif report_type == 'allowance': + allowances = self.sudo().env['hr.employee.reward'].search( + ['&', ('date', '>=', date_from), ('date', '<=', date_to), ('state', '=', 'done') + ]) + for allowance in allowances: + reward_line_obj = self.sudo().env['lines.ids.reward'] + if entry_type == 'all': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + elif entry_type == 'unposted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('move_id.state', '=', 'draft'), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not reward_lines_ids: + continue + sn = 0o001 + for reward in reward_lines_ids: + data_list = [reward.employee_id.emp_no, reward.employee_id.name or ' ', + reward.employee_id.bank_account_id.acc_number or ' ', + reward.employee_id.bank_account_id.bank_id.bic, + round(reward.amount, 2), + reward.employee_id.saudi_number.saudi_id if reward.employee_id.check_nationality == True else reward.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + reward.employee_id.branch_id.name if branch else reward.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[5], format1) + sheet.write(row, 13, data_list[12], format1) + sheet.write(row, 14, data_list[13], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + elif report_type == 'overtime': + overtime = self.sudo().env['employee.overtime.request'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('transfer_type', '=', 'accounting'), ('state', '=', 'validated') + ]) + for over in overtime: + reward_line_obj = self.sudo().env['line.ids.over.time'] + if entry_type == 'all': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + elif entry_type == 'unposted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not overtime_lines_ids: + continue + sn = 0o001 + for ove in overtime_lines_ids: + data_list = [ove.employee_id.emp_no, ove.employee_id.name or ' ', + ove.employee_id.bank_account_id.acc_number or ' ', + ove.employee_id.bank_account_id.bank_id.bic, + round(ove.price_hour, 2), + ove.employee_id.saudi_number.saudi_id if ove.employee_id.check_nationality == True else ove.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + ove.employee_id.branch_id.name if branch else ove.employee_id.working_location.name] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[5], format1) + sheet.write(row, 13, data_list[12], format1) + sheet.write(row, 14, data_list[13], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + + elif report_type == 'mission': + missions = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'mission'), + ('state', '=', 'approve') + ]) + for mission in missions: + mission_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'unposted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not mission_lines_ids: + continue + sn = 0o001 + for miss in mission_lines_ids: + data_list = [miss.employee_id.emp_no, miss.employee_id.name or ' ', + miss.employee_id.bank_account_id.acc_number or ' ', + miss.employee_id.bank_account_id.bank_id.bic, + round(miss.amount, 2), + miss.employee_id.saudi_number.saudi_id if miss.employee_id.check_nationality == True else miss.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + miss.employee_id.branch_id.name if branch else miss.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[5], format1) + sheet.write(row, 13, data_list[12], format1) + sheet.write(row, 14, data_list[13], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + elif report_type == 'training': + trainings = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'training'), ('state', '=', 'approve') + ]) + for training in trainings: + training_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + elif entry_type == 'posted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('account_move_id.state', '=', 'posted')]) + elif entry_type == 'unposted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', '=', bank.id)]) + + if not training_lines_ids: + continue + sn = 0o001 + for train in training_lines_ids: + data_list = [train.employee_id.emp_no, train.employee_id.name or ' ', + train.employee_id.bank_account_id.acc_number or ' ', + train.employee_id.bank_account_id.bank_id.bic, + round(train.amount, 2), + train.employee_id.saudi_number.saudi_id if train.employee_id.check_nationality == True else train.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + train.employee_id.branch_id.name if branch else train.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[5], format1) + sheet.write(row, 13, data_list[12], format1) + sheet.write(row, 14, data_list[13], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + else: + + row = 4 + if bank_type == 'rajhi': + sheet.write(3, 2, 'Bank', format2) + sheet.write(3, 3, 'Account #', format2) + sheet.write(3, 4, 'Employee Name', format2) + sheet.write(3, 5, 'Employee Number', format2) + sheet.write(3, 6, 'Legal #', format2) + sheet.write(3, 7, 'Amount', format2) + sheet.write(3, 8, 'Employee Basic Salary', format2) + sheet.write(3, 9, 'Housing Allowance', format2) + sheet.write(3, 10, 'Other Earnings', format2) + sheet.write(3, 11, 'Deductions', format2) + sheet.write(4, 2, 'البنك', format2) + sheet.write(4, 3, 'رقم الحساب', format2) + sheet.write(4, 4, 'إسم الموظف', format2) + sheet.write(4, 5, 'الرقم الوظيفي', format2) + sheet.write(4, 6, 'رقم الهويه/الإقامة', format2) + sheet.write(4, 7, 'المبلغ', format2) + sheet.write(4, 8, 'الراتب الاساسى', format2) + sheet.write(4, 9, 'بدل السكن', format2) + sheet.write(4, 10, 'دخل اخر', format2) + sheet.write(4, 11, 'الخصومات', format2) + elif bank_type == 'alahli': + sheet.write(row, 2, 'Bank', format1) + sheet.write(row, 3, 'Account Number', format1) + sheet.write(row, 4, 'Total Salary', format1) + sheet.write(row, 5, 'Transaction Reference', format1) + sheet.write(row, 6, 'Employee Name', format1) + sheet.write(row, 7, 'National ID/Iqama ID', format1) + sheet.write(row, 8, 'Employee Address', format1) + sheet.write(row, 9, 'Basic Salary', format1) + sheet.write(row, 10, 'Housing Allowance', format1) + sheet.write(row, 11, 'Other Earnings', format1) + sheet.write(row, 12, 'Deductions', format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, 'SN', format4) + sheet.write(row, 3, 'هوية المستفيد/ المرجع', format4) + sheet.write(row, 4, 'المستفيد / اسم الموظف', format4) + sheet.write(row, 5, 'رقم الحساب ', format4) + sheet.write(row, 6, 'رمز البنك', format4) + sheet.write(row, 7, 'إجمالي المبلغ', format4) + sheet.write(row, 8, 'الراتب الأساسي', format4) + sheet.write(row, 9, 'بدل السكن', format4) + sheet.write(row, 10, 'دخل آخر', format4) + sheet.write(row, 11, 'الخصومات', format4) + sheet.write(row, 12, 'العنوان', format4) + sheet.write(row, 13, 'العملة', format4) + sheet.write(row, 14, 'الحالة', format4) + sheet.write(row, 15, 'وصف الدفع', format4) + sheet.write(row, 16, 'مرجع الدفع', format4) + if report_type == 'salary': + if entry_type == 'all': + if employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + elif salary: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif salary and employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + else: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + + elif salary: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + elif salary and employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), ('employee_id', 'in', employees.ids), + ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted')]) + else: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'posted'), ('payslip_run_id.move_id.state', '=', 'posted') + ]) + elif entry_type == 'unposted': + if employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + + elif salary: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('struct_id', 'in', salary_ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + elif salary and employees: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id', 'in', employees.ids), ('struct_id', 'in', salary.ids), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + else: + payslip_ids = self.sudo().env['hr.payslip'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('state', '=', 'transfered'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids) + , '|', + ('move_id.state', '=', 'draft'), ('payslip_run_id.move_id.state', '=', 'draft') + ]) + salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted( + key=lambda v: v.sequence).ids + payslip_line_obj = self.sudo().env['hr.payslip.line'] + sn = 0o001 + for payslip in payslip_ids: + + # sheet.write(row - 1, 1, bank.name, format3) + + basic = 0.0 + housing = 0.0 + payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)]) + if not payslip_lines_ids: + continue + for payslip_line_rec in payslip_lines_ids: + if payslip_line_rec.salary_rule_id.id in salary_rules: + if payslip_line_rec.salary_rule_id.rules_type == 'salary': + basic += payslip_line_rec.total + elif payslip_line_rec.salary_rule_id.rules_type == 'house': + housing += payslip_line_rec.total + other = round((payslip.total_allowances - basic - housing), 2) + data_list = [payslip.employee_id.emp_no, payslip.employee_id.name or ' ', + payslip.employee_id.bank_account_id.acc_number or ' ', + payslip.employee_id.bank_account_id.bank_id.bic, + payslip.total_sum, + payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else payslip.employee_id.iqama_number.iqama_id, + basic, housing, other, round((payslip.total_deductions + payslip.total_loans), 2), + payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + print("============================", + payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name) + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[10], format1) + sheet.write(row, 13, data_list[11], format1) + sheet.write(row, 14, data_list[12], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + + elif report_type == 'allowance': + allowances = self.sudo().env['hr.employee.reward'].search( + ['&', ('date', '>=', date_from), ('date', '<=', date_to), ('state', '=', 'done') + ]) + for allowance in allowances: + reward_line_obj = self.sudo().env['lines.ids.reward'] + if entry_type == 'all': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + reward_lines_ids = reward_line_obj.sudo().search( + [('employee_reward_id', '=', allowance.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not reward_lines_ids: + continue + sn = 0o001 + for reward in reward_lines_ids: + data_list = [reward.employee_id.emp_no, reward.employee_id.name or ' ', + reward.employee_id.bank_account_id.acc_number or ' ', + reward.employee_id.bank_account_id.bank_id.bic, + round(reward.amount, 2), + reward.employee_id.saudi_number.saudi_id if reward.employee_id.check_nationality == True else reward.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + reward.employee_id.branch_id.name if branch else reward.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[10], format1) + sheet.write(row, 13, data_list[11], format1) + sheet.write(row, 14, data_list[12], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + elif report_type == 'overtime': + overtime = self.sudo().env['employee.overtime.request'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), ('state', '=', 'validated'), + ('transfer_type', '=', 'accounting') + ]) + for over in overtime: + reward_line_obj = self.sudo().env['line.ids.over.time'] + if entry_type == 'all': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'posted')]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('move_id.state', '=', 'draft')]) + else: + overtime_lines_ids = reward_line_obj.sudo().search( + [('employee_over_time_id', '=', over.id), ('move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not overtime_lines_ids: + continue + sn = 0o001 + for ove in overtime_lines_ids: + data_list = [ove.employee_id.emp_no, + ove.employee_id.name or ' ', + ove.employee_id.bank_account_id.acc_number or ' ', + ove.employee_id.bank_account_id.bank_id.bic, + round(ove.price_hour, 2), + ove.employee_id.saudi_number.saudi_id if ove.employee_id.check_nationality == True else ove.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + ove.employee_id.branch_id.name if branch else ove.employee_id.working_location.name + ,company_id.currency_id.name,'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[10], format1) + sheet.write(row, 13, data_list[11], format1) + sheet.write(row, 14, data_list[12], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + elif report_type == 'mission': + missions = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'mission'), + ('state', '=', 'approve') + ]) + for mission in missions: + mission_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'posted'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'unposted': + if employees: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + mission_lines_ids = mission_line_obj.sudo().search( + [('official_mission_id', '=', mission.id), ('account_move_id.state', '=', 'draft'), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + + if not mission_lines_ids: + continue + sn = 0o001 + for miss in mission_lines_ids: + data_list = [miss.employee_id.emp_no, miss.employee_id.name or ' ', + miss.employee_id.bank_account_id.acc_number or ' ', + miss.employee_id.bank_account_id.bank_id.bic, + round(miss.amount, 2), + miss.employee_id.saudi_number.saudi_id if miss.employee_id.check_nationality == True else miss.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + miss.employee_id.branch_id.name if branch else miss.employee_id.working_location.name, + company_id.currency_id.name, 'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[10], format1) + sheet.write(row, 13, data_list[11], format1) + sheet.write(row, 14, data_list[12], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 + elif report_type == 'training': + trainings = self.sudo().env['hr.official.mission'].search( + ['&', ('date_from', '>=', date_from), ('date_to', '<=', date_to), + ('process_type', '=', 'training'), ('state', '=', 'approve') + ]) + for training in trainings: + training_line_obj = self.sudo().env['hr.official.mission.employee'] + if entry_type == 'all': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids)]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids)]) + elif entry_type == 'posted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'posted')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('account_move_id.state', '=', 'posted')]) + elif entry_type == 'unposted': + if employees: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('employee_id', 'in', employees.ids), ('account_move_id.state', '=', 'draft')]) + else: + training_lines_ids = training_line_obj.sudo().search( + [('official_mission_id', '=', training.id), + ('employee_id.bank_account_id.bank_id', 'in', all_bank.ids), + ('account_move_id.state', '=', 'draft')]) + + if not training_lines_ids: + continue + sn = 0o001 + for train in training_lines_ids: + data_list = [train.employee_id.emp_no, train.employee_id.name or ' ', + train.employee_id.bank_account_id.acc_number or ' ', + train.employee_id.bank_account_id.bank_id.bic, + round(train.amount, 2), + train.employee_id.saudi_number.saudi_id if train.employee_id.check_nationality == True else train.employee_id.iqama_number.iqama_id, + 0.0, 0.0, 0.0, 0.0, + train.employee_id.branch_id.name if branch else train.employee_id.working_location.name, + company_id.currency_id.name,'Active'] + col = 1 + row += 1 + col += 1 + if bank_type == 'rajhi': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[5], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + elif bank_type == 'alahli': + sheet.write(row, 2, data_list[3], format1) + sheet.write(row, 3, data_list[2], format1) + sheet.write(row, 4, data_list[4], format1) + sheet.write(row, 5, data_list[0], format1) + sheet.write(row, 6, data_list[1], format1) + sheet.write(row, 7, data_list[5], format1) + sheet.write(row, 8, data_list[10], format1) + sheet.write(row, 9, data_list[6], format1) + sheet.write(row, 10, data_list[7], format1) + sheet.write(row, 11, data_list[8], format1) + sheet.write(row, 12, data_list[9], format1) + elif bank_type == 'riyadh': + sheet.write(row, 2, sn, format1) + sheet.write(row, 3, data_list[5], format1) + sheet.write(row, 4, data_list[1], format1) + sheet.write(row, 5, data_list[2], format1) + sheet.write(row, 6, data_list[3], format1) + sheet.write(row, 7, data_list[4], format1) + sheet.write(row, 8, data_list[6], format1) + sheet.write(row, 9, data_list[7], format1) + sheet.write(row, 10, data_list[8], format1) + sheet.write(row, 11, data_list[9], format1) + sheet.write(row, 12, data_list[10], format1) + sheet.write(row, 13, data_list[11], format1) + sheet.write(row, 14, data_list[12], format1) + sheet.write(row, 15, report_type, format1) + sheet.write(row, 16, data_list[0], format1) + sn += 1 diff --git a/odex30_base/exp_payroll_custom/report/payslip_monthly_report.py b/odex30_base/exp_payroll_custom/report/payslip_monthly_report.py new file mode 100644 index 0000000..f41a6a6 --- /dev/null +++ b/odex30_base/exp_payroll_custom/report/payslip_monthly_report.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- +from odoo import models, api, _ +from odoo.exceptions import ValidationError + + +class PayslipMonthlyReport(models.AbstractModel): + _name = 'report.exp_payroll_custom.payslip_monthly_report' + _description = 'Payslip Monthly Report' + + def get_rule_values(self, data=None, context={}): + docs = [] + payslip_line = self.env['hr.payslip.line'] + count = 0 + exception = False + if data['delist'] == 'ff': + ftotal = 0 + title = _('Allowances and deduction Totals') + docs.append({'count': '#', 'rule': _('Name'), 'type': _('Type'), 'amount': _('Amount'), }) + rules = self.env[data['model']].browse(data['ids']).sorted('sequence') + for rule in rules: + count += 1 + total = sum(payslip_line.browse(data['payslip_line_ids']).filtered( + lambda r: r.salary_rule_id.id == rule.id).mapped('total')) + ftotal += total + docs.append({ + 'count': count, + 'rule': rule.name, + 'type': _(dict(rule.category_id._fields['rule_type'].selection, context={}).get( + rule.category_id.rule_type)), + 'amount': "{:.2f}".format(total), + }) + docs.append({'count': '', 'rule': _('Total'), 'type': '', 'amount': "{:.2f}".format(ftotal), }) + elif data['delist'] == 'tt': + # TODO review bellow raise + if not data['payslip_line_ids'] or not data['rule_ids']: raise ValidationError( + _('Sorry No Data To Be Printed')) + title = _('Employees Paysheet') + rule_dict = {} + sorted_rules = self.env['hr.salary.rule'].browse(data['rule_ids']).sorted('sequence') + for line in sorted_rules: + rule_dict.setdefault(line.category_id.rule_type, []) + rule_dict[line.category_id.rule_type] += line + tdict = {'count': '#', 'emp_no':_('EMP #'),'emp': _('Name'), } + ndict = {'count': '', 'emp_no': _('Nets'),'emp':'' } + for key, value in rule_dict.items(): + for x in value: + tdict[x.id], ndict[x.id] = x.name, 0 + rule_type_name = dict( + x.category_id._fields['rule_type']._description_selection(self.env) + ).get(key) + tdict[key], ndict[key] = _('%s %s') % (_('Total'), rule_type_name), 0 + tdict['net'], ndict['net'], = _('Net'), 0 + if self.env.context.get('track_emp', False): tdict['track_id'] = 'track_id' + docs.append(tdict) + fnet = 0 + for emp in self.env[data['model']].browse(data['ids']): + emp_dict = {} + count += 1 + net = 0 + for key, value in tdict.items(): + if value == _('#'): + emp_dict[key] = count + continue + elif value == _('EMP #'): + emp_dict[key] = emp.emp_no + continue + elif value == _('Name'): + emp_dict[key] = emp.name + continue + elif value == 'track_id': + emp_dict['track_id'] = emp.id + continue + elif isinstance(key, int): + total = sum(payslip_line.browse(data['payslip_line_ids']).filtered( + lambda r: r.employee_id.id == emp.id and r.salary_rule_id.id == key).mapped('total')) + emp_dict[key] = "{:.2f}".format(total) + net += total + fnet += total + ndict[key] += total + elif isinstance(key, str): + total = sum(payslip_line.browse(data['payslip_line_ids']).filtered( + lambda + r: r.employee_id.id == emp.id and r.salary_rule_id.category_id.rule_type == key).mapped( + 'total')) + emp_dict[key] = "{:.2f}".format(total) + ndict[key] += total + elif isinstance(key, bool): + total = sum(payslip_line.browse(data['payslip_line_ids']).filtered( + lambda + r: r.employee_id.id == emp.id and r.salary_rule_id.category_id.rule_type == False).mapped( + 'total')) + emp_dict[key] = "{:.2f}".format(total) + ndict[key] += total + if value == _('Net'): + emp_dict[key] = "{:.2f}".format(net) + continue + docs.append(emp_dict) + for key in ndict: + if isinstance(ndict[key], (int, float)) and key not in ['count', 'emp_no', 'emp']: + ndict[key] = "{:.2f}".format(ndict[key]) + ndict['net'] = "{:.2f}".format(fnet) + docs.append(ndict) + else: + title = _('Specific Allowance and deduction Report') + exception = True + rules = self.env[data['model']].browse(data['ids']).sorted('sequence') + for rule in rules: + count = 0 + ftotal = 0 + inner_doc = {'rule': rule.name, 'lines': [], } + inner_doc['lines'].append({'count': '#', 'emp_no': _('EMP #'), 'emp': _('Employee'), 'amount': _('Amount'), }) + for emp in set(payslip_line.browse(data['payslip_line_ids']).filtered( + lambda r: r.salary_rule_id.id == rule.id).mapped('employee_id')): + count += 1 + total = sum(payslip_line.browse(data['payslip_line_ids']).filtered( + lambda r: r.employee_id.id == emp.id and r.salary_rule_id.id == rule.id).mapped('total')) + ftotal += total + inner_doc['lines'].append({'count': count, 'emp_no': emp.emp_no, 'emp': emp.name, 'amount': "{:.2f}".format(total), }) + inner_doc['lines'].append({'count': '', 'emp_no': _('Total'),'emp':'', 'amount': "{:.2f}".format(ftotal), }) + docs.append(inner_doc) + return title, exception, docs + + @api.model + def _get_report_values(self, docids, data=None): + title, exception, docs = self.get_rule_values(data) + return { + 'exception': exception, + 'title': title, + 'date_from': data['form']['date_from'], + 'date_to': data['form']['date_to'], + 'docs': docs, + } + + +class PayslipMonthlyReportXlsx(models.AbstractModel): + _name = "report.exp_payroll_custom.payslip_monthly_report_xlsx" + _inherit = 'report.report_xlsx.abstract' + + @api.model + def generate_xlsx_report(self, workbook, data, objs): + title, exception, docs = self.env['report.exp_payroll_custom.payslip_monthly_report'].get_rule_values(data) + sheet = workbook.add_worksheet('Proll Monthly report') + format1 = workbook.add_format({'bottom': True, 'right': True, 'left': True, 'top': True, 'align': 'center', }) + format2 = workbook.add_format({'font_size': 14, 'bottom': True, 'right': True, 'left': True, 'top': True, + 'align': 'center', 'bold': True}) + format2.set_align('center') + format2.set_align('vcenter') + format3 = workbook.add_format({'bottom': True, 'align': 'center', 'bold': True, }) + format_amount = workbook.add_format({ + 'bottom': True, 'right': True, 'left': True, 'top': True, 'align': 'center', + 'num_format': '#,##0.00', + }) + if data['delist'] != 'tf': + sheet.merge_range('C5:F5', title, format2) + sheet.merge_range('C6:F6', data['form']['date_from'] + ' - ' + data['form']['date_to'], format2) + else: + sheet.merge_range('C5:E5', title, format2) + sheet.merge_range('C6:E6', data['form']['date_from'] + ' - ' + data['form']['date_to'], format2) + sheet.set_column('C:C', 10) + sheet.set_column('D:D', 40) + # sheet.set_column('E:Z', 20) + row = 6 + for line in docs: + if data['delist'] != 'tf': + row += 1 + clm = 1 + for k, v in line.items(): + clm += 1 + if isinstance(v, (int, float)): + sheet.write(row, clm, v, format_amount) + else: + sheet.write(row, clm, v, format1) + #sheet.write(row, clm, line[k], format1) + else: + row += 1 + clm = 2 + sheet.write(row, clm, line['rule'], format3) + for ln in line['lines']: + row += 1 + clm = 1 + for k, v in ln.items(): + clm += 1 + if isinstance(v, (int, float)): + sheet.write(row, clm, v, format_amount) + else: + sheet.write(row, clm, v, format1) + #sheet.write(row, clm, ln[k], format1) + row += 1 diff --git a/odex30_base/exp_payroll_custom/security/ir.model.access.csv b/odex30_base/exp_payroll_custom/security/ir.model.access.csv new file mode 100644 index 0000000..e4cb6e0 --- /dev/null +++ b/odex30_base/exp_payroll_custom/security/ir.model.access.csv @@ -0,0 +1,55 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_salary_advance_user,salary.advance,model_salary_advance,base.group_user,1,1,1,0 +access_salary_advance_officer,salary.advance,model_salary_advance,hr.group_hr_user,1,1,1,1 +access_salary_advance_manager,salary.advance,model_salary_advance,hr.group_hr_manager,1,1,1,1 +access_account_manager,salary.advance,model_salary_advance,account.group_account_manager,1,1,1,1 +access_hr_salary_rule_emp,hr.salary.rule.emp,model_hr_salary_rule,base.group_user,1,0,0,0 +access_hr_salary_rule_category_emp,hr.salary.rule.category.emp,model_hr_salary_rule_category,base.group_user,1,0,0,0 +access_hr_payroll_structure_emp,hr.payroll.structure.emp,model_hr_payroll_structure,base.group_user,1,0,0,0 +access_hr_reward_line_officer,hr.reward.line.officer,model_lines_ids_reward,hr.group_hr_user,1,0,0,0 +access_hr_reward_line_hr_manager,hr.reward.line.hr.manager,model_lines_ids_reward,hr.group_hr_manager,1,1,1,0 +access_hr_reward_line_division,hr.reward.line.division,model_lines_ids_reward,hr_base.group_division_manager,1,1,1,1 +access_hr_reward_line_general_manager,hr.reward.line.gm,model_lines_ids_reward,hr_base.group_general_manager,1,1,0,0 +access_hr_reward_line_department,hr.reward.department,model_lines_ids_reward,hr_base.group_department_manager,1,0,0,0 +access_hr_reward_hr_user,hr.reward.hr.user,model_hr_employee_reward,hr.group_hr_user,1,1,0,0 +access_hr_reward_hr_manager,hr.reward.hr.manager,model_hr_employee_reward,hr.group_hr_manager,1,1,1,0 +access_hr_reward_division,hr.reward.division,model_hr_employee_reward,hr_base.group_division_manager,1,1,1,1 +access_hr_reward_general_manager,hr.reward.gm,model_hr_employee_reward,hr_base.group_general_manager,1,1,0,0 +access_hr_reward_department,hr.reward.department,model_hr_employee_reward,hr_base.group_department_manager,1,0,0,0 +access_hr_employee_reward,access_hr_employee_reward,model_hr_employee_reward,base.group_user,1,0,0,0 +access_hr_contract_advantage_officer,hr_contract_advantage_officer,model_contract_advantage,hr.group_hr_user,1,1,1,0 +access_hr_contract_advantage_manager,hr_contract_advantage_manager,model_contract_advantage,hr.group_hr_manager,1,1,1,1 +access_hr_contract_advantage_executive,hr_contract_advantage_executive,model_contract_advantage,hr_base.group_executive_manager,1,1,0,0 +access_hr_contract_advantage_general_manager,hr_contract_advantage_gm,model_contract_advantage,hr_base.group_general_manager,1,0,0,0 +access_hr_contract_advantage_employee,hr_contract_advantage_employee,model_contract_advantage,base.group_user,1,0,0,0 +access_payslip_loans_user,payslip.loans.payroll.user,model_payslip_loans,exp_hr_payroll.group_hr_payroll_user,1,1,1,1 +access_hr_payslip_payroll_contributor,hr.payslip.line.payroll.contributor,model_hr_payslip,exp_payroll_custom.group_hr_payroll_contributor,1,1,0,0 +access_hr_payslip_line_payroll_contributor,hr.payslip.line.payroll.contributor,model_hr_payslip_line,exp_payroll_custom.group_hr_payroll_contributor,1,1,0,0 +exp_hr_payroll.access_hr_payslip_run,hr.payslip.run,model_hr_payslip_run,exp_hr_payroll.group_hr_payroll_user,1,1,1,1 +access_employee_promotions_contributor,employee.promotions.contributor,model_employee_promotions,exp_payroll_custom.group_hr_payroll_contributor,1,1,0,0 +access_employee_promotions_payroll_user,employee.promotions.payroll.user,model_employee_promotions,exp_hr_payroll.group_hr_payroll_user,1,1,1,1 +access_hr_payslip_emp,hr.payslip.emp,model_hr_payslip,base.group_user,1,0,0,0 +access_hr_payslip_line_emp,hr.payslip.line.emp,model_hr_payslip_line,base.group_user,1,0,0,0 +access_payslip_loans_emp,payslip.loans.emp,model_payslip_loans,base.group_user,1,0,0,0 +access_related_salary_amount_officer,related_salary_amount_officer,model_related_salary_amount,hr.group_hr_user,1,1,1,1 +access_related_salary_amount_emp,related_salary_amount_emp,model_related_salary_amount,base.group_user,1,0,0,0 +access_payroll_bank_wiz_user,access_payroll_bank_wiz_user,model_payroll_bank_wiz,hr.group_hr_user,1,1,1,0 +access_payslip_monthly_report_user,access_payslip_monthly_report_user,model_payslip_monthly_report,hr.group_hr_user,1,1,1,0 +access_employee_selection_wizard_hr_manager,employee.selection.wizard.hr.manager,model_employee_selection_wizard,,1,1,1,1 +access_hr_salary_rule_account_emp,hr_salary_rule_account_emp,model_hr_salary_rule_account,base.group_user,1,0,0,0 +access_hr_salary_rule_account_hr_user,hr_salary_rule_account_hr_user,model_hr_salary_rule_account,hr.group_hr_user,1,1,1,1 +access_hr_payslip_finance_review,access_hr_payslip_finance_review,model_hr_payslip,exp_payroll_custom.group_payroll_finance_review,1,1,0,0 +access_hr_payslip_expense_manger,access_hr_payslip_expense_manger,model_hr_payslip,exp_payroll_custom.group_payroll_expense_manger,1,1,0,0 +access_hr_payslip_account_manager,access_hr_payslip_account_manager,model_hr_payslip,hr_base.group_account_manager,1,1,0,0 +access_hr_payslip_hr_manager,access_hr_payslip_hr_manager,model_hr_payslip,hr.group_hr_manager,1,1,0,0 +access_hr_payslip_general_manager,access_hr_payslip_general_manager,model_hr_payslip,hr_base.group_general_manager,1,1,0,0 +access_hr_payslip_run_finance_review,access_hr_payslip_finance_review,model_hr_payslip_run,exp_payroll_custom.group_payroll_finance_review,1,1,0,0 +access_hr_payslip_run_expense_manger,access_hr_payslip_expense_manger,model_hr_payslip_run,exp_payroll_custom.group_payroll_expense_manger,1,1,0,0 +access_hr_payslip_run_account_manager,access_hr_payslip_account_manager,model_hr_payslip_run,hr_base.group_account_manager,1,1,0,0 +access_hr_payslip_run_hr_manager,access_hr_payslip_hr_manager,model_hr_payslip_run,hr.group_hr_manager,1,1,0,0 +access_hr_payslip_run_general_manager,access_hr_payslip_general_manager,model_hr_payslip_run,hr_base.group_general_manager,1,1,0,0 +access_hr_payslip_line_finance_review,access_hr_payslip_finance_review,model_hr_payslip_line,exp_payroll_custom.group_payroll_finance_review,1,1,0,0 +access_hr_payslip_line_expense_manger,access_hr_payslip_expense_manger,model_hr_payslip_line,exp_payroll_custom.group_payroll_expense_manger,1,1,0,0 +access_hr_payslip_line_account_manager,access_hr_payslip_account_manager,model_hr_payslip_line,hr_base.group_account_manager,1,1,0,0 +access_hr_payslip_line_hr_manager,access_hr_payslip_hr_manager,model_hr_payslip_line,hr.group_hr_manager,1,1,0,0 +access_hr_payslip_line_general_manager,access_hr_payslip_general_manager,model_hr_payslip_line,hr_base.group_general_manager,1,1,0,0 \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/security/ir_rule.xml b/odex30_base/exp_payroll_custom/security/ir_rule.xml new file mode 100644 index 0000000..4ed1428 --- /dev/null +++ b/odex30_base/exp_payroll_custom/security/ir_rule.xml @@ -0,0 +1,134 @@ + + + + Employee promotions multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Employee reward multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Employee hr payslip run multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Employee hr_salary_rule multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Employee hr_salary_rule multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Advance Request Multi Company + + + ['|', ('company_id','=',False), ('company_id', 'in',company_ids)] + + + + + Employee hr payslip multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Employee contract advantage multi company rule + + ['|', ('company_id','=',False), ('company_id', 'in', + company_ids)] + + + + Salary Advance + + + + + + + + + + Salary Advance Rule For Employee + [('employee_id.user_id','=',user.id)] + + + + + + + + + + + Employee Payslip + + [('employee_id.user_id.id', '=', user.id),('state', '=', + 'transfered')] + + + + + All Payslip + + [(1,'=',1)] + + + + + Employee: views its promotions only + + [('employee_id.user_id','=',user.id)] + + + + + Division/Dep Manager: views promotions of its subordinates + + ['|','|',('employee_id.department_id.manager_id','=',False), + ('employee_id.department_id.manager_id.user_id','=', user.id), + ('employee_id.department_id.parent_id.manager_id.user_id','=', user.id)] + + + + + + Manager: views promotions of all employee + + [(1 ,'=', 1)] + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/security/security.xml b/odex30_base/exp_payroll_custom/security/security.xml new file mode 100644 index 0000000..41ee2db --- /dev/null +++ b/odex30_base/exp_payroll_custom/security/security.xml @@ -0,0 +1,21 @@ + + + + + + Contributor + This user is a participant in payroll related processes. + + + + Finance Review + + + + + Expense Manager + + + + + diff --git a/odex30_base/exp_payroll_custom/static/description/icon.png b/odex30_base/exp_payroll_custom/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4141f52daab6a780510b5f4e3dd762511add78fa GIT binary patch literal 32929 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 OgtWK + + + + + diff --git a/odex30_base/exp_payroll_custom/templates/employee_cost_template.xml b/odex30_base/exp_payroll_custom/templates/employee_cost_template.xml new file mode 100644 index 0000000..e8461f6 --- /dev/null +++ b/odex30_base/exp_payroll_custom/templates/employee_cost_template.xml @@ -0,0 +1,98 @@ + + + + + + + + hr.contract + Employee Cost Report + qweb-pdf + exp_payroll_custom.employee_cost_report + exp_payroll_custom.employee_cost_report + + + hr.contract + Employee Cost Report + xlsx + exp_payroll_custom.employee_cost_report_xlsx + exp_payroll_custom.employee_cost_report_xlsx + + + + diff --git a/odex30_base/exp_payroll_custom/templates/hr_payslip_run_template.xml b/odex30_base/exp_payroll_custom/templates/hr_payslip_run_template.xml new file mode 100644 index 0000000..b62f1f6 --- /dev/null +++ b/odex30_base/exp_payroll_custom/templates/hr_payslip_run_template.xml @@ -0,0 +1,95 @@ + + + + + + paperformat.payslips.batches.recap.report + + A3 + 0 + 0 + Portrait + 30 + 5 + 10 + 5 + + 20 + 90 + + + + + + + + hr.payslip.run + Payslips Batches Report + qweb-pdf + exp_payroll_custom.hr_payslip_run_report_pdf_template + exp_payroll_custom.hr_payslip_run_report_pdf_template + + + + diff --git a/odex30_base/exp_payroll_custom/templates/payroll_bank_text.xml b/odex30_base/exp_payroll_custom/templates/payroll_bank_text.xml new file mode 100644 index 0000000..bcb5fc7 --- /dev/null +++ b/odex30_base/exp_payroll_custom/templates/payroll_bank_text.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/odex30_base/exp_payroll_custom/templates/payslip_monthly_report.xml b/odex30_base/exp_payroll_custom/templates/payslip_monthly_report.xml new file mode 100644 index 0000000..b10a8b4 --- /dev/null +++ b/odex30_base/exp_payroll_custom/templates/payslip_monthly_report.xml @@ -0,0 +1,134 @@ + + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/templates/report_payslip.xml b/odex30_base/exp_payroll_custom/templates/report_payslip.xml new file mode 100644 index 0000000..00178c7 --- /dev/null +++ b/odex30_base/exp_payroll_custom/templates/report_payslip.xml @@ -0,0 +1,84 @@ + + + + + + diff --git a/odex30_base/exp_payroll_custom/templates/report_payslip_details.xml b/odex30_base/exp_payroll_custom/templates/report_payslip_details.xml new file mode 100644 index 0000000..f91dedf --- /dev/null +++ b/odex30_base/exp_payroll_custom/templates/report_payslip_details.xml @@ -0,0 +1,38 @@ + + + + + + diff --git a/odex30_base/exp_payroll_custom/views/bank_pdf_report.xml b/odex30_base/exp_payroll_custom/views/bank_pdf_report.xml new file mode 100644 index 0000000..38668a7 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/bank_pdf_report.xml @@ -0,0 +1,612 @@ + + + + + + + + diff --git a/odex30_base/exp_payroll_custom/views/company_custom.xml b/odex30_base/exp_payroll_custom/views/company_custom.xml new file mode 100644 index 0000000..792ae79 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/company_custom.xml @@ -0,0 +1,16 @@ + + + + + res.company.form.inherit + res.company + + + + + + + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/contract_advantage.xml b/odex30_base/exp_payroll_custom/views/contract_advantage.xml new file mode 100644 index 0000000..1488146 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/contract_advantage.xml @@ -0,0 +1,128 @@ + + + + + + Employee Allowances and Deductions + contract.advantage + list,form + + + + + Employee Allowances and Deductions + contract.advantage + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + + Employee Allowances and Deductions + contract.advantage + + + + + + + + + + + + + + + + Employee Allowances and Deductions + contract.advantage + search + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/employee_promotions_view.xml b/odex30_base/exp_payroll_custom/views/employee_promotions_view.xml new file mode 100644 index 0000000..401d063 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/employee_promotions_view.xml @@ -0,0 +1,158 @@ + + + + + Employee promotions + ir.actions.act_window + employee.promotions + list,form + +

+ Create Requests. +

+
+
+ + + employee.promotions.form + employee.promotions + +
+
+
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+
+
+
+ + + employee.promotions.tree + employee.promotions + + + + + + + + + + + + + + + + + Promotions Action + employee.promotions + list,form + {'search_default_employee_id': active_id, 'default_employee_id': + active_id} + [('state','=','approved'), ('employee_id','=',active_id)] + +

+ Click to add a Promotions... +

+
+
+ + + hr.employee + hr.employee + + 14 + + + + + + +
+
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/employee_reward_view.xml b/odex30_base/exp_payroll_custom/views/employee_reward_view.xml new file mode 100644 index 0000000..f1dfa9a --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/employee_reward_view.xml @@ -0,0 +1,171 @@ + + + + + Employee Reward and Allowances + hr.employee.reward + list,form + + + + + Employee Reward and Allowances + hr.employee.reward + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + + Employee Reward and Allowances + hr.employee.reward + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/hr_contract.xml b/odex30_base/exp_payroll_custom/views/hr_contract.xml new file mode 100644 index 0000000..4995683 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_contract.xml @@ -0,0 +1,109 @@ + + + + + hr.contract.salary.form.inherit + hr.contract + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/hr_employee.xml b/odex30_base/exp_payroll_custom/views/hr_employee.xml new file mode 100644 index 0000000..dd2980e --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_employee.xml @@ -0,0 +1,22 @@ + + + inheriting hr_employee form view + + hr.employee.form.view + hr.employee + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/hr_recontract.xml b/odex30_base/exp_payroll_custom/views/hr_recontract.xml new file mode 100644 index 0000000..63c1a7d --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_recontract.xml @@ -0,0 +1,44 @@ + + + + + + hr.re.contract.inherited.form.view + hr.re.contract + + + + + + + + + + + + + + diff --git a/odex30_base/exp_payroll_custom/views/hr_salary_menus.xml b/odex30_base/exp_payroll_custom/views/hr_salary_menus.xml new file mode 100644 index 0000000..79c851a --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_salary_menus.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + diff --git a/odex30_base/exp_payroll_custom/views/hr_salary_rules.xml b/odex30_base/exp_payroll_custom/views/hr_salary_rules.xml new file mode 100644 index 0000000..6e11786 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_salary_rules.xml @@ -0,0 +1,189 @@ + + + + + hr.salary.rule.form.inherit + hr.salary.rule + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + hr.salary.rule.category.inherited.form + hr.salary.rule.category + + + + + + + + + + + hr.salary.rule.list + hr.salary.rule + + + + 1 + + + + + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/hr_salary_scale.xml b/odex30_base/exp_payroll_custom/views/hr_salary_scale.xml new file mode 100644 index 0000000..0555d0d --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_salary_scale.xml @@ -0,0 +1,140 @@ + + + + + + salary.scale.filter.view + hr.payroll.structure + + + + + + + + + + + + + + + Salary Scale + ir.actions.act_window + hr.payroll.structure + list,form + [('type','=','scale')] + {'default_type':'scale', 'search_default_scale': 1} + + + + + + Salary Scale + hr.payroll.structure + + + +
+ +
+
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/hr_salary_scale_level.xml b/odex30_base/exp_payroll_custom/views/hr_salary_scale_level.xml new file mode 100644 index 0000000..47c3a59 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_salary_scale_level.xml @@ -0,0 +1,29 @@ + + + + + Salary Scale Level + ir.actions.act_window + hr.payroll.structure + list,form + [('type','=','level')] + {'default_type':'level', 'search_default_level': 1} + + + + + Salary Scale Level + hr.payroll.structure + + + + + + + + + + + \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/hr_salary_scale_level_degree.xml b/odex30_base/exp_payroll_custom/views/hr_salary_scale_level_degree.xml new file mode 100644 index 0000000..2fd6e99 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/hr_salary_scale_level_degree.xml @@ -0,0 +1,16 @@ + + + + + + Salary Scale Level Degree + ir.actions.act_window + hr.payroll.structure + tree,form + [('type','=','degree')] + {'default_type':'degree', 'search_default_degree': 1} + + + + + diff --git a/odex30_base/exp_payroll_custom/views/menu_security_cus.xml b/odex30_base/exp_payroll_custom/views/menu_security_cus.xml new file mode 100644 index 0000000..5761274 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/menu_security_cus.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/odex30_base/exp_payroll_custom/views/payroll_menus.xml b/odex30_base/exp_payroll_custom/views/payroll_menus.xml new file mode 100644 index 0000000..e47d7b0 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/payroll_menus.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/odex30_base/exp_payroll_custom/views/payroll_report.xml b/odex30_base/exp_payroll_custom/views/payroll_report.xml new file mode 100644 index 0000000..87d8ce8 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/payroll_report.xml @@ -0,0 +1,64 @@ + + + + + hr.payslip + Payslip PDF Report + qweb-pdf + exp_payroll_custom.payslip_monthly_report + exp_payroll_custom.payslip_monthly_report + + + + hr.payslip + Payslip Xlsx Report + xlsx + exp_payroll_custom.payslip_monthly_report_xlsx + exp_payroll_custom.payslip_monthly_report_xlsx + + + + hr.payslip + Payslip PDF Report + qweb-pdf + exp_payroll_custom.payslip_monthly_report + exp_payroll_custom.payslip_monthly_report + + + + + + hr.payslip + Payslip Xlsx Report + xlsx + exp_payroll_custom.payslip_monthly_report_xlsx + exp_payroll_custom.payslip_monthly_report_xlsx + + + + payroll.bank.wiz + Payroll Bank Report + xlsx + exp_payroll_custom.report_payroll_bank_xlsx + Payroll Bank Report + False + + + + payroll.bank.wiz + Payroll Bank Report + qweb-pdf + exp_payroll_custom.report_payroll_bank_pdf + False + + + payroll.bank.wiz + Print Text + qweb-text + exp_payroll_custom.report_payroll_bank_pdf_docx + report + False + + + + diff --git a/odex30_base/exp_payroll_custom/views/payslip_view.xml b/odex30_base/exp_payroll_custom/views/payslip_view.xml new file mode 100644 index 0000000..582d878 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/payslip_view.xml @@ -0,0 +1,339 @@ + + + + hr.payslip.form + hr.payslip + + + +
+
+
+ + + 0 + 0 + 0 + + + + 1 + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + hr.payslip.run.inherit.form + hr.payslip.run + + + +
+
+
+ + 1 + + + + + 1 + + + 1 + + + + + + + + + + + + + + +
+
+ + + hr.payslip.run.tree + hr.payslip.run + + + + + + + + + + + + + + + + hr.payslip.line.form.inherited + hr.payslip.line + + + + + + + + + + hr.payslip.line.tree.inherited + hr.payslip.line + + + + 1 + + + 0 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + hr.payslip.tree + hr.payslip + + + + + + + + + + + + + + + + + + + + + + + + + + + hr.payslip.kanban + hr.payslip + + + + 0 + 0 + 0 + + +
+ + + +
+
+ +
+ + + +
+
+ + 1 + +
+
+ + + hr.payslip.select + hr.payslip + + + + + + + +
+
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/views/salary_advance.xml b/odex30_base/exp_payroll_custom/views/salary_advance.xml new file mode 100644 index 0000000..9b72617 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/salary_advance.xml @@ -0,0 +1,165 @@ + + + + + + salary.advance.form + salary.advance + +
+
+
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + +
+
+
+
+ + + salary.advance.tree + salary.advance + + + + + + + + + + + + salary.advance.select + salary.advance + + + + + + + + + + + + + + + + + + + Salary Advance + ir.actions.act_window + salary.advance + + tree,form + + {'search_default_my_requests_filter':1} + [('employee_id.user_id', '=', uid)] + +

+ Create Requests. +

+
+
+ + + Salary Advance + ir.actions.act_window + salary.advance + + tree,form + + {'search_default_approved':1} + [('employee_id.user_id', '=', uid)] + +

+ Create Requests. +

+
+
+ + + Salary Advance + ir.actions.act_window + salary.advance + + tree,form + + {'search_default_submitted': 1} + + +

+ Create Requests. +

+
+
+ + + Salary Advance Request + salary.advance.seq + SAR + 4 + 1 + 1 + + + + + + + + + +
+
diff --git a/odex30_base/exp_payroll_custom/views/salary_scale_level_group.xml b/odex30_base/exp_payroll_custom/views/salary_scale_level_group.xml new file mode 100644 index 0000000..1a5c16d --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/salary_scale_level_group.xml @@ -0,0 +1,15 @@ + + + + + Salary Scale Group + ir.actions.act_window + hr.payroll.structure + tree,form + [('type','=','group')] + {'default_type':'group', 'search_default_group': 1} + + + + + diff --git a/odex30_base/exp_payroll_custom/views/salary_structure.xml b/odex30_base/exp_payroll_custom/views/salary_structure.xml new file mode 100644 index 0000000..2f0bf85 --- /dev/null +++ b/odex30_base/exp_payroll_custom/views/salary_structure.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + diff --git a/odex30_base/exp_payroll_custom/wizard/__init__.py b/odex30_base/exp_payroll_custom/wizard/__init__.py new file mode 100644 index 0000000..0e51dac --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/__init__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# LCT, Life Connection Technology +# Copyright (C) 2019-2020 LCT +# +############################################################################## + +from . import payslip_monthly_report +from . import payroll_bank_report +from . import employee_selection_wizard diff --git a/odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.py b/odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.py new file mode 100644 index 0000000..2fc4fa4 --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.py @@ -0,0 +1,82 @@ +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class EmployeeSelectionWizard(models.TransientModel): + _name = 'employee.selection.wizard' + _description = 'Employee Selection Wizard' + + + employee_ids = fields.Many2many( + 'hr.employee', + string='Employees', + required=True, + ) + + employee_reward_id = fields.Many2one(comodel_name='hr.employee.reward',string='Employee_reward_id') + + @api.onchange('employee_ids') + def _onchange_employee_ids(self): + return { + 'domain': { + 'employee_ids': [ + ('id', 'not in', self.employee_ids.ids), + ('state','=','open'), + ('active', '=', True) + ] + } + } + + def _get_active_employee_reward(self): + reward_id = self.env.context.get('default_employee_reward_id') + if not reward_id and self.env.context.get('active_model') == 'hr.employee.reward': + reward_id = self.env.context.get('active_id') + return reward_id + + def action_confirm(self): + """ + Action to add employees to current employee reward record + """ + self.ensure_one() + + # Get the current reward record or create a new one + reward = self.env['hr.employee.reward'].browse(self._get_active_employee_reward()) + + if not reward.exists(): + # Get values from context + reward_vals = self.env.context.get('default_reward_vals', {}) + reward = self.env['hr.employee.reward'].create(reward_vals) + + print('percentage >>>>>>', self.env.context.get('default_reward_vals', {})) + + # Prepare values for reward lines + vals_list = [ + { + 'employee_id': employee.id, + 'employee_reward_id': reward.id, + } + for employee in self.employee_ids + ] + existing_employees = reward.line_ids_reward.mapped('employee_id').ids + duplicate_employees = set(self.employee_ids.ids) & set(existing_employees) + + if duplicate_employees: + duplicate_names = self.env['hr.employee'].browse(list(duplicate_employees)).mapped('name') + raise ValidationError(_( + "The following employees are already in reward lines: %s" % ', '.join(duplicate_names) + )) + + # Create all records in a single operation + reward.write({ + 'line_ids_reward': [(0, 0, vals) for vals in vals_list] + }) + for line in reward.line_ids_reward: + fields = ['percentage', 'account_id', 'journal_id'] + default_values = line.sudo().default_get(fields) + + # Apply the default values to the line + line.write(default_values) + line.sudo().get_percentage_appraisal() + line.sudo()._compute_calculate_amount() + + return {'type': 'ir.actions.act_window_close'} \ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.xml b/odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.xml new file mode 100644 index 0000000..8306f06 --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/employee_selection_wizard.xml @@ -0,0 +1,36 @@ + + + + employee.selection.wizard.form + employee.selection.wizard + +
+ + + + + + +
+
+
+
+
+ + + + Select Employees + ir.actions.act_window + employee.selection.wizard + form + new + +
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/wizard/payroll_bank_report.py b/odex30_base/exp_payroll_custom/wizard/payroll_bank_report.py new file mode 100644 index 0000000..6e12d01 --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/payroll_bank_report.py @@ -0,0 +1,195 @@ +# -*- coding:utf-8 -*- + +from datetime import date, datetime +from dateutil.relativedelta import relativedelta +from odoo import models, fields, api, _ +from zoneinfo import ZoneInfo +import datetime +import random + +class BankPayslipReport(models.TransientModel): + _name = 'payroll.bank.wiz' + _description = "Bank Payslips Report" + + date_from = fields.Date(string='Date From',required=True, + default=lambda self: date(date.today().year, date.today().month, 1)) + date_to = fields.Date(string='Date To', required=True, + default=lambda self: date(date.today().year, date.today().month, 1)+relativedelta(months=1,days=-1)) + pay_date = fields.Date( + string='Pay Date', + required=False) + salary_type= fields.Char( + string='', + required=False) + + bank_ids = fields.Many2many('res.bank', string='Banks',required=True) + salary_ids = fields.Many2many('hr.payroll.structure', 'hrpayroll_rel', 'salary_id', 'colum2_id',string='Salary Structures') + level_ids = fields.Many2many('hr.payroll.structure','hrpayroll_rel_str', 'col1', 'col2', string='Salary Levels') + group_ids = fields.Many2many('hr.payroll.structure','hrpayroll_rel3', 'col11', 'colid2', string='Salary Degrees') + degree_ids = fields.Many2many('hr.payroll.structure','hrpayroll_rel4', 'colid1', 'col22' ,string='Salary Basice') + company_id = fields.Many2one('res.company', string="Company", default=lambda self:self.env.company.id) + employee_ids = fields.Many2many('hr.employee', string='Employees') + no_details = fields.Boolean('No Details' ,default=False) + report_type = fields.Selection( + [('salary', 'Salary'), + ('overtime', 'Overtime'), + ('mission', 'Mission'), + ('training', 'Training'), + ('allowance', 'Allowance'), + ],default='salary', string='Report Type') + entry_type = fields.Selection( + [('all', 'ALL'), + ('posted', 'Post'), + ('unposted', 'Un Post'), + ], default='all', string='Entry Type') + bank_type = fields.Selection( + [('rajhi', 'Al-Rajhi Bank'), + ('alahli', 'Al-Ahli Bank'), + ('riyadh', 'Al-Riyadh Bank'), + ], default='rajhi', string='Select Bank') + + @api.onchange('date_from') + def onchange_date_from(self): + if self.date_from : + self.date_to = fields.Date.from_string(self.date_from)+relativedelta(months=+1, day=1, days=-1) + + + + def print_pdf_report(self): + self.ensure_one() + [data] = self.read() + date_from = self.date_from + date_to = self.date_to + employees = self.env['hr.employee'].search([('id', 'in', self.employee_ids.ids)]) + banks = self.env['res.bank'].search([('id', 'in', self.bank_ids.ids)]) + salary = self.env['hr.payroll.structure'].search([('id', 'in', self.salary_ids.ids)]) + no_details =self.no_details + report_type = self.report_type + entry_type = self.entry_type + bank_type = self.bank_type + company_id = self.env['res.company'].search([('id', '=', self.company_id.id)]) + + + + + + datas = { + 'employees': employees.ids, + 'banks': banks.ids, + 'salary': salary.ids, + 'form': data, + 'date_from': date_from, + 'date_to': date_to, + 'no_details': no_details, + 'report_type': report_type, + 'entry_type': entry_type, + 'bank_type': bank_type, + 'company_id': company_id, + } + + return self.env.ref('exp_payroll_custom.bank_payslip_report').report_action(self, data=datas) + + + + def print_report(self): + [data] = self.read() + date_from = self.date_from + date_to = self.date_to + no_details=self.no_details + report_type=self.report_type + entry_type=self.entry_type + bank_type=self.bank_type + employees = self.env['hr.employee'].search([('id', 'in', self.employee_ids.ids)]) + banks = self.env['res.bank'].search([('id', 'in', self.bank_ids.ids)]) + salary = self.env['hr.payroll.structure'].search([('id', 'in', self.salary_ids.ids)]) + company_id = self.env['res.company'].search([('id', '=', self.company_id.id)]) + + + datas = { + 'employees': employees.ids, + 'banks': banks.ids, + 'salary': salary.ids, + 'form': data, + 'date_from': date_from, + 'date_to': date_to, + 'no_details': no_details, + 'report_type': report_type, + 'entry_type': entry_type, + 'bank_type': bank_type, + 'company_id': company_id.id, + } + + return self.env.ref('exp_payroll_custom.report_payroll_bank_xlsx').report_action(self,data=datas) + + def print_report_text(self): + self.ensure_one() + [data] = self.read() + date_from = self.date_from.strftime("%B") + date_from = self.date_from + date_to = self.date_to + pay_slip = self.date_from.strftime("%B %Y") + employees = self.env['hr.employee'].search([('id', 'in', self.employee_ids.ids)]) + banks = self.env['res.bank'].search([('id', 'in', self.bank_ids.ids)]) + salary = self.env['hr.payroll.structure'].search([('id', 'in', self.salary_ids.ids)]) + no_details = self.no_details + report_type = self.report_type + entry_type = self.entry_type + bank_type = self.bank_type + company_id = self.env['res.company'].search([('id', '=', self.company_id.id)]) + company_hr_no = self.env['res.company'].search([('id', '=', self.company_id.id)]).company_hr_no + phone = self.env['res.company'].search([('id', '=', self.company_id.id)]).phone + company_pay_no = self.env['res.company'].search([('id', '=', self.company_id.id)]).company_pay_no + company_registry = self.env['res.company'].search([('id', '=', self.company_id.id)]).company_registry + datestamp = datetime.datetime.now().strftime("%Y/%m/%d") + timestamp = datetime.datetime.now().strftime("%H:%M:%S") + currency = self.env['res.company'].search([('id', '=', self.company_id.id)]).currency_id.name + if self.pay_date: + pay_date = self.pay_date + else: + pay_date = self.date_to + if report_type == 'salary': + self.salary_type = 'S' + elif report_type=='overtime': + self.salary_type = 'O' + else: + self.salary_type='B' + salary_type = self.salary_type + ## Ranom vlaues in report + length_of_string = 5 + length_of_string2 = 4 + sample = "ABCDEFGHIJKLMNOPQURSTYWXZ0123456789" + generated_string1 = ''.join(random.choice(sample) for _ in range(length_of_string)) + generated_string2 = ''.join(random.choice(sample) for _ in range(length_of_string2)) + random_char = str(generated_string1) + random_char2 = str(generated_string2) + + datas = { + 'employees': employees.ids, + 'banks': banks.ids, + 'salary': salary.ids, + 'form': data, + 'date_from':date_from, + 'date_to': date_to, + 'no_details': no_details, + 'report_type': report_type, + 'entry_type': entry_type, + 'bank_type': bank_type, + 'company_id': company_id.english_name, + 'timestamp': timestamp, + 'datestamp': datestamp, + 'currency': currency, + 'pay_date': pay_date, + 'salary_type': salary_type, + 'company_hr_no': company_hr_no, + 'phone': phone, + 'company_pay_no': company_pay_no, + 'company_registry': company_registry, + 'pay_slip': pay_slip, + 'random_char': random_char, + 'random_char2': random_char2, + + } + return self.env.ref('exp_payroll_custom.payroll_bank_wiz_report_docx').report_action(self, data=datas) + + + diff --git a/odex30_base/exp_payroll_custom/wizard/payroll_bank_report_view.xml b/odex30_base/exp_payroll_custom/wizard/payroll_bank_report_view.xml new file mode 100644 index 0000000..b7e1e1c --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/payroll_bank_report_view.xml @@ -0,0 +1,86 @@ + + + + + + + payroll.bank.wiz.form + payroll.bank.wiz + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + + Bank Payslip Report + ir.actions.act_window + payroll.bank.wiz + form + + new + + + + +
+
\ No newline at end of file diff --git a/odex30_base/exp_payroll_custom/wizard/payslip_monthly_report.py b/odex30_base/exp_payroll_custom/wizard/payslip_monthly_report.py new file mode 100644 index 0000000..447d5db --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/payslip_monthly_report.py @@ -0,0 +1,118 @@ +## -*- coding: utf-8 -*- +############################################################################## +# +# LCT, Life Connection Technology +# Copyright (C) 2019-2020 LCT +# +############################################################################## +from datetime import date + +from dateutil.relativedelta import relativedelta + +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError, UserError + + +class PayslipMonthlyReport(models.TransientModel): + _name = 'payslip.monthly.report' + _description = "Payslips Monthly Report" + + date_from = fields.Date(string='Date From', required=True, + default=lambda self: date(date.today().year, date.today().month, 1)) + date_to = fields.Date(string='Date To', required=True, + default=lambda self: date(date.today().year, date.today().month, 1) + relativedelta(months=1, + days=-1)) + detailed = fields.Boolean('Detail By Employees', default=False) + listed = fields.Boolean('List By Rules', default=False) + salary_ids = fields.Many2many('hr.payroll.structure', 'wiz_sal_rel', 'w_id', 'sal_id', string='Salary Structures') + level_ids = fields.Many2many('hr.payroll.structure', 'wiz_lvl_rel', 'w_id', 'lvl_id', string='Salary Levels') + group_ids = fields.Many2many('hr.payroll.structure', 'wiz_grp_rel', 'w_id', 'grp_id', string='Salary Groups') + degree_ids = fields.Many2many('hr.payroll.structure', 'wiz_dgr_rel', 'w_id', 'dgr_id', string='Salary Degrees') + rule_ids = fields.Many2many('hr.salary.rule', string='Rules') + allow = fields.Boolean('Allowances') + deduct = fields.Boolean('Deductions') + employee_ids = fields.Many2many('hr.employee', string='Employees') + no_rule = fields.Boolean('No Rules', default=False) + + @api.onchange('date_from') + def onchange_date_from(self): + if self.date_from: + self.date_to = fields.Date.from_string(self.date_from) + relativedelta(months=+1, day=1, days=-1) + + @api.onchange('allow', 'deduct') + def get_rule(self): + domain = (self.allow and self.deduct) and [('category_id.rule_type', 'in', ('allowance', 'deduction')), ] or \ + self.allow and [('category_id.rule_type', '=', 'allowance'), ] or \ + self.deduct and [('category_id.rule_type', '=', 'deduction'), ] or [] + domain += [('appears_on_payslip', '=', True), ('active', '=', True)] + return {'domain': {'rule_ids': [('id', 'in', self.env['hr.salary.rule'].search(domain).ids)]}} + + def get_payslip_line(self): + domain = [('slip_id.date_from', '>=', self.date_from), ('slip_id.date_to', '<=', self.date_to), + ('slip_id.state', '!=', 'cancel'), ('appears_on_payslip', '=', True), ] + if self.rule_ids: + domain += [('salary_rule_id', 'in', self.rule_ids.ids)] + if self.allow and self.deduct: + domain += [('category_id.rule_type', 'in', ('allowance', 'deduction'))] + elif self.deduct: + domain += [('salary_rule_id.category_id.rule_type', '=', 'deduction')] + elif self.allow: + domain += [('salary_rule_id.category_id.rule_type', '=', 'allowance')] + if self.employee_ids: + domain += [('employee_id', 'in', self.employee_ids.ids)] + if self.salary_ids: + domain += [('slip_id.struct_id', 'in', self.salary_ids.ids)] + if self.level_ids: + domain += [('slip_id.level_id', 'in', self.level_ids.ids)] + if self.group_ids: + domain += [('slip_id.group_id', 'in', self.group_ids.ids)] + if self.degree_ids: + domain += [('slip_id.degree_id', 'in', self.degree_ids.ids)] + return self.env['hr.payslip.line'].search(domain) + + def check_data(self): + landscape = False + if self.date_from > self.date_to: + raise UserError(_('Date From must be less than or equal Date To')) + payslip_lines = self.get_payslip_line() + if not payslip_lines: + raise ValidationError(_('Sorry No Data To Be Printed')) + rule_ids = self.no_rule and [0, ] or self.rule_ids and self.rule_ids.ids or \ + self.env['hr.salary.rule'].search([('appears_on_payslip', '=', True), ('active', '=', True)]).ids + rule_ids = list(set(rule_ids) and set([r.id for r in payslip_lines.mapped('salary_rule_id')])) + datas = { + 'ids': rule_ids, + 'model': 'hr.salary.rule', + 'payslip_line_ids': [pl.id for pl in payslip_lines], + 'form': (self.read()[0]), + 'rule_ids': rule_ids, + } + ctx = self.env.context.copy() + ctx.update({'active_model': 'hr.salary.rule', 'active_ids': rule_ids, }) + if self.detailed and self.listed: + delist = 'tt' + emp_ids = self.employee_ids and self.employee_ids.ids or \ + list(set(r['employee_id'][0] for r in self.env['hr.payslip'].search_read([ + ('date_from', '>=', self.date_from), ('date_to', '<=', self.date_to), + ('state', '!=', 'cancel')], ['employee_id', ]))) + emp_ids = list(set(emp_ids) and set([emp.id for emp in payslip_lines.mapped('employee_id')])) + datas['ids'] = emp_ids + datas['model'] = 'hr.employee' + ctx.update({'active_model': 'hr.employee', 'active_ids': emp_ids, }) + landscape = True + elif self.detailed and not self.listed: + delist = 'tf' + else: + delist = 'ff' + datas['delist'] = delist + return datas, ctx, landscape + + def print_report(self): + datas, ctx, lndkp = self.check_data() + return self.env.ref('exp_payroll_custom.act_payslip_monthly_report').with_context( + ctx, landscape=lndkp).report_action(self, data=datas) + + def print_report_xlsx(self): + datas, ctx, lndkp = self.check_data() + return self.env.ref('exp_payroll_custom.payslip_monthly_report_xlsx').with_context( + ctx).report_action(self, data=datas) diff --git a/odex30_base/exp_payroll_custom/wizard/payslip_monthly_report_view.xml b/odex30_base/exp_payroll_custom/wizard/payslip_monthly_report_view.xml new file mode 100644 index 0000000..841302c --- /dev/null +++ b/odex30_base/exp_payroll_custom/wizard/payslip_monthly_report_view.xml @@ -0,0 +1,84 @@ + + + + + + payslip.monthly.report.form + payslip.monthly.report + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + Payslips Report + ir.actions.act_window + payslip.monthly.report + form + new + + + + + + +
+
\ No newline at end of file