feature/odoo18 #2
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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', {})
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import timesheet
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue