@ -0,0 +1,48 @@ |
|||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
Cleaning Management |
||||
|
=================== |
||||
|
This module allows users to manage cleaning processes easily. |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
* Choose a user within the settings to provide access to the module. |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
General Public License, Version 3 (AGPL v3). |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
* Developer: (V17) Mohammed Dilshad TK , |
||||
|
(V18) Gayathri V |
||||
|
Contact: odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
* Website : https://cybrosys.com |
||||
|
|
||||
|
Bug Tracker |
||||
|
----------- |
||||
|
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
||||
|
|
||||
|
Maintainer |
||||
|
========== |
||||
|
.. image:: https://cybrosys.com/images/logo.png |
||||
|
:target: https://cybrosys.com |
||||
|
|
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
|
||||
|
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V(odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import controllers |
||||
|
from . import models |
@ -0,0 +1,67 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
{ |
||||
|
'name': 'Cleaning Management', |
||||
|
'version': '18.0.1.0.0', |
||||
|
"category": "Industries", |
||||
|
'summary': """Cleaning Management with Online Booking System""", |
||||
|
'description': """This module facilitates the booking of cleaning processes |
||||
|
and effectively manages the cleaning procedures.""", |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://cybrosys.com/", |
||||
|
'depends': ['base', 'website', 'hr', 'account'], |
||||
|
'data': [ |
||||
|
"security/cleaning_management_groups.xml", |
||||
|
'security/ir.model.access.csv', |
||||
|
'views/cleaning_team_views.xml', |
||||
|
'views/cleaning_team_duty_views.xml', |
||||
|
'views/employee_details_views.xml', |
||||
|
'views/cleaning_shift_views.xml', |
||||
|
'views/cleaning_booking_views.xml', |
||||
|
'views/cleaning_inspection_views.xml', |
||||
|
'views/cleaning_management_website_template.xml', |
||||
|
'views/building_type_views.xml', |
||||
|
'views/cleaning_management_dashboard_views.xml', |
||||
|
'views/cleaning_management_menus.xml', |
||||
|
'data/cleaning_management_data.xml', |
||||
|
'data/building_type_demo.xml', |
||||
|
'data/cleaning_shift_demo.xml', |
||||
|
], |
||||
|
'assets': { |
||||
|
'web.assets_backend': [ |
||||
|
'cleaning_management/static/src/css/cleaning_management_dashboard.css', |
||||
|
'cleaning_management/static/src/xml/cleaning_management_dashboard_template.xml', |
||||
|
'cleaning_management/static/src/js/cleaning_management_dashboard.js', |
||||
|
'https://cdn.jsdelivr.net/npm/chart.js', |
||||
|
], |
||||
|
'web.assets_frontend': [ |
||||
|
'cleaning_management/static/src/js/cleaning_management_website.js', |
||||
|
], |
||||
|
}, |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': True, |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import cleaning_management |
@ -0,0 +1,69 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from datetime import datetime |
||||
|
|
||||
|
from odoo import http |
||||
|
from odoo.http import request |
||||
|
|
||||
|
|
||||
|
class CleaningRequest(http.Controller): |
||||
|
"""Create a controller function that retrieves all data from the backend, |
||||
|
sends it to the frontend, and handles the storage of data sent from |
||||
|
the frontend back to the backend.""" |
||||
|
|
||||
|
@http.route(['/cleaning/request/form'], type='http', |
||||
|
auth="user", website=True) |
||||
|
def request_form(self): |
||||
|
"""Retrieving data from the backend.""" |
||||
|
customer_name_rec = http.request.env['res.partner'].sudo().search([]) |
||||
|
building_type_rec = http.request.env['building.type'].sudo().search([]) |
||||
|
cleaning_team_id = http.request.env['cleaning.team'].sudo().search([]) |
||||
|
location_state_id = http.request.env['res.country.state'].sudo().search([]) |
||||
|
return http.request.render( |
||||
|
"cleaning_management.cleaning_online_request", { |
||||
|
'customer_name_rec': customer_name_rec, |
||||
|
'building_type_rec': building_type_rec, |
||||
|
'location_state_id': location_state_id, |
||||
|
'cleaning_team_id': cleaning_team_id, |
||||
|
}) |
||||
|
|
||||
|
@http.route(['/cleaning/request/form/submit'], type='http', |
||||
|
auth="public", website=True) |
||||
|
def online_request_cleaning_form(self, **kw): |
||||
|
"""Storing data into the backend.""" |
||||
|
bookings = request.env['cleaning.booking'].sudo().create({ |
||||
|
'customer_name_id': kw.get('customer_name_id'), |
||||
|
'address': kw.get('address'), |
||||
|
'building_type_id': kw.get('building_type_id'), |
||||
|
'booking_date': kw.get('booking_date'), |
||||
|
'cleaning_date': str( |
||||
|
datetime.fromisoformat(kw.get('cleaning_date'))), |
||||
|
'cleaning_time': (kw.get('cleaning_time')), |
||||
|
'cleaning_team_id': (kw.get('cleaning_team_id')), |
||||
|
'location_state_id': (kw.get('location_state_id')), |
||||
|
'description': kw.get('description') |
||||
|
}) |
||||
|
value = { |
||||
|
'vals': bookings, |
||||
|
} |
||||
|
return http.request.render("cleaning_management.cleaning_online_thanks", |
||||
|
value) |
@ -0,0 +1,18 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!--Provide predefined values for the building type.--> |
||||
|
<record id="building_type_house" model="building.type"> |
||||
|
<field name="name">House</field> |
||||
|
</record> |
||||
|
<record id="building_type_hospital" model="building.type"> |
||||
|
<field name="name">Hospital</field> |
||||
|
</record> |
||||
|
<record id="building_type_institution" model="building.type"> |
||||
|
<field name="name">Institution</field> |
||||
|
</record> |
||||
|
<record id="building_type_office" model="building.type"> |
||||
|
<field name="name">Office</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,13 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- By clicking the menu on the website, |
||||
|
users can access links to different pages. --> |
||||
|
<record id="menu_cleaning_request" model="website.menu"> |
||||
|
<field name="name">Cleaning Online Request</field> |
||||
|
<field name="url">/cleaning/request/form</field> |
||||
|
<field name="parent_id" ref="website.main_menu"/> |
||||
|
<field name="sequence">55</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!--Provide predefined values for the cleaning shift--> |
||||
|
<record id="morning_shift" model="cleaning.shift"> |
||||
|
<field name="shift_type">Morning Shift</field> |
||||
|
<field name="shift_time_from">7.5</field> |
||||
|
<field name="shift_time_to">15.5</field> |
||||
|
</record> |
||||
|
<record id="evening_shift" model="cleaning.shift"> |
||||
|
<field name="shift_type">Evening Shift</field> |
||||
|
<field name="shift_time_from">15.5</field> |
||||
|
<field name="shift_time_to">23.5</field> |
||||
|
</record> |
||||
|
<record id="night_shift" model="cleaning.shift"> |
||||
|
<field name="shift_type">Night Shift</field> |
||||
|
<field name="shift_time_from">23.5</field> |
||||
|
<field name="shift_time_to">07.5</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,6 @@ |
|||||
|
## Module <cleaning_management> |
||||
|
#### 24.12.2024 |
||||
|
#### Version 18.0.1.0.0 |
||||
|
##### ADD |
||||
|
|
||||
|
- Initial commit for Cleaning Management |
@ -0,0 +1,31 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import building_type |
||||
|
from . import cleaning_booking |
||||
|
from . import cleaning_team |
||||
|
from . import cleaning_team_duty |
||||
|
from . import cleaning_inspection |
||||
|
from . import cleaning_shift |
||||
|
from . import cleaning_management_dashboard |
||||
|
from . import cleaning_management_website |
||||
|
from . import employee_details |
||||
|
from . import account_move |
@ -0,0 +1,40 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models, _ |
||||
|
|
||||
|
|
||||
|
class AccountMove(models.Model): |
||||
|
"""Inherit account_move for adding cleaning_id to get invoices""" |
||||
|
_inherit = "account.move" |
||||
|
|
||||
|
cleaning_id = fields.Many2one("cleaning.booking", |
||||
|
string='Cleaning', |
||||
|
help="Choose Cleaning Management") |
||||
|
|
||||
|
def _invoice_paid_hook(self): |
||||
|
"""Function for getting chatter activity for invoice""" |
||||
|
res = super(AccountMove, self)._invoice_paid_hook() |
||||
|
[rec.cleaning_id.message_post( |
||||
|
body=_('Invoice %s paid', rec._get_html_link())) |
||||
|
for rec in self if |
||||
|
rec.cleaning_id and rec.payment_state == 'paid'] |
||||
|
return res |
@ -0,0 +1,32 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class BuildingType(models.Model): |
||||
|
"""Create a new model for specifying the building category |
||||
|
in order to facilitate cleaning services.""" |
||||
|
_name = "building.type" |
||||
|
_description = "Building Type" |
||||
|
|
||||
|
name = fields.Char(string='Building Type', |
||||
|
help="Enter building type", required=True) |
@ -0,0 +1,228 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from datetime import date |
||||
|
from odoo import api, fields, models, _ |
||||
|
from odoo.exceptions import ValidationError |
||||
|
|
||||
|
|
||||
|
class CleaningBooking(models.Model): |
||||
|
"""Create a new model for booking purposes. |
||||
|
The system will incorporate three buttons to indicate the |
||||
|
booking and cleaning status: "Confirm", "Clean" and "Cancel".""" |
||||
|
_name = "cleaning.booking" |
||||
|
_description = "Cleaning Booking" |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_rec_name = 'customer_name_id' |
||||
|
|
||||
|
def _default_cleaning_team_ids(self): |
||||
|
"""Returns default values for cleaning_team_ids records""" |
||||
|
return self.env['cleaning.team'].search([]).ids |
||||
|
|
||||
|
customer_name_id = fields.Many2one('res.partner', |
||||
|
string='Name of Customer', |
||||
|
required=True, |
||||
|
help="Choose customer name") |
||||
|
address = fields.Char(string='Address', |
||||
|
required=True, |
||||
|
help="Enter address of customer") |
||||
|
building_type_id = fields.Many2one('building.type', |
||||
|
string='Building Type', |
||||
|
required=True, |
||||
|
help="Choose building type") |
||||
|
booking_date = fields.Date(default=fields.date.today(), |
||||
|
help="Choose the booking date", |
||||
|
string='Booking Date') |
||||
|
cleaning_team_ids = fields.Many2many('cleaning.team', |
||||
|
string='Cleaning Team', |
||||
|
help="Store cleaning team based on " |
||||
|
"cleaning_time", |
||||
|
default=_default_cleaning_team_ids) |
||||
|
cleaning_team_id = fields.Many2one('cleaning.team', |
||||
|
string='Cleaning Team', |
||||
|
help="Choose cleaning team", |
||||
|
required=True, |
||||
|
domain="[('id', 'in', " |
||||
|
"cleaning_team_ids)]") |
||||
|
cleaning_inspection_id = fields.Many2one('cleaning.inspection', |
||||
|
string="Cleaning Inspection", |
||||
|
help="Choose Cleaning Inspection") |
||||
|
cleaning_team_duty_id = fields.Many2one('cleaning.team.duty', |
||||
|
string="Cleaning Team Duty", |
||||
|
help="Choose Cleaning Team Duty") |
||||
|
cleaning_date = fields.Date(string='Cleaning Date', |
||||
|
required=True, |
||||
|
help="Choose Date for cleaning") |
||||
|
cleaning_time = fields.Selection([('morning', 'Morning'), |
||||
|
('evening', 'Evening'), |
||||
|
('night', 'Night')], |
||||
|
string='Cleaning Time', |
||||
|
help="Choose Time for cleaning", |
||||
|
required=True) |
||||
|
description = fields.Char(string='Description', |
||||
|
help="Enter Description For Booking") |
||||
|
duration = fields.Selection([('forever', 'Forever'), |
||||
|
('fixed', 'Fixed')], |
||||
|
default='forever', string='Duration', |
||||
|
help="Choose Duration For Cleaning") |
||||
|
end_after = fields.Integer(string='End After', |
||||
|
help="Choose End of cleaning management") |
||||
|
end_duration = fields.Selection([('months', 'Months'), |
||||
|
('years', 'Years')], |
||||
|
string="End Duration", |
||||
|
help="Choose End duration of booking") |
||||
|
cleaning_shift_id = fields.Many2one("cleaning.shift", |
||||
|
help="Cleaning Shift", |
||||
|
string="Choose Cleaning Shift") |
||||
|
self_closable = fields.Boolean(string='Is Self Closable', |
||||
|
help="When checked reservations will" |
||||
|
"be automatically closed.") |
||||
|
automatic_closing = fields.Integer(string='Automatic Closing', |
||||
|
help="Automatic Closing Chooser") |
||||
|
location_state_id = fields.Many2one('res.country.state', |
||||
|
string="State", |
||||
|
required=True, |
||||
|
help="Choose State For Cleaning") |
||||
|
place = fields.Char(string="Place", help="Enter Place of Customer") |
||||
|
state = fields.Selection([('draft', 'Draft'), |
||||
|
('booked', 'Booked'), |
||||
|
('cleaned', 'Cleaned'), |
||||
|
('cancelled', 'Cancelled')], |
||||
|
default='draft', string='Status', |
||||
|
help="Stages For Cleaning Processes", |
||||
|
tracking=True) |
||||
|
confirm_stage = fields.Boolean(string="Is Confirm", default=True, |
||||
|
help="When checked,the status" "" |
||||
|
"will be 'Confirm'.") |
||||
|
clean_stage = fields.Boolean(string="Clean", default=True, |
||||
|
help="When checked,the status will be 'Clean'") |
||||
|
cancel_stage = fields.Boolean(string="Cancel", default=True, |
||||
|
help="When checked,the status" |
||||
|
"will be 'Cancel'.") |
||||
|
unit_price = fields.Float(string="Unit Price", default=0.0, required=True, |
||||
|
help="Uit Price for an hour") |
||||
|
total_hour_of_working = fields.Char(string="Total working hours", |
||||
|
help="Total working hours done by Team") |
||||
|
invoice_count = fields.Integer(compute="_compute_invoice_count", |
||||
|
string='Invoice Count') |
||||
|
|
||||
|
@api.onchange('cleaning_time') |
||||
|
def _onchange_cleaning_time(self): |
||||
|
"""The team leader will appear at the scheduled cleaning time.""" |
||||
|
domain = [] |
||||
|
if self.cleaning_time: |
||||
|
res = self.env['cleaning.team.duty'].search( |
||||
|
[('cleaning_date', '=', self.cleaning_date), |
||||
|
('cleaning_time', '=', self.cleaning_time), |
||||
|
('state', 'not in', ['cancelled', 'cleaned'])]) |
||||
|
if res: |
||||
|
domain = [('duty_type', '=', self.cleaning_time), |
||||
|
('id', 'not in', [duty.team_id.id for duty in res])] |
||||
|
else: |
||||
|
domain.append(('duty_type', '=', self.cleaning_time)) |
||||
|
self.write({'cleaning_team_ids': [(6, 0, self.env |
||||
|
['cleaning.team'].search(domain).ids)]}) |
||||
|
|
||||
|
@api.onchange('cleaning_team_id') |
||||
|
def _onchange_cleaning_team_id(self): |
||||
|
"""The team leader's time will appear when changing the leader.""" |
||||
|
self.cleaning_time = self.cleaning_team_id.duty_type |
||||
|
|
||||
|
def action_booking(self): |
||||
|
"""The button action for "Confirm" typically involves |
||||
|
finalizing and saving the booking details entered |
||||
|
by the user.""" |
||||
|
duty_ids_to_add = [] |
||||
|
for rec in self: |
||||
|
cleaning_team_duty = rec.cleaning_team_duty_id.create({ |
||||
|
"team_id": rec.cleaning_team_id.id, |
||||
|
"team_leader_id": rec.cleaning_team_id.team_leader_id.employee_name_id.id, |
||||
|
"members_ids": rec.cleaning_team_id.members_ids.ids, |
||||
|
"location_state_id": rec.location_state_id.id, |
||||
|
"place": rec.place, |
||||
|
"customer_id": rec.customer_name_id.id, |
||||
|
"cleaning_date": rec.cleaning_date, |
||||
|
"cleaning_time": rec.cleaning_time, |
||||
|
"cleaning_id": rec.id |
||||
|
}) |
||||
|
rec.write( |
||||
|
{'state': 'booked', 'confirm_stage': False, |
||||
|
'clean_stage': False, |
||||
|
'cancel_stage': False, |
||||
|
'cleaning_team_duty_id': cleaning_team_duty.id}) |
||||
|
duty_ids_to_add.append((4, cleaning_team_duty.id)) |
||||
|
|
||||
|
def action_cancel(self): |
||||
|
"""The button action for "Cancel" typically involves canceling |
||||
|
and removing a booking that was previously confirmed or reserved.""" |
||||
|
for rec in self: |
||||
|
rec.cleaning_team_duty_id.write({'state': 'cancelled'}) |
||||
|
rec.write( |
||||
|
{'state': 'cancelled', 'confirm_stage': False, |
||||
|
'cancel_stage': True, |
||||
|
'clean_stage': True}) |
||||
|
|
||||
|
def action_create_invoice(self): |
||||
|
"""Function for create an invoice for cleaning processes""" |
||||
|
for rec in self: |
||||
|
if rec.unit_price > 0.0: |
||||
|
invoice = rec.env['account.move'].create({ |
||||
|
'move_type': 'out_invoice', |
||||
|
'partner_id': rec.customer_name_id.id, |
||||
|
'invoice_date': date.today(), |
||||
|
'payment_reference': rec.cleaning_date, |
||||
|
'cleaning_id': rec.id, |
||||
|
'invoice_line_ids': [(0, 0, { |
||||
|
'name': f"{rec.cleaning_team_id.name} ({rec.cleaning_inspection_id.inspection_date_and_time})", |
||||
|
'price_unit': float(rec.unit_price) * float( |
||||
|
rec.total_hour_of_working), |
||||
|
})], |
||||
|
}) |
||||
|
return { |
||||
|
'name': 'account.move.form', |
||||
|
'res_model': 'account.move', |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'view_mode': 'form', |
||||
|
'view_type': 'form', |
||||
|
'view_id': rec.env.ref("account.view_move_form").id, |
||||
|
'res_id': invoice.id, |
||||
|
'target': 'current' |
||||
|
} |
||||
|
else: |
||||
|
raise ValidationError(_("Specify the Unit Price for a hour")) |
||||
|
|
||||
|
def action_view_invoice(self): |
||||
|
"""Function for open Invoice Smart Button""" |
||||
|
self.ensure_one() |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'name': 'Invoice', |
||||
|
'view_mode': 'list,form', |
||||
|
'res_model': 'account.move', |
||||
|
'domain': [('cleaning_id', '=', self.id)], |
||||
|
'context': "{'create': False}" |
||||
|
} |
||||
|
|
||||
|
def _compute_invoice_count(self): |
||||
|
"""Function for count number of Invoices""" |
||||
|
for record in self: |
||||
|
record.invoice_count = self.env['account.move'].search_count( |
||||
|
[('cleaning_id', '=', self.id)]) |
@ -0,0 +1,92 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V(odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class CleaningInspection(models.Model): |
||||
|
"""Create a new model for detailing cleaning inspection specifics. |
||||
|
The system will incorporate two buttons to indicate the |
||||
|
cleaning status: "Clean" and "Dirty".""" |
||||
|
_name = "cleaning.inspection" |
||||
|
_description = "Cleaning Inspection" |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_rec_name = 'cleaning_team_id' |
||||
|
|
||||
|
inspector_name_id = fields.Many2one('res.users', |
||||
|
string='Inspector Name', |
||||
|
required=True, |
||||
|
help="Choose Inspector Name") |
||||
|
cleaning_id = fields.Many2one('cleaning.booking', |
||||
|
help="Cleaning Management", |
||||
|
string="Select Cleaning Management") |
||||
|
inspection_date_and_time = fields.Datetime( |
||||
|
string='Inspection Date and Time', |
||||
|
required=True, |
||||
|
help="Choose Inspection date and time") |
||||
|
cleaning_team_id = fields.Many2one('cleaning.team', |
||||
|
string='Cleaning Team', |
||||
|
required=True, |
||||
|
help="Choose cleaning team") |
||||
|
cleaning_team_duty_id = fields.Many2one('cleaning.team.duty', |
||||
|
string='Cleaning Team Duty', |
||||
|
required=True, |
||||
|
help="Choose cleaning team Duty") |
||||
|
team_leader_id = fields.Many2one('hr.employee', |
||||
|
string='Team Leader', |
||||
|
help="Choose team leader") |
||||
|
date_from = fields.Char(string='Cleaning Start Time', |
||||
|
help="Choose Cleaning Start Time", readonly=True) |
||||
|
date_to = fields.Char(string='Cleaning End Date', |
||||
|
help="Choose Cleaning End Time", readonly=True) |
||||
|
state = fields.Selection([('draft', 'Draft'), |
||||
|
('cleaned', 'Cleaned'), |
||||
|
('dirty', 'Dirty') |
||||
|
], string='Status', |
||||
|
default='draft', |
||||
|
help="Inspection stages for cleaning") |
||||
|
dirty_clean = fields.Boolean('Is Dirty or Clean', |
||||
|
help="When the button is disabled," |
||||
|
" it signifies a Dirty state, " |
||||
|
"while an enabled button signifies" |
||||
|
" a Clean state.") |
||||
|
|
||||
|
def action_clean(self): |
||||
|
"""The button action for "Clean" involves executing a process |
||||
|
to perform cleaning tasks""" |
||||
|
self.write({'state': 'cleaned', 'dirty_clean': True}) |
||||
|
self.cleaning_id.write({'state': 'cleaned', 'clean_stage': True, |
||||
|
'cleaning_inspection_id': self.id}) |
||||
|
self.cleaning_team_duty_id.write( |
||||
|
{'state': 'cleaned'}) |
||||
|
if not self.cleaning_id.cancel_stage: |
||||
|
self.cleaning_id.cancel_stage = True |
||||
|
|
||||
|
def action_dirt(self): |
||||
|
"""The button action for "Dirty" typically |
||||
|
involves marking task as dirty. """ |
||||
|
self.write({'state': 'dirty', 'dirty_clean': True}) |
||||
|
self.cleaning_team_duty_id.write( |
||||
|
{'state': 'dirty'}) |
||||
|
|
||||
|
def action_reclean(self): |
||||
|
"""Function for Reclean processes""" |
||||
|
self.write({'state': 'draft'}) |
@ -0,0 +1,310 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
from odoo.tools import date_utils |
||||
|
|
||||
|
|
||||
|
class CleaningManagementDashBoard(models.Model): |
||||
|
"""Creating new model to extract counts for bookings, cleanings, |
||||
|
and dirty states, intended for visualization on a dashboard.""" |
||||
|
_name = "cleaning.management.dashboard" |
||||
|
_description = "Cleaning Management Dashboard" |
||||
|
|
||||
|
def get_dashboard_count(self): |
||||
|
"""Getting count of bookings,teams,cleaning and dirty count""" |
||||
|
bookings = self.env['cleaning.booking'].search_count([ |
||||
|
('state', '=', 'booked')]) |
||||
|
teams = self.env['cleaning.team'].search_count([]) |
||||
|
cleaning_counts = self.env['cleaning.inspection'].search_count([ |
||||
|
('state', '=', 'cleaned')]) |
||||
|
dirty_counts = self.env['cleaning.inspection'].search_count([ |
||||
|
('state', '=', 'dirty')]) |
||||
|
values = { |
||||
|
'bookings': bookings, |
||||
|
'teams': teams, |
||||
|
'cleaned': cleaning_counts, |
||||
|
'dirty': dirty_counts |
||||
|
} |
||||
|
return values |
||||
|
|
||||
|
def get_the_booking_year(self): |
||||
|
"""Get year wise booking""" |
||||
|
cleaning = self.env['cleaning.booking'] |
||||
|
total_booking_stage_year = cleaning.search([]).filtered( |
||||
|
lambda l: l.booking_date.year == fields.date.today().year).mapped( |
||||
|
'state') |
||||
|
year_stage = [*set(total_booking_stage_year)] |
||||
|
order_of_stages = ['draft', 'booked', 'cleaned', 'cancelled'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(year_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
year_booking_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
total_booking_stage_draft_year = cleaning.search([ |
||||
|
('state', '=', 'draft')]).filtered( |
||||
|
lambda l: l.booking_date.year == fields.date.today().year) |
||||
|
total_booking_stage_booked_year = cleaning.search([ |
||||
|
('state', '=', 'booked')]).filtered( |
||||
|
lambda l: l.booking_date.year == fields.date.today().year) |
||||
|
total_booking_stage_cleaned_year = cleaning.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda l: l.booking_date.year == fields.date.today().year) |
||||
|
total_booking_stage_canceled_year = cleaning.search([ |
||||
|
('state', '=', 'cancelled')]).filtered( |
||||
|
lambda l: l.booking_date.year == fields.date.today().year) |
||||
|
return { |
||||
|
'total_booking_stage_year': year_booking_stages, |
||||
|
'total_booking_stage_draft_year': len( |
||||
|
total_booking_stage_draft_year), |
||||
|
'total_booking_stage_booked_year': len( |
||||
|
total_booking_stage_booked_year), |
||||
|
'total_booking_stage_cleaned_year': len( |
||||
|
total_booking_stage_cleaned_year), |
||||
|
'total_booking_stage_canceled_year': len( |
||||
|
total_booking_stage_canceled_year) |
||||
|
} |
||||
|
|
||||
|
def get_the_booking_month(self): |
||||
|
"""Get month wise booking""" |
||||
|
cleaning = self.env['cleaning.booking'] |
||||
|
total_booking_stage_month = cleaning.search([]).filtered( |
||||
|
lambda l: l.booking_date.month == fields.date.today().month).mapped( |
||||
|
'state') |
||||
|
month_stage = [*set(total_booking_stage_month)] |
||||
|
order_of_stages = ['draft', 'booked', 'cleaned', 'cancelled'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(month_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
monthly_booking_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
total_booking_stage_draft_month = cleaning.search( |
||||
|
[('state', '=', 'draft')]).filtered( |
||||
|
lambda l: l.booking_date.month == fields.date.today().month) |
||||
|
total_booking_stage_booked_month = cleaning.search([ |
||||
|
('state', '=', 'booked')]).filtered( |
||||
|
lambda l: l.booking_date.month == fields.date.today().month) |
||||
|
total_booking_stage_cleaned_month = cleaning.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda l: l.booking_date.month == fields.date.today().month) |
||||
|
total_booking_stage_canceled_month = cleaning.search([ |
||||
|
('state', '=', 'cancelled')]).filtered( |
||||
|
lambda l: l.booking_date.month == fields.date.today().month) |
||||
|
return { |
||||
|
'total_booking_stage_month': monthly_booking_stages, |
||||
|
'total_booking_stage_draft_month': len( |
||||
|
total_booking_stage_draft_month), |
||||
|
'total_booking_stage_booked_month': len( |
||||
|
total_booking_stage_booked_month), |
||||
|
'total_booking_stage_cleaned_month': len( |
||||
|
total_booking_stage_cleaned_month), |
||||
|
'total_booking_stage_canceled_month': len( |
||||
|
total_booking_stage_canceled_month), |
||||
|
} |
||||
|
|
||||
|
def get_the_booking_week(self): |
||||
|
"""Get week wise booking""" |
||||
|
cleaning = self.env['cleaning.booking'] |
||||
|
total_booking_stage_week = cleaning.search([]).filtered( |
||||
|
lambda l: l.booking_date.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]).mapped('state') |
||||
|
week_stage = [*set(total_booking_stage_week)] |
||||
|
order_of_stages = ['draft', 'booked', 'cleaned', 'cancelled'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(week_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
weekly_booking_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
total_booking_stage_draft_week = cleaning.search([ |
||||
|
('state', '=', 'draft')]).filtered( |
||||
|
lambda l: l.booking_date.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]) |
||||
|
total_booking_stage_booked_week = cleaning.search([ |
||||
|
('state', '=', 'booked')]).filtered( |
||||
|
lambda l: l.booking_date.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]) |
||||
|
total_booking_stage_cleaned_week = cleaning.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda l: l.booking_date.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]) |
||||
|
total_booking_stage_canceled_week = cleaning.search([ |
||||
|
('state', '=', 'cancelled')]).filtered( |
||||
|
lambda l: l.booking_date.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]) |
||||
|
return { |
||||
|
'total_booking_stage_week': weekly_booking_stages, |
||||
|
'total_booking_stage_draft_week': len( |
||||
|
total_booking_stage_draft_week), |
||||
|
'total_booking_stage_booked_week': len( |
||||
|
total_booking_stage_booked_week), |
||||
|
'total_booking_stage_cleaned_week': len( |
||||
|
total_booking_stage_cleaned_week), |
||||
|
'total_booking_stage_canceled_week': len( |
||||
|
total_booking_stage_canceled_week), |
||||
|
} |
||||
|
|
||||
|
def get_the_booking_quarter(self): |
||||
|
"""Get quarter wise booking""" |
||||
|
cleaning = self.env['cleaning.booking'] |
||||
|
start_date, end_date = date_utils.get_quarter(fields.date.today()) |
||||
|
total_booking_stage_quarter = cleaning.search([]).filtered( |
||||
|
lambda l: start_date <= l.booking_date <= end_date).mapped('state') |
||||
|
quarter_stage = [*set(total_booking_stage_quarter)] |
||||
|
order_of_stages = ['draft', 'booked', 'cleaned', 'cancelled'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(quarter_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
quarterly_booking_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
total_booking_stage_draft_quarter = cleaning.search([ |
||||
|
('state', '=', 'draft')]).filtered( |
||||
|
lambda l: start_date <= l.booking_date <= end_date) |
||||
|
total_booking_stage_booked_quarter = cleaning.search([ |
||||
|
('state', '=', 'booked')]).filtered( |
||||
|
lambda l: start_date <= l.booking_date <= end_date) |
||||
|
total_booking_stage_cleaned_quarter = cleaning.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda l: start_date <= l.booking_date <= end_date) |
||||
|
total_booking_stage_canceled_quarter = cleaning.search([ |
||||
|
('state', '=', 'cancelled')]).filtered( |
||||
|
lambda l: start_date <= l.booking_date <= end_date) |
||||
|
return { |
||||
|
'total_booking_stage_quarter': quarterly_booking_stages, |
||||
|
'total_booking_stage_draft_quarter': len( |
||||
|
total_booking_stage_draft_quarter), |
||||
|
'total_booking_stage_booked_quarter': len( |
||||
|
total_booking_stage_booked_quarter), |
||||
|
'total_booking_stage_cleaned_quarter': len( |
||||
|
total_booking_stage_cleaned_quarter), |
||||
|
'total_booking_stage_canceled_quarter': len( |
||||
|
total_booking_stage_canceled_quarter), |
||||
|
} |
||||
|
|
||||
|
def quality_year(self): |
||||
|
"""Get year wise quality of cleaning""" |
||||
|
quality = self.env['cleaning.inspection'] |
||||
|
quality_year = quality.search([]).filtered( |
||||
|
lambda |
||||
|
l: l.inspection_date_and_time.year == fields.date.today().year).mapped( |
||||
|
'state') |
||||
|
year_stage = [*set(quality_year)] |
||||
|
order_of_stages = ['cleaned', 'dirty'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(year_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
yearly_quality_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
cleaned_quality_year = quality.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda |
||||
|
l: l.inspection_date_and_time.year == fields.date.today().year) |
||||
|
dirty_quality_year = quality.search([('state', '=', 'dirty')]).filtered( |
||||
|
lambda |
||||
|
l: l.inspection_date_and_time.year == fields.date.today().year) |
||||
|
return { |
||||
|
'quality_year': yearly_quality_stages, |
||||
|
'cleaned_quality_year': len(cleaned_quality_year), |
||||
|
'dirty_quality_year': len(dirty_quality_year) |
||||
|
} |
||||
|
|
||||
|
def quality_month(self): |
||||
|
"""Get month wise quality of cleaning""" |
||||
|
quality = self.env['cleaning.inspection'].search([]) |
||||
|
quality_month = quality.search([]).filtered( |
||||
|
lambda |
||||
|
l: l.inspection_date_and_time.month == fields.date.today().month).mapped( |
||||
|
'state') |
||||
|
month_stage = [*set(quality_month)] |
||||
|
order_of_stages = ['cleaned', 'dirty'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(month_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
monthly_quality_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
cleaned_quality_month = quality.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda |
||||
|
l: l.inspection_date_and_time.month == fields.date.today().month) |
||||
|
dirty_quality_month = quality.search( |
||||
|
[('state', '=', 'dirty')]).filtered( |
||||
|
lambda |
||||
|
l: l.inspection_date_and_time.month == fields.date.today().month) |
||||
|
return { |
||||
|
'quality_month': monthly_quality_stages, |
||||
|
'cleaned_quality_month': len(cleaned_quality_month), |
||||
|
'dirty_quality_month': len(dirty_quality_month) |
||||
|
} |
||||
|
|
||||
|
def quality_week(self): |
||||
|
"""Get week wise quality of cleaning""" |
||||
|
quality = self.env['cleaning.inspection'] |
||||
|
quality_week = quality.search([]).filtered( |
||||
|
lambda l: l.inspection_date_and_time.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]).mapped('state') |
||||
|
week_stage = [*set(quality_week)] |
||||
|
order_of_stages = ['cleaned', 'dirty'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(week_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
capitalized_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
cleaned_quality_week = quality.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda l: l.inspection_date_and_time.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]) |
||||
|
dirty_quality_week = quality.search([ |
||||
|
('state', '=', 'dirty')]).filtered( |
||||
|
lambda l: l.inspection_date_and_time.isocalendar()[1] == |
||||
|
fields.date.today().isocalendar()[1]) |
||||
|
return { |
||||
|
'quality_week': capitalized_stages, |
||||
|
'cleaned_quality_week': len(cleaned_quality_week), |
||||
|
'dirty_quality_week': len(dirty_quality_week) |
||||
|
} |
||||
|
|
||||
|
def quality_quarter(self): |
||||
|
"""Get quarter wise quality of cleaning""" |
||||
|
start_date, end_date = date_utils.get_quarter(fields.datetime.today()) |
||||
|
quality = self.env['cleaning.inspection'] |
||||
|
quality_quarter = quality.search([]).filtered( |
||||
|
lambda |
||||
|
l: start_date <= l.inspection_date_and_time <= end_date).mapped( |
||||
|
'state') |
||||
|
quarter_stage = [*set(quality_quarter)] |
||||
|
order_of_stages = ['cleaned', 'dirty'] |
||||
|
# Sort the stages based on the predefined order |
||||
|
sorted_stages = sorted(quarter_stage, |
||||
|
key=lambda x: order_of_stages.index(x)) |
||||
|
capitalized_stages = [stage.capitalize() for stage in sorted_stages] |
||||
|
cleaned_quality_quarter = quality.search([ |
||||
|
('state', '=', 'cleaned')]).filtered( |
||||
|
lambda l: start_date <= l.inspection_date_and_time <= end_date) |
||||
|
dirty_quality_quarter = quality.search([ |
||||
|
('state', '=', 'dirty')]).filtered( |
||||
|
lambda l: start_date <= l.inspection_date_and_time <= end_date) |
||||
|
return { |
||||
|
'quality_quarter': capitalized_stages, |
||||
|
'cleaned_quality_quarter': len(cleaned_quality_quarter), |
||||
|
'dirty_quality_quarter': len(dirty_quality_quarter) |
||||
|
} |
||||
|
|
||||
|
def cleaning_count(self): |
||||
|
"""Creating a function to retrieve the counts of bookings, |
||||
|
cleanings, and dirty states.""" |
||||
|
return { |
||||
|
'bookings': [rec for rec in |
||||
|
self.env['cleaning.booking'].search([])], |
||||
|
'inspections': [rec for rec in |
||||
|
self.env['cleaning.inspection'].search([])] |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import models |
||||
|
|
||||
|
|
||||
|
class CleaningManagementWebsite(models.Model): |
||||
|
"""Creating new model to search teams for website """ |
||||
|
_name = "cleaning.management.website" |
||||
|
_description = "Cleaning Management Website" |
||||
|
|
||||
|
def get_team_details(self): |
||||
|
"""Search for teams from the cleaning_team model and |
||||
|
pass all data to the frontend.""" |
||||
|
teams = self.env["cleaning.team"].search([]) |
||||
|
return {'team_list': [ |
||||
|
{'id': team.id, 'name': team.name, 'duty': team.duty_type} |
||||
|
for team in teams], |
||||
|
'duty': [{'id': duty.id, 'team_id': duty.team_id.id, |
||||
|
'team_name': duty.team_id.name, |
||||
|
'duty': duty.cleaning_time, |
||||
|
'date': duty.cleaning_date} |
||||
|
for duty in self.env["cleaning.team.duty"].search([])]} |
@ -0,0 +1,41 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class CleaningShift(models.Model): |
||||
|
"""Creating a new model to acquire cleaning shifts for all employees, |
||||
|
it includes type, start time, and end time details.""" |
||||
|
_name = "cleaning.shift" |
||||
|
_description = "Cleaning Shift" |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_rec_name = 'shift_type' |
||||
|
|
||||
|
shift_type = fields.Char(string='Shift Type', |
||||
|
help="Shift type for an employee", required=True, |
||||
|
readonly=True) |
||||
|
shift_time_from = fields.Float(string='Shift Time From', |
||||
|
help="Enter the start time for shift", |
||||
|
required=True) |
||||
|
shift_time_to = fields.Float(string='Shift Time To', |
||||
|
help="Enter the End time for shift", |
||||
|
required=True) |
@ -0,0 +1,102 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
|
||||
|
class CleaningTeam(models.Model): |
||||
|
"""Creating a new model for specifying cleaning teams and |
||||
|
their associated leaders, members and other details.""" |
||||
|
_name = "cleaning.team" |
||||
|
_description = "Cleaning Team" |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
|
||||
|
def _default_emp_detail_ids(self): |
||||
|
"""Returns default values for emp_detail_ids records""" |
||||
|
return self.env['employee.details'].search([]).ids |
||||
|
|
||||
|
name = fields.Char(string='Team Name', required=True, |
||||
|
help="Choose Team Name") |
||||
|
_sql_constraints = [ |
||||
|
('name_unique', 'unique(name)', 'The team already exists')] |
||||
|
|
||||
|
emp_detail_ids = fields.Many2many('employee.details', |
||||
|
help="Stores records to define domain " |
||||
|
"for field.", |
||||
|
relation="emp_detail_ids_rel", |
||||
|
default=_default_emp_detail_ids) |
||||
|
team_leader_id = fields.Many2one('employee.details', |
||||
|
string='Leader', required=True, |
||||
|
help="Choose Leader For Team", |
||||
|
domain="[('id', 'in', " |
||||
|
"emp_detail_ids)]") |
||||
|
members_ids = fields.Many2many('employee.details', |
||||
|
string='Members', |
||||
|
help="Choose Members For Team", |
||||
|
required=True, |
||||
|
domain="[('id', 'in', " |
||||
|
"emp_detail_ids)]") |
||||
|
duty_type = fields.Selection([('morning', 'Morning'), |
||||
|
('night', 'Night'), |
||||
|
('evening', 'Evening')], string='Duty Type', |
||||
|
required=True, |
||||
|
help="Select Duty Type Of The Team") |
||||
|
cleaning_date = fields.Date(string='Cleaning Date and Time', |
||||
|
help="Choose Cleaning Date For Team") |
||||
|
cleaning_time = fields.Selection([('morning', 'Morning'), |
||||
|
('evening', 'Evening'), |
||||
|
('night', 'Night')], |
||||
|
string='Cleaning Time', |
||||
|
help="Choose Cleaning Time For Team") |
||||
|
cleaning_duty_id = fields.Many2one("cleaning.team.duty", |
||||
|
string="Cleaning Duty", |
||||
|
help="Choose Cleaning Duty") |
||||
|
cleaning_duty_ids = fields.Many2many("cleaning.team.duty", |
||||
|
string="Cleaning Team Duty", |
||||
|
readonly=False, |
||||
|
help="Choose Cleaning Duty") |
||||
|
inspection_id = fields.Many2one('cleaning.inspection', |
||||
|
string="Cleaning Inspection", |
||||
|
help="Choose Cleaning Inspection") |
||||
|
|
||||
|
@api.onchange('team_leader_id', 'duty_type') |
||||
|
def _onchange_team_leader_or_duty_type(self): |
||||
|
"""Function for getting Employees with respect to Duty Type""" |
||||
|
if self.team_leader_id: |
||||
|
self.write({'members_ids': [(6, 0, [self.team_leader_id.id])]}) |
||||
|
if self.duty_type in ["morning", "evening", "night"]: |
||||
|
teams_with_same_shift = self.env['cleaning.team'].search([ |
||||
|
('duty_type', '=', self.duty_type)]) |
||||
|
excluded_leaders = [team.team_leader_id.id for team in |
||||
|
teams_with_same_shift] |
||||
|
excluded_members = [member.id for team in teams_with_same_shift for |
||||
|
member in team.members_ids] |
||||
|
shift = self.env['employee.details'].search([ |
||||
|
('time_shift_id', '=', f"{self.duty_type.capitalize()} Shift"), |
||||
|
('id', 'not in', excluded_leaders)]) |
||||
|
val = [rec.id for rec in shift if |
||||
|
rec.id != self.team_leader_id.id and rec.id not in |
||||
|
excluded_members] |
||||
|
self.write({'emp_detail_ids': [(6, 0, self.env[ |
||||
|
'employee.details'].search([('id', 'in', val)]).ids)]}) |
||||
|
else: |
||||
|
self.write({'emp_detail_ids': [(6, 0, self.env[ |
||||
|
'employee.details'].search([]).ids)]}) |
@ -0,0 +1,152 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
from pytz import timezone |
||||
|
|
||||
|
|
||||
|
class CleaningTeamDuty(models.Model): |
||||
|
"""Creating new model to retrieve comprehensive details regarding |
||||
|
the duties assigned to each team.""" |
||||
|
_name = "cleaning.team.duty" |
||||
|
_description = "Cleaning Team Duty" |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_rec_name = 'team_id' |
||||
|
|
||||
|
team_leader_id = fields.Many2one('hr.employee', |
||||
|
readonly=True, |
||||
|
string="Team Leader", |
||||
|
help="Choose Leader of Corresponding Team") |
||||
|
team_id = fields.Many2one('cleaning.team', string='Team Name', |
||||
|
readonly=True, help="Choose Cleaning team") |
||||
|
inspection_id = fields.Many2one('cleaning.inspection', |
||||
|
string="Cleaning Inspection", |
||||
|
help="Choose Cleaning Inspection") |
||||
|
cleaning_id = fields.Many2one('cleaning.booking', |
||||
|
string="Cleaning Booking", |
||||
|
help="Choose Cleaning Booking") |
||||
|
members_ids = fields.Many2many('hr.employee', string='Members', |
||||
|
readonly=True, |
||||
|
help="Choose Members Of Corresponding Team") |
||||
|
location_state_id = fields.Many2one('res.country.state', |
||||
|
string="State", readonly=True, |
||||
|
help="Location for Team To Work") |
||||
|
place = fields.Char(string="Place", readonly=True, |
||||
|
help="Enter Place For The Work") |
||||
|
customer_id = fields.Many2one('res.partner', string='Customer', |
||||
|
readonly=True, help="Choose Customer Name") |
||||
|
cleaning_time = fields.Selection([('morning', 'Morning'), |
||||
|
('evening', 'Evening'), |
||||
|
('night', 'Night')], |
||||
|
string='Cleaning Time', |
||||
|
readonly=True, |
||||
|
help="Cleaning Time, Booked By Customer") |
||||
|
cleaning_date = fields.Date(string='Cleaning Date', |
||||
|
readonly=True, |
||||
|
help="Cleaning Date That Booked By Customer") |
||||
|
inspection_boolean = fields.Boolean(string="Is Inspection", default=True, |
||||
|
readonly=True, |
||||
|
help="Got 'INSPECTION' button in" |
||||
|
" form view") |
||||
|
start_time = fields.Char(string="Start Time", |
||||
|
help="Real time to complete all cleaning process") |
||||
|
start_cleaning = fields.Boolean(string="Is Started") |
||||
|
end_time = fields.Char(string="End Time", |
||||
|
help="Real time to complete all cleaning process") |
||||
|
end_cleaning = fields.Boolean(string="Is Ended", |
||||
|
help="Real time to end all cleaning process") |
||||
|
state = fields.Selection([('draft', 'Draft'), |
||||
|
('dirty', 'Dirty'), |
||||
|
('cleaned', 'Cleaned'), |
||||
|
('cancelled', 'Cancelled')], |
||||
|
default='draft', string='Status', |
||||
|
help="Stages For Cleaning Team Duty", |
||||
|
tracking=True) |
||||
|
inspection_count = fields.Integer(compute="_compute_inspection_count", |
||||
|
string='Inspection Count') |
||||
|
|
||||
|
def action_start(self): |
||||
|
"""Function for start cleaning processes""" |
||||
|
user_tz = self.env.user.tz or 'UTC' |
||||
|
start_time_utc = fields.Datetime.now() |
||||
|
start_time_user_tz = fields.Datetime.to_string( |
||||
|
fields.Datetime.context_timestamp(self, start_time_utc).astimezone( |
||||
|
timezone(user_tz))) |
||||
|
|
||||
|
self.write({ |
||||
|
'start_time': start_time_user_tz, |
||||
|
'start_cleaning': True |
||||
|
}) |
||||
|
|
||||
|
def action_finish(self): |
||||
|
"""Function for finish cleaning processes""" |
||||
|
if self.start_cleaning: |
||||
|
user_tz = self.env.user.tz or 'UTC' |
||||
|
end_time_utc = fields.Datetime.now() |
||||
|
end_time_user_tz = fields.Datetime.to_string( |
||||
|
fields.Datetime.context_timestamp(self, |
||||
|
end_time_utc).astimezone( |
||||
|
timezone(user_tz))) |
||||
|
self.write({ |
||||
|
'end_time': end_time_user_tz, |
||||
|
'inspection_boolean': False, |
||||
|
'end_cleaning': True |
||||
|
}) |
||||
|
start_time_utc = fields.Datetime.from_string(self.start_time) |
||||
|
end_time_utc = fields.Datetime.from_string(end_time_user_tz) |
||||
|
total_hours = (end_time_utc - start_time_utc).total_seconds() / 3600 |
||||
|
self.cleaning_id.total_hour_of_working = total_hours |
||||
|
|
||||
|
def action_inspection(self): |
||||
|
"""Clicking the "Inspection" button will direct the user |
||||
|
to the inspection page.""" |
||||
|
self.inspection_boolean = True |
||||
|
return { |
||||
|
'name': 'cleaning_team_id', |
||||
|
'res_model': 'cleaning.inspection', |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'view_mode': 'form', |
||||
|
'context': {'default_cleaning_team_id': self.team_id.id, |
||||
|
'default_inspector_name_id': self.env.user.id, |
||||
|
'default_cleaning_id': self.cleaning_id.id, |
||||
|
'default_date_from': self.start_time, |
||||
|
'default_date_to': self.end_time, |
||||
|
'default_cleaning_team_duty_id': self.id |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
def action_view_inspection(self): |
||||
|
"""Function for Open Inspection Smart Button""" |
||||
|
self.ensure_one() |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'name': 'Inspection', |
||||
|
'view_mode': 'list,form', |
||||
|
'res_model': 'cleaning.inspection', |
||||
|
'domain': [('cleaning_team_duty_id', '=', self.id)], |
||||
|
'context': "{'create': False}" |
||||
|
} |
||||
|
|
||||
|
def _compute_inspection_count(self): |
||||
|
"""Function for getting total count of inspections""" |
||||
|
for record in self: |
||||
|
record.inspection_count = self.env['cleaning.inspection'].search_count( |
||||
|
[('cleaning_team_duty_id', '=', self.id)]) |
@ -0,0 +1,40 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Gayathri V (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class EmployeeDetails(models.Model): |
||||
|
"""Creating new model for inputting employee information |
||||
|
such as names and shifts.""" |
||||
|
_name = "employee.details" |
||||
|
_description = "Employee Details" |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_rec_name = 'employee_name_id' |
||||
|
|
||||
|
employee_name_id = fields.Many2one('hr.employee', |
||||
|
string='Employee Name', |
||||
|
help="Choose Employee Name", |
||||
|
required=True) |
||||
|
time_shift_id = fields.Many2one('cleaning.shift', |
||||
|
string='Time Shift', |
||||
|
help="Choose Time Shift for Employee", |
||||
|
required=True) |
@ -0,0 +1,27 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- Set a category selection field for users--> |
||||
|
<record id="cleaning_management_groups" model="ir.module.category"> |
||||
|
<field name="name">Cleaning Management</field> |
||||
|
<field name="description">Cleaning management</field> |
||||
|
<field name="sequence">5</field> |
||||
|
</record> |
||||
|
<!-- Action for user--> |
||||
|
<record id="cleaning_management_group_user" model="res.groups"> |
||||
|
<field name="name">User</field> |
||||
|
<field name="category_id" |
||||
|
ref="cleaning_management.cleaning_management_groups"/> |
||||
|
</record> |
||||
|
<!-- Action for manager--> |
||||
|
<record id="cleaning_management_group_manager" model="res.groups"> |
||||
|
<field name="name">Cleaning Manager</field> |
||||
|
<field name="category_id" |
||||
|
ref="cleaning_management.cleaning_management_groups"/> |
||||
|
<field name="users" |
||||
|
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> |
||||
|
<field name="implied_ids" |
||||
|
eval="[(4, ref('cleaning_management_group_user'))]"/> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
|
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 303 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 3.2 KiB |