Merge branch 'dev_odex25_hr' of https://github.com/expsa/odex25-standard-modules into bakry_hr3

This commit is contained in:
Bakry 2025-05-18 17:14:24 +03:00
commit f0332f2ab2
13 changed files with 957 additions and 313 deletions

File diff suppressed because it is too large Load Diff

View File

@ -94,7 +94,7 @@ class HrAttendanceReport(models.Model):
missed_hours = planed_hours - working_hours
if missed_hours < 0:
missed_hours = 0
return {'leaves': leave_hours, 'missed_hours': missed_hours, 'mission_by_days': mission_by_days_hours}
return {'leaves': leave_hours, 'missed_hours': missed_hours, 'mission_by_days': mission_by_days_hours, 'additional_hours': 0.0}
def generate_report(self):
transaction_values = {}
@ -127,13 +127,12 @@ class HrAttendanceReport(models.Model):
no_of_days = len(set(flexible_trans.mapped('date')))
flexible_days = emp_calendar.number_of_flexi_days
if no_of_days >= flexible_days:
ord_trans_dates = sorted(set(flexible_trans.mapped('date')),
key=lambda x: datetime.strptime(x, DATE_FORMAT))
ord_trans_dates = sorted(set(flexible_trans.mapped('date')))
index = len(ord_trans_dates) > flexible_days and flexible_days - 1 or len(ord_trans_dates) - 1
df = fields.Date.from_string(self.date_from)
while no_of_days >= flexible_days:
dt = ord_trans_dates[index]
current_trans = attendance_transaction.filtered(lambda t: str(df) <= t.date <= str(dt))
current_trans = attendance_transaction.filtered(lambda t: df <= t.date <= dt)
hours_dict = self.calcualte_flexible_transaction(current_trans)
total_mission += hours_dict['mission_by_days']
missed_hours += hours_dict['missed_hours']
@ -144,7 +143,7 @@ class HrAttendanceReport(models.Model):
no_of_days -= flexible_days
else:
if no_of_days and no_of_days < flexible_days:
current_trans = attendance_transaction.filtered(lambda t: str(df) <= t.date <= self.date_to)
current_trans = attendance_transaction.filtered(lambda t: df <= t.date <= self.date_to)
hours_dict = self.calcualte_flexible_transaction(current_trans)
total_mission += hours_dict['mission_by_days']
missed_hours += hours_dict['missed_hours']

View File

@ -323,6 +323,67 @@
</template>
<template id="totals_only_attendance_report_template">
<div class="page" style="font-size:12pt" dir="rtl">
<center>
<h2 style="font-weight:bold">تقرير الحضور والانصراف للموظفين</h2>
</center>
<br/>
<div class="row">
<div class="col-md-6">
<h4 style="font-weight:bold">من تاريخ</h4>
<t t-esc="date_start"/>
</div>
<div class="col-md-6">
<h4 style="font-weight:bold">إلى</h4>
<t t-esc="date_end"/>
</div>
</div>
<table class="table table-condensed" style="width:100%">
<thead style="width:100%;">
<tr style="width:100%;text-align:center;">
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">الرقم الوظيفي</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:10%;text-align:center;">اسم الموظف</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:10%;text-align:center;">رقم الهوية</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:10%;text-align:center;">اﻹدارة</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:10%;text-align:center;">المسمي الوظيفي</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">ايام الحضور</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">الاجازات</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">الاجازات الرسمية</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">الغياب</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">ساعات العمل الفعلية</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">ساعات العمل الاضافية</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">الاستئذان</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">مهام عمل/انتداب/تدريب</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">التأخيرات</th>
<th tyle="border: 1px solid gray; padding: 1px; font-size:0.8em;background-color: #b9d7d4;color: black;width:6%;text-align:center;">الخروج المبكر</th>
</tr>
</thead>
<tbody>
<t t-foreach="summary" t-as="row">
<tr style="text-align:center;font-weight:bold;background-color: #b9d7d4">
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['employee_number']"/>
<td style="border: 1px solid gray;padding: 1px; width:10%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['name']"/>
<td style="border: 1px solid gray;padding: 1px; width:10%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['iqama']"/>
<td style="border: 1px solid gray;padding: 1px; width:10%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['department']"/>
<td style="border: 1px solid gray;padding: 1px; width:10%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['job']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['days_present']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['leave_days']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['holiday_days']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['absent_days']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['office_hours']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['extra_hours']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['permission_hours']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['mission_hours']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['lateness_approved']"/>
<td style="border: 1px solid gray;padding: 1px; width:6%;font-size:0.8em;background-color: white;color: black; text-align:center" t-esc="row['early_exit_approved']"/>
</tr>
</t>
</tbody>
</table>
</div>
</template>
<template id="general_attendances_report_temp">
<t t-call="web.html_container">
<t t-call="web.external_layout">
@ -336,14 +397,19 @@
font-family: ae_AlMohanad;
}
</style>
<t t-if="type == 'late'">
<t t-call="attendances.late_attendance_report_template"/>
<t t-if="totals_only">
<t t-call="attendances.totals_only_attendance_report_template"/>
</t>
<t t-if="type == 'absent'">
<t t-call="attendances.absent_attendance_report_template"/>
</t>
<t t-if="type == 'employee'">
<t t-call="attendances.employee_attendance_report_template"/>
<t t-else="">
<t t-if="type == 'late'">
<t t-call="attendances.late_attendance_report_template"/>
</t>
<t t-if="type == 'absent'">
<t t-call="attendances.absent_attendance_report_template"/>
</t>
<t t-if="type == 'employee'">
<t t-call="attendances.employee_attendance_report_template"/>
</t>
</t>
</t>
</t>

View File

@ -15,9 +15,11 @@
</group>
<group>
<field name="to_date"/>
<!-- <field name="employee_ids" widget="many2many_tags"/>-->
</group>
</group>
<group>
<field name="print_totals_only"/>
</group>
<notebook>
<page string="Employees" >
<field name="employee_ids" nolabel="1" required="1">

View File

@ -1,62 +1,71 @@
# -*- coding: utf-8 -*-
import collections
import datetime
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
import pytz
from pytz import timezone
import collections
import datetime
from odoo import fields, models, _,api
from odoo.exceptions import ValidationError
WEEK_DAYS_AR = {
0: "الاثنين",
1: "الثلاثاء",
2: "الاربعاء",
3: "الخميس",
4: "الجمعة",
5: "السبت",
6: "الاحد",
}
def hhmm(val):
if not val:
return "00:00"
mins = float(val) * 60
return "{0:02.0f}:{1:02.0f}".format(*divmod(mins, 60))
week_dayS_arabic={0:"الاثنين", 1: 'الثلاثاء', 4:'الجمعة' , 2:'الاربعاء',3: 'الخميس', 6:'الاحد',5: 'السبت'}
class AttendancesReport(models.TransientModel):
_name = "employee.attendance.report"
_description = "Employee Attendance Report"
from_date = fields.Date(string='From Date', required=True)
to_date = fields.Date(string='To Date', required=True)
employee_ids = fields.Many2many(comodel_name='hr.employee', string='Employees' ,required=True)
resource_calender_id = fields.Many2one(comodel_name='resource.calendar', string='Employee work record')
type = fields.Selection(selection=[('late', 'Late and Early exit'), ('absent', 'Absent'), ('employee', 'Employee')],
required=True,
default='late', string='Type')
from_date = fields.Date(required=True)
to_date = fields.Date(required=True)
employee_ids = fields.Many2many("hr.employee", string="Employees", required=True)
resource_calender_id = fields.Many2one("resource.calendar", string="Employee work record")
type = fields.Selection([
("late", "Late and Early exit"),
("absent", "Absent"),
("employee", "Employee"),
], default="late", required=True)
print_totals_only = fields.Boolean(string="Totals only (one line per employee)", default=False)
def _payload(self):
return {
"ids": self.ids,
"model": self._name,
"form": {
"resource_calender_id": self.resource_calender_id.id,
"from_date": self.from_date,
"to_date": self.to_date,
"employee_ids": self.employee_ids.ids,
"type": self.type,
"print_totals_only": self.print_totals_only,
},
}
def print_report(self):
if not self.employee_ids:
raise ValidationError(_("Please select Employees Name"))
data = {
'ids': self.ids,
'model': self._name,
'form': {
'resource_calender_id': self.resource_calender_id.id,
'from_date': self.from_date,
'to_date': self.to_date,
'employee_ids': self.employee_ids.ids,
'type': self.type,
},
}
return self.env.ref('attendances.general_attendance_action_report').report_action(self, data=data)
return self.env.ref("attendances.general_attendance_action_report").report_action(self, data=self._payload())
def print_excel_report(self):
data = {
'ids': self.ids,
'model': self._name,
'form': {
'resource_calender_id': self.resource_calender_id.id,
'from_date': self.from_date,
'to_date': self.to_date,
'employee_ids': self.employee_ids.ids,
'type': self.type,
},
}
return self.env.ref('attendances.general_attendance_action_xls').report_action(self, data=data, config=False)
return self.env.ref("attendances.general_attendance_action_xls").report_action(self, data=self._payload(), config=False)
class ReportAttendancePublic(models.AbstractModel):
_name = 'report.attendances.general_attendances_report_temp'
_name = "report.attendances.general_attendances_report_temp"
_description = "General Attendances Report"
def get_value(self, data):
type = data['form']['type']
totals_only = data["form"].get("print_totals_only", False)
employee_ids = data['form']['employee_ids']
resource_calender_id = data['form']['resource_calender_id']
from_date = data['form']['from_date']
@ -68,7 +77,7 @@ class ReportAttendancePublic(models.AbstractModel):
total_dic = {}
mykey = []
resource = self.env['resource.calendar'].browse(resource_calender_id)
if resource and not employee_ids:
if resource and not employee_ids:
if resource.employee_ids:
for emp in resource.employee_ids:
employee_ids.append(emp.id)
@ -83,23 +92,23 @@ class ReportAttendancePublic(models.AbstractModel):
attendance_transaction_ids = self.env['hr.attendance.transaction'].search(domain)
employees = attendance_transaction_ids.mapped('employee_id.name')
employee_ids = attendance_transaction_ids.mapped('employee_id')
emp_data=[]
emp_data = []
for emp in employee_ids:
emp_data.append({'job': emp.sudo().job_id.name, 'department': emp.department_id.name,
'emp_no': emp.emp_no,'emp_namw':emp.name})
'emp_no': emp.emp_no, 'emp_namw': emp.name})
grouped_data = collections.defaultdict(list)
emp_data_dict={}
emp_data_dict = {}
for item in emp_data:
grouped_data[item['emp_namw']].append(item)
for key,value in grouped_data.items():
emp_data_dict[key]=list(value)
for key, value in grouped_data.items():
emp_data_dict[key] = list(value)
if type == 'late':
for resource in attendance_transaction_ids:
note=''
note = ''
if resource.is_absent:
note='غياب'
note = 'غياب'
elif resource.public_holiday:
note="عطلة رسمية"
note = "عطلة رسمية"
elif resource.official_id:
note = resource.official_id.mission_type.name
elif resource.normal_leave:
@ -109,27 +118,27 @@ class ReportAttendancePublic(models.AbstractModel):
data.append({
'date': resource.date,
'day': week_dayS_arabic[resource.date.weekday()],
'day': WEEK_DAYS_AR[resource.date.weekday()],
'sig_in': resource.sign_in,
'sig_out': resource.sign_out,
'lateness': resource.lateness,
'early_exit': resource.early_exit,
'extra_hours': resource.additional_hours,
'office_hours':resource.office_hours,
'note':note,
'department':resource.employee_id.department_id.name,
'employee_number':resource.employee_number,
'calendar_id':resource.calendar_id.name,
'office_hours': resource.office_hours,
'note': note,
'department': resource.employee_id.department_id.name,
'employee_number': resource.employee_number,
'calendar_id': resource.calendar_id.name,
'employee_id': resource.employee_id,
'employee_name': resource.employee_id.name,
})
data=sorted(data, key=lambda d: d['date'])
data = sorted(data, key=lambda d: d['date'])
for emp in employees:
list_cat = attendance_transaction_ids.filtered(lambda r: r.employee_id.name == emp)
total_lateness = sum(list_cat.mapped('lateness'))
total_early_exit = sum(list_cat.mapped('early_exit'))
total_late_early = str(datetime.timedelta(minutes=total_early_exit+total_lateness))
total_late_early = str(datetime.timedelta(minutes=total_early_exit + total_lateness))
total_extra_hours = sum(list_cat.mapped('additional_hours'))
total_extra_hours = str(datetime.timedelta(minutes=total_extra_hours))
list_absent = attendance_transaction_ids.filtered(
@ -141,11 +150,13 @@ class ReportAttendancePublic(models.AbstractModel):
list_not_log_out = attendance_transaction_ids.filtered(
lambda r: r.employee_id.name == emp and r.sign_out == 0.0)
list_leave = attendance_transaction_ids.filtered(
lambda r: r.employee_id.name == emp and (r.normal_leave or r.approve_personal_permission ))
lambda r: r.employee_id.name == emp and (r.normal_leave or r.approve_personal_permission))
total_not_sig_out = len(list_not_log_out)
total_leave = len(list_leave)
total_dic[emp] = {'total_lateness': total_lateness, 'total_early_exit': total_early_exit,
"total_extra_hours":total_extra_hours, "total_late_early":total_late_early,"total_leave":total_leave,'total_absent': total_absent, 'total_not_sig_in': total_not_sig_in,
"total_extra_hours": total_extra_hours, "total_late_early": total_late_early,
"total_leave": total_leave, 'total_absent': total_absent,
'total_not_sig_in': total_not_sig_in,
'total_not_sig_out': total_not_sig_out}
grouped = collections.defaultdict(list)
for item in data:
@ -154,7 +165,7 @@ class ReportAttendancePublic(models.AbstractModel):
final_dic[key] = list(value)
key_list.append(key)
mykey = list(dict.fromkeys(key_list))
return final_dic, mykey,total_dic,emp_data_dict
return final_dic, mykey, total_dic, emp_data_dict
elif type == 'absent':
for resource in attendance_transaction_ids.filtered(lambda r: r.is_absent == True):
@ -171,7 +182,7 @@ class ReportAttendancePublic(models.AbstractModel):
final_dic[key] = list(value)
key_list.append(key)
mykey = list(dict.fromkeys(key_list))
return final_dic, mykey, '',emp_data_dict
return final_dic, mykey, '', emp_data_dict
elif type == 'employee':
for emp in employees:
list_cat = attendance_transaction_ids.filtered(lambda r: r.employee_id.name == emp)
@ -182,20 +193,55 @@ class ReportAttendancePublic(models.AbstractModel):
total_dic[emp] = {'total_lateness': total_lateness, 'total_early_exit': total_early_exit}
key_list.append(emp)
mykey = list(dict.fromkeys(key_list))
print("mk",mykey,total_dic)
return '', mykey, total_dic,emp_data_dict
print("mk", mykey, total_dic)
return '', mykey, total_dic, emp_data_dict
@api.model
def _get_report_values(self, docids, data=None):
final_dic, mykey, total,emp_data = self.get_value(data)
final_dic, mykey, total, emp_data = self.get_value(data)
start_date = data['form']['from_date']
end_date = data['form']['to_date']
type = data['form']['type']
print("dataa",mykey,final_dic)
print("dataa", mykey, final_dic)
local_tz = pytz.timezone(
self.env.user.tz or 'GMT')
print_date=datetime.datetime.now(timezone('UTC'))
print_date=print_date.astimezone(local_tz)
print_date = datetime.datetime.now(timezone('UTC'))
print_date = print_date.astimezone(local_tz)
totals_only = data['form'].get('print_totals_only', False)
summary_rows = []
if totals_only:
domain = [('date', '>=', start_date), ('date', '<=', end_date)]
emp_ids = data['form']['employee_ids']
cal_id = data['form']['resource_calender_id']
if emp_ids:
domain.append(('employee_id', 'in', list(set(emp_ids))))
elif cal_id:
rc = self.env['resource.calendar'].browse(cal_id)
domain.append(('employee_id', 'in', rc.employee_ids.ids))
att = self.env['hr.attendance.transaction'].search(domain)
for emp in att.mapped('employee_id'):
lines = att.filtered(lambda l, e=emp: l.employee_id == e)
iq = getattr(emp, 'iqama_number', False) or getattr(emp, 'saudi_number', '')
summary_rows.append({
'employee_number': emp.emp_no or '',
'name': emp.name,
'iqama':iq.display_name or '',
'department': emp.department_id.name,
'job': emp.sudo().job_id.name,
'days_present': len({l.date for l in lines if not l.is_absent}),
'leave_days': len(lines.filtered(lambda l: l.normal_leave)),
'holiday_days': len(lines.filtered(lambda l: l.public_holiday)),
'absent_days': len(lines.filtered(lambda l: l.is_absent)),
'office_hours': hhmm(sum(lines.mapped('office_hours'))),
'extra_hours': hhmm(sum(lines.mapped('additional_hours'))),
'permission_hours': hhmm(sum(lines.mapped('total_permission_hours'))),
'mission_hours': hhmm(sum(lines.mapped('total_mission_hours'))),
'lateness_approved': hhmm(sum(lines.filtered(lambda l: l.approve_lateness).mapped('lateness'))),
'early_exit_approved': hhmm(sum(lines.filtered(lambda l: l.approve_exit_out).mapped('early_exit'))),
})
return {
'doc_ids': data['ids'],
'doc_model': data['model'],
@ -204,14 +250,15 @@ class ReportAttendancePublic(models.AbstractModel):
'type': type,
'data': final_dic,
'mykey': mykey,
'emp_data':emp_data,
'emp_data': emp_data,
'total': total,
'print_date':print_date.strftime("%H:%m %m/%d/%Y" ),
'print_user':self.env.user.name
'summary': summary_rows,
'totals_only': totals_only,
'print_date': print_date.strftime("%H:%m %m/%d/%Y"),
'print_user': self.env.user.name
}
class AttendancesReportXls(models.AbstractModel):
_name = 'report.attendances.general_attendance_xls'
_inherit = 'report.report_xlsx.abstract'
@ -219,19 +266,85 @@ class AttendancesReportXls(models.AbstractModel):
def generate_xlsx_report(self, workbook, data, datas):
self = self.with_context(lang=self.env.user.lang)
x = self.env['report.attendances.general_attendances_report_temp']
final_dic, mykey, total,emp_data = ReportAttendancePublic.get_value(x, data)
final_dic, mykey, total, emp_data = ReportAttendancePublic.get_value(x, data)
start_date = data['form']['from_date']
end_date = data['form']['to_date']
type = data['form']['type']
totals_only = data['form'].get('print_totals_only', False)
sheet = workbook.add_worksheet(U'Holiday Report')
calendar_id = data['form']['resource_calender_id']
sheet.right_to_left()
sheet.set_column(1, 10, 15)
# sheet.set_column(6, 100, 25)
employee_ids = data['form']['employee_ids']
format2 = 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')
fmt = workbook.add_format({'font_size': 10, 'border': 1, 'align': 'center', 'valign': 'vcenter', 'bold': True})
if totals_only:
sheet.merge_range('F2:P2', _('تقرير الحضور والانصراف للموظفين'), fmt)
sheet.write('G3', _('من تاريخ'), fmt)
sheet.write('J3', _('إلى تاريخ'), fmt)
sheet.write(2, 7, str(start_date)[0:10], fmt)
sheet.write(2, 10, str(end_date)[0:10], fmt)
headers = [
'الرقم الوظيفي', 'اسم الموظف', 'رقم الهوية', 'اﻹدارة', 'المسمي الوظيفي',
'ايام الحضور', 'الاجازات', 'الاجازات الرسمية', 'الغياب', 'ساعات العمل الفعلية',
'ساعات العمل الاضافية', 'الاستئذان', 'مهام عمل/انتداب/تدريب', 'التأخيرات', 'الخروج المبكر',
]
header_row = 5
for col, title in enumerate(headers, start=1):
sheet.write(header_row, col, title, fmt)
domain = [('date', '>=', start_date), ('date', '<=', end_date)]
if employee_ids:
domain.append(('employee_id', 'in', list(set(employee_ids))))
elif calendar_id:
rc = self.env['resource.calendar'].browse(calendar_id)
domain.append(('employee_id', 'in', rc.employee_ids.ids))
att = self.env['hr.attendance.transaction'].search(domain)
row = header_row + 1
for emp in att.mapped('employee_id'):
lines = att.filtered(lambda l, e=emp: l.employee_id == e)
sheet.write(row, 1, emp.emp_no or '', fmt)
sheet.write(row, 2, emp.name, fmt)
iq = getattr(emp, 'iqama_number', False) or getattr(emp, 'saudi_number', '')
sheet.write(row, 3, iq.display_name, fmt)
sheet.write(row, 4, emp.department_id.name, fmt)
sheet.write(row, 5, emp.sudo().job_id.name, fmt)
sheet.write(row, 6, len({l.date for l in lines if not l.is_absent}), fmt)
sheet.write(row, 7, len(lines.filtered(lambda l: l.normal_leave)), fmt)
sheet.write(row, 8, len(lines.filtered(lambda l: l.public_holiday)), fmt)
sheet.write(row, 9, len(lines.filtered(lambda l: l.is_absent)), fmt)
sheet.write(row, 10, hhmm(sum(lines.mapped('office_hours'))), fmt)
sheet.write(row, 11, hhmm(sum(lines.mapped('additional_hours'))), fmt)
sheet.write(row, 12, hhmm(sum(lines.mapped('total_permission_hours'))), fmt)
sheet.write(row, 13, hhmm(sum(lines.mapped('total_mission_hours'))), fmt)
sheet.write(row, 14, hhmm(sum(lines.filtered(lambda l: l.approve_lateness).mapped('lateness'))), fmt)
sheet.write(row, 15, hhmm(sum(lines.filtered(lambda l: l.approve_exit_out).mapped('early_exit'))), fmt)
row += 1
sheet.write(row, 1, _('الاجمالي'), fmt)
sheet.write(row, 2, '', fmt)
sheet.write(row, 3, '', fmt)
sheet.write(row, 4, '', fmt)
sheet.write(row, 5, '', fmt)
sheet.write(row, 6, len({l.date for l in att if not l.is_absent}), fmt)
sheet.write(row, 7, len(att.filtered(lambda l: l.normal_leave)), fmt)
sheet.write(row, 8, len(att.filtered(lambda l: l.public_holiday)), fmt)
sheet.write(row, 9, len(att.filtered(lambda l: l.is_absent)), fmt)
sheet.write(row, 10, hhmm(sum(att.mapped('office_hours'))), fmt)
sheet.write(row, 11, hhmm(sum(att.mapped('additional_hours'))), fmt)
sheet.write(row, 12, hhmm(sum(att.mapped('total_permission_hours'))), fmt)
sheet.write(row, 13, hhmm(sum(att.mapped('total_mission_hours'))), fmt)
sheet.write(row, 14, hhmm(sum(att.filtered(lambda l: l.approve_lateness).mapped('lateness'))), fmt)
sheet.write(row, 15, hhmm(sum(att.filtered(lambda l: l.approve_exit_out).mapped('early_exit'))), fmt)
return
if type == 'late':
sheet.merge_range('D3:I3', _("Attendance Reports"), format2)
sheet.write('E4:E4', _("From date"), format2)
@ -241,16 +354,16 @@ class AttendancesReportXls(models.AbstractModel):
row = 8
for key in mykey:
n = 1
h_col=4
h_col = 4
size = len(final_dic[key])
tot_size = len(total[key])
sheet.write(row - 3, h_col, _('Employee Number'), format2)
sheet.write(row - 3, h_col+3, _('job '), format2)
sheet.write(row - 3, h_col + 3, _('job '), format2)
sheet.write(row - 2, h_col, _('Name'), format2)
sheet.write(row - 2, h_col+3, _('Department'), format2)
sheet.write(row - 2, h_col + 3, _('Department'), format2)
sheet.write(row, n, _('date'), format2)
sheet.write(row, n+1, _('day'), format2)
sheet.write(row, n +2 , _('Sign in'), format2)
sheet.write(row, n + 1, _('day'), format2)
sheet.write(row, n + 2, _('Sign in'), format2)
sheet.write(row, n + 3, _('Sign out'), format2)
sheet.write(row, n + 4, _('lateness'), format2)
sheet.write(row, n + 5, _('Early Exit'), format2)
@ -262,13 +375,12 @@ class AttendancesReportXls(models.AbstractModel):
total_lateness = total_early_exit = total_extra_hours = total_office_hours = 0.0
for line in final_dic[key]:
sheet.merge_range(row - 3, h_col + 1,row - 3,h_col + 2, emp_data[key][0]['emp_no'], format2)
sheet.merge_range(row - 3, h_col + 1, row - 3, h_col + 2, emp_data[key][0]['emp_no'], format2)
sheet.write(row - 3, h_col + 4, emp_data[key][0]['job'], format2)
sheet.merge_range(row - 2, h_col + 1,row - 2,h_col + 2, emp_data[key][0]['emp_namw'], format2)
sheet.merge_range(row - 2, h_col + 1, row - 2, h_col + 2, emp_data[key][0]['emp_namw'], format2)
sheet.write(row - 2, h_col + 4, emp_data[key][0]['department'], format2)
sheet.write(data_row, n, str(line['date']), format2)
sheet.write(data_row, n+1, str(line['day']), format2)
sheet.write(data_row, n + 1, str(line['day']), format2)
sheet.write(data_row, n + 2, '{0:02.0f}:{1:02.0f}'.format(*divmod(float(line['sig_in']) * 60, 60)),
format2)
@ -283,7 +395,7 @@ class AttendancesReportXls(models.AbstractModel):
sheet.write(data_row, n + 7,
'{0:02.0f}:{1:02.0f}'.format(*divmod(float(line['office_hours']) * 60, 60)), format2)
sheet.write(data_row, n + 8,line['note'], format2)
sheet.write(data_row, n + 8, line['note'], format2)
sheet.write(data_row, n + 9, line['calendar_id'], format2)
total_lateness += float(line['lateness'])
total_early_exit += float(line['early_exit'])
@ -301,13 +413,13 @@ class AttendancesReportXls(models.AbstractModel):
sheet.write(data_row + 1, n + 7, '{0:02.0f}:{1:02.0f}'.format(*divmod(total_office_hours * 60, 60)),
format2)
sheet.write(data_row+3, n+4, _('Total lateness'), format2)
sheet.write(data_row + 3, n + 4, _('Total lateness'), format2)
# sheet.set_column(data_row,data_row, 15)
sheet.write(data_row+3, n + 5, str(total[key]['total_late_early'].split('.')[0]), format2)
sheet.write(data_row+3, n + 6, _('Total Absent'), format2)
sheet.write(data_row+3, n + 7, str(total[key]['total_absent']), format2)
sheet.write(data_row + 3, n + 5, str(total[key]['total_late_early'].split('.')[0]), format2)
sheet.write(data_row + 3, n + 6, _('Total Absent'), format2)
sheet.write(data_row + 3, n + 7, str(total[key]['total_absent']), format2)
size -= 2
sheet.write(data_row + 4, n+4, _('Total Extra'), format2)
sheet.write(data_row + 4, n + 4, _('Total Extra'), format2)
sheet.write(data_row + 4, n + 5, str(total[key]['total_extra_hours'].split('.')[0]), format2)
sheet.write(data_row + 4, n + 6, _('Total Leave'), format2)
sheet.write(data_row + 4, n + 7, total[key]['total_leave'], format2)

View File

@ -619,6 +619,7 @@ msgstr "المنصب"
#. module: exp_official_mission
#: model:ir.model.fields,field_description:exp_official_mission.field_hr_official_mission__destination
#: model:ir.ui.menu,name:exp_official_mission.mission_destination_menu
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_especially_hours_form_view
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_official_mission_form_view
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_official_mission_tree_view
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_training_form_view
@ -1217,6 +1218,7 @@ msgstr "مكان المهمة"
#. module: exp_official_mission
#: model:ir.model.fields,field_description:exp_official_mission.field_hr_official_mission__mission_purpose
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_especially_hours_form_view
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_official_mission_form_view
msgid "Mission Purpose"
msgstr "الغرض من المهمة"
@ -1393,7 +1395,7 @@ msgstr ""
#: model:ir.model.fields,field_description:exp_official_mission.field_hr_official_mission_employee__official_mission_id
#: model:ir.ui.menu,name:exp_official_mission.employee_official_mission_menu_item
msgid "Official Mission"
msgstr "الانتدابات ومهام العمل"
msgstr "الانتداب"
#. module: exp_official_mission
#: model:ir.actions.act_window,name:exp_official_mission.hr_official_mission_action_hr_employee
@ -2349,7 +2351,7 @@ msgstr ""
#: model:ir.ui.menu,name:exp_official_mission.employee_especially_hours_menu_item
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_especially_hours_tree_view
msgid "Employee Especially Hours"
msgstr "ساعات الموظف الخاصة"
msgstr "مهام العمل"
#. module: exp_official_mission
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_especially_hours_form_view
@ -2685,6 +2687,10 @@ msgstr "للأسف الموظف %s, لايمكن تجاوز عدد ايام ال
msgid "Sorry The Employee %s Cannot Exceed %s Days, This Maximum Days Per year."
msgstr "للأسف الموظف %s, لايمكن تجاوز عدد %s يوم, وهي اقصى أيام للإنتدابات بالسنة."
#. module: exp_official_mission
#: model:ir.model.fields,field_description:exp_official_mission.field_hr_official_mission_type__approve_by
msgid "Approve By"
msgstr "تصديق بواسطة"
#. module: exp_official_mission
#: model_terms:ir.ui.view,arch_db:exp_official_mission.employee_official_mission_type_form_view
@ -2718,3 +2724,13 @@ msgstr "يجب إدخال اعدادات اسم دفتر اليومية لـ %s.
#, python-format
msgid "Employee %s, The Mission %s Has No Account Setting Base On Employee Type."
msgstr "الموظف %s,لايوجد إعدادات بند الصرف لـ %s حسب نوع الموظف."
#. module: exp_official_mission
#: model:ir.model.fields.selection,name:exp_official_mission.selection__hr_official_mission_type__approve_by__direct_manager
msgid "Direct Manager"
msgstr "المدير المباشر"
#. module: exp_official_mission
#: model:ir.model.fields.selection,name:exp_official_mission.selection__hr_official_mission_type__approve_by__depart_manager
msgid "HR Department"
msgstr "الموارد البشرية"

View File

@ -488,18 +488,20 @@ class HrOfficialMission(models.Model):
self.employee_ids.chick_not_overtime()
self.employee_ids.compute_Training_cost_emp()
self.employee_ids.write({'status': 'approved'})
self.state = "direct_manager"
self.state = 'depart_manager'
for rec in self:
manager = rec.sudo().employee_id.parent_id
hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
if manager:
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
rec.write({'state': 'direct_manager'})
rec.write({'state': 'depart_manager'})
else:
raise exceptions.Warning(
_("Sorry, The Approval For The Direct Manager '%s' Only OR HR Manager!")%(manager.name))
else:
rec.write({'state': 'direct_manager'})
rec.write({'state': 'depart_manager'})
if self.mission_type.approve_by == 'direct_manager':
self.approve()
#Refuse For The Direct Manager Only
def direct_manager_refused(self):
@ -978,6 +980,7 @@ class HrOfficialMissionType(models.Model):
('training', _('Training')),
('others', _('others'))], 'Work Status')
special_hours = fields.Boolean(string='Special Hours', default=False)
approve_by = fields.Selection([('direct_manager', 'Direct Manager'), ('depart_manager', 'HR Department')], default='direct_manager', required=True)
transfer_by_emp_type = fields.Boolean('Transfer By Emp Type')
account_ids = fields.One2many('hr.mission.type.account', 'mission_id')

View File

@ -14,7 +14,7 @@
<menuitem name="Employee Especially Hours" id="employee_especially_hours_menu_item"
parent="employee_requests.employee_request_menu_item"
action="employee_especially_hours_action"
sequence="4"/>
sequence="9"/>
<record model="ir.ui.view" id="employee_especially_hours_form_view">
<field name="name">Employee Especially Hours</field>
@ -31,15 +31,15 @@
states="send" groups="hr_base.group_division_manager"/>
<button name="approve" string="HR Approval" class="oe_highlight" type="object"
states="direct_manager" groups="hr.group_hr_user"/>
states="depart_manager" groups="hr.group_hr_user"/>
<button name="refused" string="Refused" class="oe_highlight" type="object"
states="direct_manager" groups="hr.group_hr_user"/>
states="depart_manager" groups="hr.group_hr_user"/>
<button name="draft_state" string="RE-Draft" class="oe_highlight" type="object"
states="approve,refused" groups="hr.group_hr_user"
confirm="Are you sure to Reset To Draft This Record?"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,send,direct_manager,approve,refused"/>
statusbar_visible="draft,send,depart_manager,approve,refused"/>
</header>
<sheet>
<group>
@ -59,16 +59,6 @@
<field name="date_duration" string="Duration Days" readonly="1" force_save="1"
attrs="{'required':[('duration_type','=','days')]}"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" attrs="{'readonly': [('state','!=', 'draft')]}"/>
<field name="date" string="Date Request" attrs="{'readonly':[('state','!=','draft')]}"
required="1"/>
<field name="employee_id" string="Responsible" required="1"
attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="employee_no" string="Employee Number" readonly="1"/>
<field name="department_id2" string="Department" readonly="1"/>
<!--field name="note" string='Comments' attrs="{'readonly':[('state','!=','draft')]}" /-->
<field name="hour_from" string="Hour From" widget="float_time"
attrs="{'required': [('state', '=', 'direct_manager')],
'readonly':[('state','not in',('draft','direct_manager','send'))] }"/>
@ -79,6 +69,22 @@
widget="float_time"
attrs="{'required':[('duration_type','=','hours')]}"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" attrs="{'readonly': [('state','!=', 'draft')]}"/>
<field name="date" string="Date Request" attrs="{'readonly':[('state','!=','draft')]}"
required="1"/>
<field name="employee_id" string="Responsible" required="1"
attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="employee_no" string="Employee Number" readonly="1"/>
<field name="department_id2" string="Department" readonly="1"/>
<field name="destination" string="Destination" widget="selection"
domain="[('destination_type','in',('mission','all'))]" />
<!--field name="note" string='Comments' attrs="{'readonly':[('state','!=','draft')]}" /-->
</group>
</group>
<group string="Mission Purpose">
<field name="mission_purpose" nolabel="1"
attrs="{'readonly':[('state','!=','draft')], 'required':[('state','=','draft')]}"/>
</group>
<notebook>

View File

@ -70,6 +70,7 @@
<field name="max_amount" string="Maximum Amount"
attrs="{'invisible':[('related_with_financial','=',False)],'required':[('related_with_financial','=',True)]}"/>
<field name="approve_by"/>
<!--separator string="Accounting"
attrs="{'invisible':[('related_with_financial','=',False)]}"/>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<data noupdate="1">
<record id="mail_circulars" model="mail.template">
<field name="name">Administrative circulars and decisions publication e-mail.</field>
<field name="subject">${(object.name)}</field>

View File

@ -28,7 +28,7 @@ class HrEmployee(models.Model):
_order = 'id'
# iqama fields in employee view
identity_number = fields.Char(compute='_compute_identity_number', string='Identity Number',store=True)
identity_number = fields.Char(compute_sudo=True, compute='_compute_identity_number', string='Identity Number',store=True)
iqama_creat_date = fields.Date(related="iqama_number.issue_date", readonly=True)
iqama_expiy_date = fields.Date(related="iqama_number.expiry_date", readonly=True)
iqama_job = fields.Many2one(related="iqama_number.job_id", readonly=True)
@ -78,7 +78,7 @@ class HrEmployee(models.Model):
airport = fields.Char("Nearest Airport")
first_hiring_date = fields.Date(string="First Hiring Date")
# duration_in_months = fields.Float(compute='_get_months_no')
# duration_in_months = fields.Float(compute_sudo=True, compute='_get_months_no')
contact_no = fields.Char("Contact No")
reason = fields.Char(string="Reason")
r_name = fields.Char("Name")
@ -170,10 +170,10 @@ class HrEmployee(models.Model):
("widower", "Widower"), ("divorced", "Divorced")],
string="Marital Status", groups="base.group_user", default="single", tracking=True)
base_salary = fields.Float(compute='compute_base_salary')
salary_in_words = fields.Char(compute='get_salary_amount')
payslip_lines = fields.One2many(comodel_name='hr.payslip.line', compute='compute_base_salary')
check_nationality = fields.Boolean(compute="_check_nationality_type")
base_salary = fields.Float(compute_sudo=True, compute='compute_base_salary')
salary_in_words = fields.Char(compute_sudo=True, compute='get_salary_amount')
payslip_lines = fields.One2many(comodel_name='hr.payslip.line', compute_sudo=True, compute='compute_base_salary')
check_nationality = fields.Boolean(compute_sudo=True, compute="_check_nationality_type")
# National address
address_city = fields.Many2one("address.city")
address_region = fields.Many2one("address.region")
@ -185,14 +185,14 @@ class HrEmployee(models.Model):
drug_type = fields.Selection([('company_property', 'Company Property'), ('property', 'Employee Property'),
('rent', 'Rent')], default="rent")
apartment_number = fields.Char()
service_year = fields.Integer(compute='_compute_service_duration')
service_month = fields.Integer(compute='_compute_service_duration')
service_day = fields.Integer(compute='_compute_service_duration')
experience_year = fields.Integer(compute='_compute_duration_experience')
experience_month = fields.Integer(compute='_compute_duration_experience')
experience_day = fields.Integer(compute='_compute_duration_experience')
service_year = fields.Integer(compute_sudo=True, compute='_compute_service_duration')
service_month = fields.Integer(compute_sudo=True, compute='_compute_service_duration')
service_day = fields.Integer(compute_sudo=True, compute='_compute_service_duration')
experience_year = fields.Integer(compute_sudo=True, compute='_compute_duration_experience')
experience_month = fields.Integer(compute_sudo=True, compute='_compute_duration_experience')
experience_day = fields.Integer(compute_sudo=True, compute='_compute_duration_experience')
relationship = fields.Char(string="Relationship")
employee_age = fields.Integer(string="Age", compute='_compute_employee_age', store=True)
employee_age = fields.Integer(string="Age", compute_sudo=True, compute='_compute_employee_age', store=True)
personal_email = fields.Char('Personal Email')
@ -202,9 +202,9 @@ class HrEmployee(models.Model):
domain="[('company_id', '=', company_id)]",
help='Current contract of the employee')
phone_ext = fields.Char(string="Extension Phone")
first_contract_date = fields.Date(compute='_compute_first_contract_date', groups="base.group_user")
first_contract_date = fields.Date(compute_sudo=True, compute='_compute_first_contract_date', groups="base.group_user")
contract_warning = fields.Boolean(string='Contract Warning', store=True, compute='_compute_contract_warning',
contract_warning = fields.Boolean(string='Contract Warning', store=True, compute_sudo=True, compute='_compute_contract_warning',
groups="base.group_user")
barcode = fields.Char(string="Badge ID", help="ID used for employee identification.", groups="base.group_user",
@ -245,7 +245,7 @@ class HrEmployee(models.Model):
branch_name = fields.Many2one(related='department_id.branch_name', store=True, string="Branch Name")
'''employee_cars_count = fields.Integer(compute="_compute_employee_cars_count", string="Cars",
'''employee_cars_count = fields.Integer(compute_sudo=True, compute="_compute_employee_cars_count", string="Cars",
groups="base.group_user")
def _compute_employee_cars_count(self):

View File

@ -2012,7 +2012,7 @@ class holidaysAttach(models.Model):
records.check_access_rule(mode)
if require_employee:
if not (self.env.user._is_admin() or self.env.user.has_group('base.group_user')):
if not (self.env.user._is_admin() or self.env.user.has_group('base.group_user') or self._context.get('bypass_check_public_holidays', False)):
raise AccessError(_("Sorry, you are not allowed to access this document."))
# @api.model

BIN
odex25_hr/to_attendance_system/.DS_Store vendored Normal file

Binary file not shown.