feature/odoo18 #2

Merged
administrator merged 43 commits from feature/odoo18 into develop 2025-05-22 16:16:43 +05:30
10 changed files with 387 additions and 0 deletions
Showing only changes of commit d85cf4822b - Show all commits

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,20 @@
{
'name': 'CWF Timesheet Update',
'version': '1.0',
'category': 'Human Resources',
'summary': 'Manage and update weekly timesheets for CWF department',
'author': 'Your Name or Company',
'depends': ['hr_attendance_extended','web', 'mail', 'base','hr_emp_dashboard'],
'data': [
# 'views/timesheet_form.xml',
'security/ir.model.access.csv',
'views/timesheet_view.xml',
'data/email_template.xml',
],
'assets': {
'web.assets_backend': [
'cwf_timesheet/static/src/js/timesheet_form.js',
],
},
'application': True,
}

View File

@ -0,0 +1,9 @@
from odoo import http
from odoo.http import request
class TimesheetController(http.Controller):
@http.route('/timesheet/form', auth='user', website=True)
def timesheet_form(self, **kw):
# This will render the template for the timesheet form
return request.render('timesheet_form', {})

View File

@ -0,0 +1,109 @@
<odoo>
<data noupdate="1">
<record id="email_template_timesheet_update" model="mail.template">
<field name="name">Timesheet Update Reminder</field>
<field name="email_from">${(user.email or '')}</field>
<field name="subject">Reminder: Update Your Weekly Timesheet</field>
<field name="body_html" type="html"> <style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f6f9;
}
.email-container {
width: 100%;
background: linear-gradient(45deg, #3498db, #2c3e50);
padding: 40px 0;
display: flex;
justify-content: center;
}
.email-content {
background-color: white;
border-radius: 12px;
width: 80%;
max-width: 700px;
padding: 30px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
color: #3498db;
font-size: 32px;
margin: 0;
}
.greeting {
font-size: 18px;
color: #2c3e50;
margin-bottom: 20px;
}
.message {
font-size: 16px;
color: #333;
line-height: 1.5;
margin-bottom: 20px;
}
.cta-button {
display: inline-block;
padding: 12px 25px;
background-color: #3498db;
color: white;
font-size: 16px;
text-decoration: none;
border-radius: 6px;
transition: background-color 0.3s;
}
.cta-button:hover {
background-color: #2980b9;
}
.footer {
font-size: 14px;
color: #7f8c8d;
margin-top: 30px;
text-align: center;
}
.footer a {
color: #3498db;
text-decoration: none;
}
</style>
<body>
<div class="email-container">
<div class="email-content">
<div class="header">
<h1>Timesheet Reminder</h1>
</div>
<div class="greeting">
<p>Dear <strong t-out="object.employee_id.name"/> ,</p>
</div>
<div class="message">
<p>
I hope this message finds you in good spirits. I would like to remind you to please update your weekly timesheet for the period from <strong t-out="object.week_id.week_start_date"/> to <strong t-out="object.week_id.week_end_date"/>.
Timely updates are crucial for maintaining accurate records and ensuring smooth processing.
</p>
<p>
To make things easier, you can use the link below to update your timesheet:
</p>
<a href="https://ftprotech.in/odoo/action-261" class="cta-button" target="_blank">Update Timesheet</a>
</div>
<div class="footer">
<p>
Thank you for your attention.<br/>
Best regards,<br/>
<strong>Fast Track Project Pvt Ltd.</strong>
</p>
<p>
<a href="https://ftprotech.in/" target="_blank">Visit our site</a> for more information.
</p>
</div>
</div>
</div>
</body>
</field>
<field name="model_id" ref="model_cwf_timesheet_line"/> <!-- Ensure this line references the correct model -->
</record>
</data>
</odoo>

View File

@ -0,0 +1 @@
from . import timesheet

View File

@ -0,0 +1,85 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError, UserError
from datetime import timedelta
from odoo import _
class CwfTimesheet(models.Model):
_name = 'cwf.timesheet'
_description = 'CWF Weekly Timesheet'
name = fields.Char(string='Week Name', required=True)
department_id = fields.Many2one('hr.department', string='Department')
week_start_date = fields.Date(string='Week Start Date', required=True)
week_end_date = fields.Date(string='Week End Date', required=True)
total_hours = fields.Float(string='Total Hours', required=True)
status = fields.Selection([
('draft', 'Draft'),
('submitted', 'Submitted')
], default='draft', string='Status')
lines = fields.One2many('cwf.timesheet.line','week_id')
def send_timesheet_update_email(self):
template = self.env.ref('cwf_timesheet.email_template_timesheet_update')
# Ensure that we have a valid employee email
current_date = fields.Date.from_string(self.week_start_date)
end_date = fields.Date.from_string(self.week_end_date)
if current_date > end_date:
raise UserError('The start date cannot be after the end date.')
# Get all employees in the department
employees = self.env['hr.employee'].search([('department_id', '=', self.department_id.id)])
# Loop through each day of the week and create timesheet lines for each employee
while current_date <= end_date:
for employee in employees:
self.env['cwf.timesheet.line'].create({
'week_id': self.id,
'employee_id': employee.id,
'week_day':current_date,
})
current_date += timedelta(days=1)
self.status = 'submitted'
for employee in employees:
if employee.work_email:
email_values = {
'email_to': employee.work_email,
'body_html': template.body_html, # Email body from template
'subject': 'Timesheet Update Notification',
}
template.send_mail(self.id, email_values=email_values, force_send=True)
class CwfTimesheetLine(models.Model):
_name = 'cwf.timesheet.line'
_description = 'CWF Weekly Timesheet Lines'
employee_id = fields.Many2one('hr.employee', string='Employee')
week_id = fields.Many2one('cwf.timesheet', 'Week')
week_day = fields.Date(string='Date')
check_in_date = fields.Datetime(string='Checkin')
check_out_date = fields.Datetime(string='Checkout ')
is_updated = fields.Boolean('Attendance Updated')
state_type = fields.Selection([('draft','Draft'),('holiday', 'Holiday'),('time_off','Time Off'),('present','Present')], default='draft')
def action_submit(self):
if self.state_type == 'draft' or not self.state_type:
raise ValidationError(_('State type should not Draft or Empty'))
if self.state_type not in ['holiday','time_off'] and not (self.check_in_date or self.check_out_date):
raise ValidationError(_('Please enter Check details'))
self._update_attendance()
def _update_attendance(self):
attendance_obj = self.env['hr.attendance']
for record in self:
attendance_obj.sudo().create({
'employee_id': record.employee_id.id,
'check_in': record.check_in_date,
'check_out': record.check_out_date,
})
record.is_updated = True

View File

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_cwf_timesheet_user,access.cwf.timesheet,model_cwf_timesheet,,1,1,1,1
access_cwf_timesheet_line_user,access.cwf.timesheet.line,model_cwf_timesheet_line,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_cwf_timesheet_user access.cwf.timesheet model_cwf_timesheet 1 1 1 1
3 access_cwf_timesheet_line_user access.cwf.timesheet.line model_cwf_timesheet_line 1 1 1 1

View File

@ -0,0 +1,48 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { NetflixProfileContainer } from "@hr_emp_dashboard/js/profile_component";
// Apply patch to NetflixProfileContainer prototype
patch(NetflixProfileContainer.prototype, {
/**
* @override
*/
setup() {
// Call parent setup method
super.setup(...arguments);
// Log the department of the logged-in employee (check if data is available)
// if (this.state && this.state.login_employee) {
// console.log(this.state.login_employee['department_id']);
// } else {
// console.error('Employee or department data is unavailable.');
// }
},
/**
* Override the hr_timesheets method
*/
hr_timesheets() {
// Check the department of the logged-in employee
if (this.state.login_employee.department_id == 'CWF') {
// If the department is 'CWF', perform the action to open the timesheets
this.action.doAction({
name: "Timesheets",
type: 'ir.actions.act_window',
res_model: 'cwf.timesheet.line', // Ensure this model exists
view_mode: 'list,form',
views: [[false, 'list'], [false, 'form']],
context: {
'search_default_week_id': true,
},
domain: [['employee_id.user_id','=', this.props.action.context.user_id]],
target: 'current',
});
} else {
// If not 'CWF', call the base functionality
return super.hr_timesheets();
}
},
});

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="timesheet_form">
<div class="container">
<h2>Weekly Timesheet</h2>
<form class="timesheet-form">
<div class="form-group">
<label for="employee">Employee</label>
<input t-att-value="state.employee" type="text" id="employee" class="form-control"/>
</div>
<div class="form-group">
<label for="weekStartDate">Week Start Date</label>
<input type="datetime-local" t-model="state.weekStartDate" id="weekStartDate" class="form-control"/>
</div>
<div class="form-group">
<label for="weekEndDate">Week End Date</label>
<input type="datetime-local" t-model="state.weekEndDate" id="weekEndDate" class="form-control"/>
</div>
<div class="form-group">
<label for="totalHours">Total Hours Worked</label>
<input type="number" t-model="state.totalHours" id="totalHours" class="form-control" min="0"/>
</div>
<button type="button" t-on-click="submitForm" class="btn btn-primary">Submit Timesheet</button>
</form>
</div>
</t>
</templates>

View File

@ -0,0 +1,83 @@
<odoo>
<record id="action_cwf_timesheet" model="ir.actions.act_window">
<field name="name">CWF Timesheet</field>
<field name="res_model">cwf.timesheet</field>
<field name="view_mode">list,form</field>
</record>
<record id="view_timesheet_form" model="ir.ui.view">
<field name="name">cwf.timesheet.form</field>
<field name="model">cwf.timesheet</field>
<field name="arch" type="xml">
<form string="Timesheet">
<header>
<button name="send_timesheet_update_email" string="Send Email" type="object" invisible="status != 'draft'"/>
</header>
<sheet>
<div class="oe_title">
<label for="name"/>
<h1><field name="name" class="oe_inline" readonly="status != 'draft'"/></h1>
</div>
<group>
<!-- Section for Employee and Date Range -->
<group>
<field name="department_id" readonly="status != 'draft'"/>
<field name="week_start_date" readonly="status != 'draft'"/>
<field name="week_end_date" readonly="status != 'draft'"/>
</group>
<!-- Section for Hours and Status -->
<group>
<field name="total_hours" readonly="status != 'draft'"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<menuitem id="menu_timesheet_form" name="Timesheet Form" parent="hr_attendance_extended.menu_attendance_attendance" action="action_cwf_timesheet"/>
<record id="action_cwf_timesheet_line" model="ir.actions.act_window">
<field name="name">CWF Timesheet Lines</field>
<field name="res_model">cwf.timesheet.line</field>
<field name="view_mode">list</field>
<field name="context">{'search_default_group_by_employee_id':1}</field>
</record>
<record id="view_cwf_timesheet_line_list" model="ir.ui.view">
<field name="name">cwf.timesheet.line.list</field>
<field name="model">cwf.timesheet.line</field>
<field name="arch" type="xml">
<list editable="bottom" create="0" delete="0" decoration-success="is_updated == True">
<field name="employee_id" readonly="1" force_save="1"/>
<field name="week_day" readonly="1" force_save="1"/>
<field name="check_in_date" readonly="is_updated == True"/>
<field name="check_out_date" readonly="is_updated == True"/>
<field name="state_type" readonly="is_updated == True"/>
<button name="action_submit" type="object" string="Submit" class="btn btn-outline-primary" invisible="is_updated == True"/>
</list>
</field>
</record>
<menuitem id="menu_timesheet_form_line" name="Week Timesheet " parent="hr_attendance_extended.menu_attendance_attendance" action="action_cwf_timesheet_line"/>
<record id="view_cwf_timesheet_line_search" model="ir.ui.view">
<field name="name">cwf.timesheet.line.search</field>
<field name="model">cwf.timesheet.line</field>
<field name="arch" type="xml">
<search string="Timesheets">
<field name="employee_id"/>
<field name="week_id"/>
<field name="week_day"/>
<field name="check_in_date"/>
<field name="check_out_date"/>
<field name="state_type"/>
<group expand="0" string="Group By">
<filter string="Employee" name="employee_id" domain="[]" context="{'group_by':'employee_id'}"/>
<filter string="Week" name="week_id" domain="[]" context="{'group_by':'week_id'}"/>
<filter string="Status" name ="state_type" domain="[]" context="{'group_by': 'state_type'}"/>
</group>
</search>
</field>
</record>
</odoo>