Browse Source

May 16: [ADD] Initital Commit 'cleaning_management'

pull/317/head
RisvanaCybro 12 months ago
parent
commit
6f336ca88e
  1. 46
      cleaning_management/README.rst
  2. 23
      cleaning_management/__init__.py
  3. 67
      cleaning_management/__manifest__.py
  4. 22
      cleaning_management/controllers/__init__.py
  5. 69
      cleaning_management/controllers/cleaning_management.py
  6. 18
      cleaning_management/data/building_type_demo.xml
  7. 13
      cleaning_management/data/cleaning_management_data.xml
  8. 21
      cleaning_management/data/cleaning_shift_demo.xml
  9. 6
      cleaning_management/doc/RELEASE_NOTES.md
  10. 31
      cleaning_management/models/__init__.py
  11. 40
      cleaning_management/models/account_move.py
  12. 32
      cleaning_management/models/building_type.py
  13. 228
      cleaning_management/models/cleaning_booking.py
  14. 92
      cleaning_management/models/cleaning_inspection.py
  15. 310
      cleaning_management/models/cleaning_management_dashboard.py
  16. 41
      cleaning_management/models/cleaning_management_website.py
  17. 41
      cleaning_management/models/cleaning_shift.py
  18. 102
      cleaning_management/models/cleaning_team.py
  19. 152
      cleaning_management/models/cleaning_team_duty.py
  20. 40
      cleaning_management/models/employee_details.py
  21. 27
      cleaning_management/security/cleaning_management_groups.xml
  22. 13
      cleaning_management/security/ir.model.access.csv
  23. BIN
      cleaning_management/static/description/assets/icons/check.png
  24. BIN
      cleaning_management/static/description/assets/icons/chevron.png
  25. BIN
      cleaning_management/static/description/assets/icons/cogs.png
  26. BIN
      cleaning_management/static/description/assets/icons/consultation.png
  27. BIN
      cleaning_management/static/description/assets/icons/ecom-black.png
  28. BIN
      cleaning_management/static/description/assets/icons/education-black.png
  29. BIN
      cleaning_management/static/description/assets/icons/hotel-black.png
  30. BIN
      cleaning_management/static/description/assets/icons/license.png
  31. BIN
      cleaning_management/static/description/assets/icons/lifebuoy.png
  32. BIN
      cleaning_management/static/description/assets/icons/manufacturing-black.png
  33. BIN
      cleaning_management/static/description/assets/icons/pos-black.png
  34. BIN
      cleaning_management/static/description/assets/icons/puzzle.png
  35. BIN
      cleaning_management/static/description/assets/icons/restaurant-black.png
  36. BIN
      cleaning_management/static/description/assets/icons/service-black.png
  37. BIN
      cleaning_management/static/description/assets/icons/trading-black.png
  38. BIN
      cleaning_management/static/description/assets/icons/training.png
  39. BIN
      cleaning_management/static/description/assets/icons/update.png
  40. BIN
      cleaning_management/static/description/assets/icons/user.png
  41. BIN
      cleaning_management/static/description/assets/icons/wrench.png
  42. BIN
      cleaning_management/static/description/assets/misc/Cybrosys R.png
  43. BIN
      cleaning_management/static/description/assets/misc/categories.png
  44. BIN
      cleaning_management/static/description/assets/misc/check-box.png
  45. BIN
      cleaning_management/static/description/assets/misc/compass.png
  46. BIN
      cleaning_management/static/description/assets/misc/corporate.png
  47. BIN
      cleaning_management/static/description/assets/misc/customer-support.png
  48. BIN
      cleaning_management/static/description/assets/misc/features.png
  49. BIN
      cleaning_management/static/description/assets/misc/logo.png
  50. BIN
      cleaning_management/static/description/assets/misc/pictures.png
  51. BIN
      cleaning_management/static/description/assets/misc/pie-chart.png
  52. BIN
      cleaning_management/static/description/assets/misc/right-arrow.png
  53. BIN
      cleaning_management/static/description/assets/misc/star.png
  54. BIN
      cleaning_management/static/description/assets/misc/support.png
  55. BIN
      cleaning_management/static/description/assets/misc/whatsapp.png
  56. BIN
      cleaning_management/static/description/assets/modules/1.gif
  57. BIN
      cleaning_management/static/description/assets/modules/2.png
  58. BIN
      cleaning_management/static/description/assets/modules/3.png
  59. BIN
      cleaning_management/static/description/assets/modules/4.jpg
  60. BIN
      cleaning_management/static/description/assets/modules/5.jpg
  61. BIN
      cleaning_management/static/description/assets/modules/6.png
  62. BIN
      cleaning_management/static/description/assets/screenshots/1.png
  63. BIN
      cleaning_management/static/description/assets/screenshots/10.png
  64. BIN
      cleaning_management/static/description/assets/screenshots/11.png
  65. BIN
      cleaning_management/static/description/assets/screenshots/12.png
  66. BIN
      cleaning_management/static/description/assets/screenshots/13.png
  67. BIN
      cleaning_management/static/description/assets/screenshots/14.png
  68. BIN
      cleaning_management/static/description/assets/screenshots/15.png
  69. BIN
      cleaning_management/static/description/assets/screenshots/16.png
  70. BIN
      cleaning_management/static/description/assets/screenshots/2img.png
  71. BIN
      cleaning_management/static/description/assets/screenshots/3.png
  72. BIN
      cleaning_management/static/description/assets/screenshots/4.png
  73. BIN
      cleaning_management/static/description/assets/screenshots/5.png
  74. BIN
      cleaning_management/static/description/assets/screenshots/6.png
  75. BIN
      cleaning_management/static/description/assets/screenshots/8.png
  76. BIN
      cleaning_management/static/description/assets/screenshots/9.png
  77. BIN
      cleaning_management/static/description/assets/screenshots/hero.gif
  78. BIN
      cleaning_management/static/description/assets/screenshots/img7.png
  79. BIN
      cleaning_management/static/description/banner.jpg
  80. BIN
      cleaning_management/static/description/icon.png
  81. 983
      cleaning_management/static/description/index.html
  82. 178
      cleaning_management/static/src/css/cleaning_management_dashboard.css
  83. 460
      cleaning_management/static/src/js/cleaning_management_dashboard.js
  84. 99
      cleaning_management/static/src/js/cleaning_management_website.js
  85. 159
      cleaning_management/static/src/xml/cleaning_management_dashboard_template.xml
  86. 39
      cleaning_management/views/building_type_views.xml
  87. 94
      cleaning_management/views/cleaning_booking_views.xml
  88. 65
      cleaning_management/views/cleaning_inspection_views.xml
  89. 8
      cleaning_management/views/cleaning_management_dashboard_views.xml
  90. 31
      cleaning_management/views/cleaning_management_menus.xml
  91. 213
      cleaning_management/views/cleaning_management_website_template.xml
  92. 48
      cleaning_management/views/cleaning_shift_views.xml
  93. 81
      cleaning_management/views/cleaning_team_duty_views.xml
  94. 52
      cleaning_management/views/cleaning_team_views.xml
  95. 46
      cleaning_management/views/employee_details_views.xml

46
cleaning_management/README.rst

@ -0,0 +1,46 @@
.. 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 , 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>`__

23
cleaning_management/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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

67
cleaning_management/__manifest__.py

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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': '17.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,
}

22
cleaning_management/controllers/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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

69
cleaning_management/controllers/cleaning_management.py

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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)

18
cleaning_management/data/building_type_demo.xml

@ -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>

13
cleaning_management/data/cleaning_management_data.xml

@ -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>

21
cleaning_management/data/cleaning_shift_demo.xml

@ -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>

6
cleaning_management/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <cleaning_management>
#### 07.05.2024
#### Version 17.0.1.0.0
##### ADD
- Initial commit for Cleaning Management

31
cleaning_management/models/__init__.py

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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

40
cleaning_management/models/account_move.py

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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

32
cleaning_management/models/building_type.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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)

228
cleaning_management/models/cleaning_booking.py

@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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': 'tree,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)])

92
cleaning_management/models/cleaning_inspection.py

@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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'})

310
cleaning_management/models/cleaning_management_dashboard.py

@ -0,0 +1,310 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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([])]
}

41
cleaning_management/models/cleaning_management_website.py

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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([])]}

41
cleaning_management/models/cleaning_shift.py

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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)

102
cleaning_management/models/cleaning_team.py

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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)]})

152
cleaning_management/models/cleaning_team_duty.py

@ -0,0 +1,152 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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': 'tree,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)])

40
cleaning_management/models/employee_details.py

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Dilshad TK (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)

27
cleaning_management/security/cleaning_management_groups.xml

@ -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 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'))]"/>
</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"/>
<field name="implied_ids"
eval="[(4, ref('cleaning_management_group_manager'))]"/>
</record>
</data>
</odoo>

13
cleaning_management/security/ir.model.access.csv

@ -0,0 +1,13 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_cleaning_booking,access.cleaning.booking,model_cleaning_booking,base.group_user,1,1,1,1
access_cleaning_team_manager,access.cleaning.team,model_cleaning_team,cleaning_management.cleaning_management_group_manager,1,1,1,1
access_cleaning_team_user,access.cleaning.team,model_cleaning_team,cleaning_management.cleaning_management_group_user,1,0,0,0
access_cleaning_team_duty,access.cleaning.team.duty,model_cleaning_team_duty,base.group_user,1,1,1,1
access_cleaning_inspection_manager,access.cleaning.inspection,model_cleaning_inspection,cleaning_management.cleaning_management_group_manager,1,1,1,1
access_cleaning_inspection_user,access.cleaning.inspection,model_cleaning_inspection,cleaning_management.cleaning_management_group_user,1,1,1,0
access_building_type_manager,access.building.type,model_building_type,base.group_user,1,1,1,1
access_cleaning_shift_user,access.cleaning.shift,model_cleaning_shift,base.group_user,1,1,1,1
access_employee_details_manager,access.employee.details,model_employee_details,cleaning_management.cleaning_management_group_manager,1,1,1,1
access_employee_details_user,access.employee.details,model_employee_details,cleaning_management.cleaning_management_group_user,1,0,0,0
access_cleaning_management_dashboard,access_cleaning_management_dashboard,cleaning_management.model_cleaning_management_dashboard,base.group_user,1,0,0,0
access_cleaning_management_website,access_cleaning_management_website,cleaning_management.model_cleaning_management_website,base.group_user,1,0,0,0
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
2 access_cleaning_booking access.cleaning.booking model_cleaning_booking base.group_user 1 1 1 1
3 access_cleaning_team_manager access.cleaning.team model_cleaning_team cleaning_management.cleaning_management_group_manager 1 1 1 1
4 access_cleaning_team_user access.cleaning.team model_cleaning_team cleaning_management.cleaning_management_group_user 1 0 0 0
5 access_cleaning_team_duty access.cleaning.team.duty model_cleaning_team_duty base.group_user 1 1 1 1
6 access_cleaning_inspection_manager access.cleaning.inspection model_cleaning_inspection cleaning_management.cleaning_management_group_manager 1 1 1 1
7 access_cleaning_inspection_user access.cleaning.inspection model_cleaning_inspection cleaning_management.cleaning_management_group_user 1 1 1 0
8 access_building_type_manager access.building.type model_building_type base.group_user 1 1 1 1
9 access_cleaning_shift_user access.cleaning.shift model_cleaning_shift base.group_user 1 1 1 1
10 access_employee_details_manager access.employee.details model_employee_details cleaning_management.cleaning_management_group_manager 1 1 1 1
11 access_employee_details_user access.employee.details model_employee_details cleaning_management.cleaning_management_group_user 1 0 0 0
12 access_cleaning_management_dashboard access_cleaning_management_dashboard cleaning_management.model_cleaning_management_dashboard base.group_user 1 0 0 0
13 access_cleaning_management_website access_cleaning_management_website cleaning_management.model_cleaning_management_website base.group_user 1 0 0 0

BIN
cleaning_management/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
cleaning_management/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
cleaning_management/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
cleaning_management/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
cleaning_management/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
cleaning_management/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
cleaning_management/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
cleaning_management/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
cleaning_management/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
cleaning_management/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
cleaning_management/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
cleaning_management/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
cleaning_management/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
cleaning_management/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
cleaning_management/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
cleaning_management/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
cleaning_management/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
cleaning_management/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
cleaning_management/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
cleaning_management/static/description/assets/misc/Cybrosys R.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
cleaning_management/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
cleaning_management/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
cleaning_management/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
cleaning_management/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
cleaning_management/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
cleaning_management/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
cleaning_management/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
cleaning_management/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
cleaning_management/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
cleaning_management/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
cleaning_management/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
cleaning_management/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
cleaning_management/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
cleaning_management/static/description/assets/modules/1.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 KiB

BIN
cleaning_management/static/description/assets/modules/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
cleaning_management/static/description/assets/modules/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
cleaning_management/static/description/assets/modules/4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
cleaning_management/static/description/assets/modules/5.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
cleaning_management/static/description/assets/modules/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
cleaning_management/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
cleaning_management/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
cleaning_management/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
cleaning_management/static/description/assets/screenshots/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
cleaning_management/static/description/assets/screenshots/13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
cleaning_management/static/description/assets/screenshots/14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
cleaning_management/static/description/assets/screenshots/15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
cleaning_management/static/description/assets/screenshots/16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
cleaning_management/static/description/assets/screenshots/2img.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

BIN
cleaning_management/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
cleaning_management/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
cleaning_management/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
cleaning_management/static/description/assets/screenshots/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
cleaning_management/static/description/assets/screenshots/8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
cleaning_management/static/description/assets/screenshots/9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
cleaning_management/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
cleaning_management/static/description/assets/screenshots/img7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
cleaning_management/static/description/banner.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
cleaning_management/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

983
cleaning_management/static/description/index.html

@ -0,0 +1,983 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cleaning Management</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
rel="stylesheet">
</head>
<body>
<section>
<div class="container"
style="font-family: 'Inter', sans-serif !important;background-color: #fff !important;">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12 d-flex justify-content-between flex-wrap align-items-sm-center"
style="border-bottom:1px solid rgba(0, 0, 0, 0.22)">
<div class="my-3">
<img src="assets/misc/Cybrosys R.png"
style="width:auto !important; height:40px !important">
</div>
<div class="my-3 d-flex align-items-center">
<div class="text-center"
style="background-color:#7c7bad !important;font-size: 0.8rem !important; color:#fff !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;">
Community
</div>
<div class="text-center"
style="background-color:#875A7B !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important;min-width: 120px !important;">
Enterprise
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12 text-center d-flex align-items-center flex-column"
style="margin: 80px 0px !important;">
<h1 style="font-size: 2.8rem;font-weight: 700; color:
#1A202C;">
Cleaning Management</h1>
<p class="my-3 mb-4"
style="max-width: 80%; font-weight: bold !important; line-height: 32px; color: #718096;font-size: 25px;">
This Module Assists with Booking for Cleaning and Manages
Cleaning Processes.</p>
<div style="width: 80%; margin-top: 3rem;">
<img src="assets/screenshots/hero.gif"
class="img-responsive" width="100%" height="auto">
</div>
</div>
</div>
<div class="container mt-5 mb-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#714b67 !important">
Key Highlights
</p>
</div>
<div class="row py-4">
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Online Booking Facility</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Different access levels for Users and
Manager</p>
</div>
</div>
</div>
</div>
<div class="row py-4">
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Activity dashboard is available</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Various shifts are available for Employees</p>
</div>
</div>
</div>
</div>
</div>
<div class="container rounded">
<ul class="nav nav-tabs d-flex"
style="width: fit-content;margin: 0 auto;gap: 1rem;">
<li class="col text-center py-2 text-nowrap "
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;">
<a class="active show" data-toggle="tab" href="#tab1"
style="color: #fff;font-weight: 500; background-color: #714B67; text-decoration: none;">
<i class="fa-regular fa-image pr-2"
style="color: #fff;"></i>
Screenshots</a></li>
<li class="col text-center py-2 text-nowrap "
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;">
<a
data-toggle="tab" href="#tab2"
style="color: #fff;font-weight: 500; text-decoration: none;"><i
class="fa-solid fa-star pr-2"
style="color: #fff;"></i>Features</a></li>
<li class="col text-center py-2 text-nowrap "
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;">
<a data-toggle="tab" href="#tab3"
style="color: #fff;font-weight: 500; text-decoration: none; background-color: #714B67;"><i
class="fa-solid fa-book-open pr-2"
style="color: #fff;"></i>Released Notes</a></li>
</ul>
<div class="tab-content"
style="background-color: rgba(121, 113, 119, 0.04);">
<div id="tab1" class="tab-pane fade in active show">
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
App access for User or Manager</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Choose "Cleaning Manager" or "User" in the
settings.
</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/1.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Main Apps menu</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Once access is granted, you will find the
Cleaning Management
app in the Main Apps menu.
</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/2img.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Website view of Cleaning Management</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
When you click on '<b>Cleaning Online
Request</b>,' a form will
appear, allowing you to book Cleaning
processes by providing
your details.
</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/3.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Employee Details</h4>
<p class=" mt8"
style="font-family:Roboto ; color:#280135">To
access the Employee Details in the Cleaning
Management
module, navigate to 'Cleaning Management,'
and then select
'Employee Details' where shifts for all
employees can be
specified.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/4.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Cleaning Team</h4>
<p class=" mt8"
style="font-family:Roboto ; color:#280135">
Cleaning Management -> Cleaning Team. Where
you can specify the
Duty Type, Leader, and Members of a
particular team.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/5.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Booking</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Cleaning Management -> Booking. Where all
created bookings are
listed, and you can also create a new
booking here.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/6.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Cleaning Team with corresponding Cleaning
Time</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
You can get the Team when they are available
on that day.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/img7.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Confirm Booking</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
After verifying the cleaning process and
confirming its
completion, the stage changes to "BOOKED."
At this stage, you
will be presented with "CANCEL" button.
</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/8.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Cleaning Team Duty</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Can track cleaning process time by clicking
'Start' button.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/9.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
INSPECTION</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
To inspect the cleaning process, click on
the "INSPECTION"
button. This will allow you to verify
whether the cleaning has
been performed successfully or not.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/10.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Cleaning Team Duty</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
You can inspect the process by clicking the
"CLEAN" or "DIRT"
button.
</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/11.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
When click on 'CLEAN' Button stage changed
to 'CLEANED'</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/12.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
You can create invoice by clicking 'CREATE
INVOICE' Button after defining 'Unit
Price'.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/13.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Invoice form of Cleaning Management.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/14.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Invoice Smart Button</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Invoice Smart Button in Cleaning Booking
Form.
</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/15.png"
class="img-responsive" width="100%"
height="auto">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Dashboard View</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Access the dashboard to view the total
number of Bookings,
Cleaning Team data, and Clean and Dirty
counts. The dashboard
will also include two bar graphs
representing Bookings and
Quality of work. Graphs can be filtered by
This Month, This
Quarter, This Year, and This Week for
comprehensive analysis.</p>
</div>
<div class="row justify-content-center p-3 w-100 m-0">
<img src="assets/screenshots/16.png"
class="img-responsive" width="100%" height="auto">
</div>
</div>
</div>
</div>
</div>
<div id="tab2" class="tab-pane fade">
<div class="col-mg-12" style="padding: 1rem 4rem;">
<ul style="list-style: none; padding: 1rem 0;font-weight: 500;">
<li class="py-3"
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<span style="margin-right: 12px;"><img
src="assets/misc/star (1) 2.svg"
alt=""
width="16px"></span>Import CSV File
Format.
</li>
<li class="py-3"
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<span style="margin-right: 12px;"><img
src="assets/misc/star (1) 2.svg"
alt=""
width="16px"></span>Import XLSX File
Format.
</li>
<li class="py-3"
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<span style="margin-right: 12px;"><img
src="assets/misc/star (1) 2.svg"
alt=""
width="16px"></span>Import OFX File
Format.
</li>
<li class="py-3"
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<span style="margin-right: 12px;"><img
src="assets/misc/star (1) 2.svg"
alt=""
width="16px"></span>Import QIF File
Format.
</li>
</ul>
</div>
</div>
<div id="tab3" class="tab-pane fade">
<div class="col-mg-12 active" style="padding: 1rem 4rem;">
<div class="py-3"
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="d-flex mb-3"
style="font-size: 0.8rem; font-weight: 500;"><span>Version
17.0.1.0.0</span><span
class="px-2">|</span><span
style="color: #714B67;font-weight: 600;">Released on:6th Feb 2024</span>
</div>
<p class="m-0"
style=" color:#718096!important; font-size:1rem !important;line-height: 28px;">
Initial commit for Cleaning Management</p>
</div>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Related Products</p>
</div>
</div>
<div id="myCarousel" class="carousel slide py-3" data-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<div class="row p-4">
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/base_accounting_kit/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/1.gif"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Odoo 17 Full Accounting
Kit</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/invoice_format_editor/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/2.png"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Invoice Format
Editor</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/inventory_barcode_scanning/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/3.png"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Barcode scanning in
Inventory</p>
</div>
</a>
</div>
</div>
</div>
</div>
<div class="carousel-item">
<div class="row p-4">
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/whatsapp_redirect/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/4.jpg"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Send Whatsapp
Message</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/base_account_budget/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px;">
<img src="assets/modules/5.jpg"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Budget Management</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/product_barcode/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px;">
<img src="assets/modules/6.png"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Product Barcode
Generator</p>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
<a class="carousel-control-prev" href="#myCarousel"
data-slide="prev" style="width: 35px; color: #000;">
<span class="carousel-control-prev-icon">
<i class="fa fa-chevron-left"
style="font-size: 24px;"></i>
</span>
</a>
<a class="carousel-control-next" href="#myCarousel"
data-slide="next" style="width: 35px; color: #000;">
<span class="carousel-control-next-icon">
<i class="fa fa-chevron-right"
style="font-size: 24px;"></i>
</span>
</a>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Our Services</p>
</div>
</div>
<div class="container my-5">
<div class="row py-3">
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#13EA36 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/cogs.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Customization</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#DBC711; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/wrench.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Implementation</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#FF6B6B ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/lifebuoy.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Support</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#FFA801 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/user.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Hire
Odoo Developer</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#54A0FF; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/puzzle.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Integration</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#6D7680 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/update.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Migration</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#786FA6 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/consultation.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Consultancy</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#F8A5C2 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/training.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Implementation</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#E6BE26; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/license.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Licensing Consultancy</p>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Our Industries</p>
</div>
</div>
<div class="container">
<div class="row my-5 py-4">
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100 "
style="border-right: 1px solid rgb(209, 209, 209); border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; box-shadow: 6px 0 10px rgba(228, 227, 227, 0.373);">
<img src="assets/icons/trading-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">Trading</p>
<p>Easily procure and sell your products</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgb(209, 209, 209); padding: 30px;">
<img src="assets/icons/pos-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">POS</p>
<p>Easy configuration and convivial experience</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgba(0, 0, 0, 0.2); padding: 30px; box-shadow: 0 5px 10px rgba(228, 227, 227, 0.373)">
<img src="assets/icons/education-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">
Education</p>
<p>A platform for educational management</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; ">
<img src="assets/icons/manufacturing-black.png"
width="42px" height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">
Manufacturing</p>
<p>Plan, track and schedule your operations</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;">
<img src="assets/icons/ecom-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">E-commerce &
Website</p>
<p>Mobile friendly, awe-inspiring product pages</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;box-shadow: 0 -5px 10px rgba(228, 227, 227, 0.373);">
<img src="assets/icons/service-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">Service
Management</p>
<p>Keep track of services and invoice</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px; ">
<img src="assets/icons/restaurant-black.png"
width="42px" height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">
Restaurant</p>
<p>Run your bar or restaurant methodically</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style=" padding: 30px;box-shadow: -5px 0 10px rgba(228, 227, 227, 0.373);">
<img src="assets/icons/hotel-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">Hotel
Management</p>
<p>An all-inclusive hotel management application</p>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Support</p>
</div>
</div>
<div class="container my-5">
<div class="row" style="background-color: #FFFAFE;">
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center"
style="border-right: 1px solid #D9D9D9;">
<div style="padding: 30px;">
<div class="d-flex align-items-center">
<img src="assets/misc/support (1) 1.svg" alt=""
width="60px" style="margin-right: 12px;">
<div style="padding: 0px 8px;">
<span
style="color: #714B67;font-size: 24px;font-weight: 600;padding-bottom: 1rem;">Need
Help?</span>
<p class="m-0" style="color:#718096;">Got
questions or need help? Get in touch.</p>
<div style="font-weight: 400;"><span><img
src="assets/misc/support-email.svg"
alt=""
width="18px"
style="filter: invert(1);margin-right: 0.8rem;"></span>odoo@cybrosys.com
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center">
<div style="padding: 30px;">
<div class="d-flex align-items-center">
<img src="assets/misc/whatsapp 1.svg" alt=""
width="60px" style="margin-right: 12px;">
<div>
<span style="color: #714B67;font-size: 24px;font-weight: 600;">WhatsApp</span>
<p class="m-0" style="color:#718096;">Say hi to
us on WhatsApp!</p>
<div style="font-weight: 400; font-size: 16px;"><span><img
src="assets/misc/phone.svg"
alt="" width="14px"
style="filter: invert(1); margin-right: 0.8rem;"></span>+91
99456767686
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

178
cleaning_management/static/src/css/cleaning_management_dashboard.css

@ -0,0 +1,178 @@
.o_action_manager {
overflow: auto !important;
}
.cleaning_dashboards {
padding-top: 10px;
background-color: #f8faff !important;
}
.cleaning-card h4 {
font-size: 1.1rem;
}
/* Widget One
---------------------------*/
.stat-content {
display: inline-block;
width: 66%;
}
.stat-icon {
display: inline-block;
}
.stat-widget-one .stat-icon {
vertical-align: top;
margin: auto;
width: 100%;
color: #01c490;
}
.stat-widget-one .stat-icon i {
font-size: 30px;
font-weight: 900;
display: inline-block;
color: #01c490;
}
.stat-widget-one .stat-text {
font-size: 14px;
color: #868e96;
font-weight: bold;
}
.stat-widget-one .stat-digit {
font-size: 24px;
color: #02448b;
}
.stat-count {
font-size: 20px;
text-align: center;
color: #00438b;
}
.stat-title {
font-size: 17px;
text-align: center;
color: #00438b;
}
/*=====================Dashboard===========================*/
.cleaning_dashboards {
background-color: #f8faff !important;
padding: 0px !important;
}
.container-fluid.o_hr_dashboard {
padding: 0px !important;
}
.cleaning-card {
padding: 0px;
margin-bottom: 1.5rem;
border-radius: 0px;
box-shadow: none;
background: none;
transition: transform 0.2s ease, box-shadow 0.2s ease;
will-change: transform, box-shadow;
}
.cleaning-card:hover {
transform: translateY(-2px) translateZ(0) !important;
box-shadow: 0 10px 10px 0 rgba(62, 57, 107, 0.12), 0 0 0 transparent !important;
}
.cleaning {
margin-top: 3%;
margin-bottom: 2%;
}
.cleaning .stat-icon {
border-radius: 15px 0 0 15px;
width: 30%;
height: 100px;
text-align: center;
padding-top: 10%;
background: #314384ff;
color: #fff;
}
.cleaning .cleaning-card {
border-radius: 15px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
will-change: transform, box-shadow;
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07),
0 2px 9px 0 rgba(62, 57, 107, 0.06);
}
.stat-widget-one .stat-text {
font-size: 14px;
color: #314384ff;
margin: 2rem 0rem 1rem 0rem;
text-align: center;
}
.stat-widget-one .stat-digit {
font-size: 20px;
font-weight: bolder;
padding: 1px 10px 2px 0;
color: #8061a9;
text-shadow: 0px 0px 20px #000000;
text-align: center;
}
.stat-widget-one .stat-icon i {
/*border-radius: 15px 0 0 15px;*/
font-size: 25px;
font-weight: 900;
display: inline-block;
color: #fff;
}
.stat-widget-one {
border-radius: 15px;
background-color: white;
text-align: inherit !important;
}
.stat-widget-one {
width: 100%;
}
.cleaning_count .stat-icon {
background: #964ec2 !important;
}
.dirty_count .stat-icon {
background: #813de6 !important;
}
.cleaning_teams .stat-icon {
background: #6f23a9 !important;
}
.cleaning_count .stat-widget-one .stat-text {
color: #964ec2;
}
.dirty_count .stat-widget-one .stat-text {
color: #813de6;
}
.cleaning_teams .stat-widget-one .stat-text {
color: #6f23a9;
}
.cleaning-card-body {
display: flex;
justify-content: space-between;
align-items: center;
}
.counting{
width: 414px;
margin-left: 762px;
margin-top: -291px;
}

460
cleaning_management/static/src/js/cleaning_management_dashboard.js

@ -0,0 +1,460 @@
/** @odoo-module */
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Component } from "@odoo/owl";
import { onMounted, useRef } from "@odoo/owl";
const actionRegistry = registry.category("actions");
/** Create component 'CleaningDashboard' by extending Component **/
export class CleaningDashboard extends Component {
/**
* Obtain yearly, monthly, quarterly, and weekly bookings,
as well as clean and dirt reports.
*/
setup() {
super.setup(...arguments);
this.orm = useService('orm')
this.rootRef = useRef('main-content')
this.today_sale = [];
// When the component is mounted, render various charts
onMounted(async () => {
await this.RenderDashboard();
await this.render_graph();
const Ref = this.rootRef.el
$(Ref.querySelector("#activity_week")).hide();
$(Ref.querySelector("#activity_year")).hide();
$(Ref.querySelector("#activity_quarter")).hide();
$(Ref.querySelector("#activity_month")).show();
$(Ref.querySelector("#quality_week")).hide();
$(Ref.querySelector("#quality_month")).show();
$(Ref.querySelector("#quality_quarter")).hide();
$(Ref.querySelector("#quality_year")).hide();
await this.orm.call('cleaning.management.dashboard',
'cleaning_count', [0]).then(function(result) {
if ((result['inspections'].length > 0) &&
(result['bookings'].length > 0)) {
$(Ref.querySelector(".report")).show();
$(Ref.querySelector(".bookings")).show();
$(Ref.querySelector(".quality")).show();
} else if (result['bookings'].length > 0) {
$(Ref.querySelector(".report")).show();
$(Ref.querySelector(".bookings")).show();
$(Ref.querySelector(".quality")).hide();
} else if (result['inspections'].length > 0) {
$(Ref.querySelector(".report")).show();
$(Ref.querySelector(".bookings")).show();
$(Ref.querySelector(".quality")).show();
} else {
$(Ref.querySelector(".report")).hide();
$(Ref.querySelector(".bookings")).hide();
$(Ref.querySelector(".quality")).hide();
}
})
});
}
ChangeFiltration(ev) {
console.log("ChangeFiltration", ev)
ev.stopPropagation();
// Return elements where event occurred
var option_val = ev.target.value;
if (option_val == "this_year") {
this.OnclickThisYear(option_val);
} else if (option_val == "this_quarter") {
this.OnclickThisQuarter(option_val);
} else if (option_val == "this_month") {
this.OnclickThisMonth(option_val);
} else if (option_val == "this_week") {
this.OnclickThisWeek(option_val);
}
}
//Generate a dashboard displaying the count of bookings, dirty instances, completed cleanings, and the cleaning team.
RenderDashboard() {
console.log("RenderDashboard")
var self = this;
this.orm.call('cleaning.management.dashboard',
'get_dashboard_count', [0]).then(function(result) {
console.log("result", result)
const MainDiv = self.rootRef.el
const bookings_count = MainDiv.querySelector("#bookings_count")
bookings_count.classList.add("stat-digit");
bookings_count.textContent = result.bookings;
const teams_count = MainDiv.querySelector("#teams_count")
teams_count.classList.add("stat-digit");
teams_count.textContent = result.teams;
const cleaning_count = MainDiv.querySelector("#cleaning_count")
cleaning_count.classList.add("stat-digit");
cleaning_count.textContent = result.cleaned;
const dirty_count = MainDiv.querySelector("#dirty_count")
dirty_count.classList.add("stat-digit");
dirty_count.textContent = result.dirty;
});
}
//Get booking records
GetBookings() {
console.log("GetBookings", )
this.env.services.action.doAction({
name: ("Bookings"),
type: 'ir.actions.act_window',
res_model: 'cleaning.booking',
view_mode: 'tree,form,calendar',
views: [
[false, 'list'],
[false, 'form']
],
domain: [
['state', '=', 'booked']
],
target: 'current',
})
}
//Get team count records
GetTeams() {
this.env.services.action.doAction({
name: ("Teams"),
type: 'ir.actions.act_window',
res_model: 'cleaning.team',
view_mode: 'tree,form,calendar',
views: [
[false, 'list'],
[false, 'form']
],
target: 'current',
})
}
//Get cleaning count
GetCleaning() {
this.env.services.action.doAction({
name: ("Count of Cleaning"),
type: 'ir.actions.act_window',
res_model: 'cleaning.inspection',
view_mode: 'tree,form,calendar',
views: [
[false, 'list'],
[false, 'form']
],
domain: [
['state', '=', 'cleaned']
],
target: 'current',
})
}
//Get dirty count
GetDirty() {
this.env.services.action.doAction({
name: ("Count of dirty"),
type: 'ir.actions.act_window',
res_model: 'cleaning.inspection',
view_mode: 'tree,form,calendar',
views: [[false, 'list'], [false, 'form']],
domain: [['state', '=', 'dirty']],
target: 'current',
})
}
//Generate a graph representing the yearly, monthly, weekly, and quarterly records.
render_graph() {
console.log("render_graph")
var self = this
var ctx = self.rootRef.el.querySelector(".total_bookings_year")
console.log("ctx", ctx)
this.orm.call('cleaning.management.dashboard', 'get_the_booking_year', [0])
.then(function(result) {
self.total_booking_stage =
result['total_booking_stage_year'],
self.total_booking_stage_draft_year =
result['total_booking_stage_draft_year'],
self.total_booking_stage_booked_year =
result['total_booking_stage_booked_year'],
self.total_booking_stage_cleaned_year =
result['total_booking_stage_cleaned_year'],
self.total_booking_stage_canceled_year =
result['total_booking_stage_canceled_year']
var ctx = self.rootRef.el.querySelector("#activity_year")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['total_booking_stage_year'],
datasets: [{
label: 'Stage', data: [
result['total_booking_stage_draft_year'],
result['total_booking_stage_booked_year'],
result['total_booking_stage_cleaned_year'],
result['total_booking_stage_canceled_year']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_bookings_week")
this.orm.call('cleaning.management.dashboard', 'get_the_booking_week', [0]).then(function(result) {
self.total_booking_stage =
result['total_booking_stage_week'],
self.total_booking_stage_draft =
result['total_booking_stage_draft_week'],
self.total_booking_stage_booked =
result['total_booking_stage_booked_week'],
self.total_booking_stage_cleaned =
result['total_booking_stage_cleaned_week'],
self.total_booking_stage_canceled =
result['total_booking_stage_canceled_week']
var ctx = self.rootRef.el.querySelector("#activity_week")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['total_booking_stage_week'],
datasets: [{
label: 'Stage',
data: [
result['total_booking_stage_draft_week'],
result['total_booking_stage_booked_week'],
result['total_booking_stage_cleaned_week'],
result['total_booking_stage_canceled_week']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_bookings_month")
this.orm.call('cleaning.management.dashboard', 'get_the_booking_month', [0]).then(function(result) {
const dataPoints = [
result['total_booking_stage_draft_month'],
result['total_booking_stage_booked_month'],
result['total_booking_stage_cleaned_month'],
result['total_booking_stage_canceled_month']
].filter(value => value !== null && value !== '' &&
value !== 0);
var ctx = self.rootRef.el.querySelector("#activity_month")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['total_booking_stage_month'],
datasets: [{
label: 'Stage',
data: dataPoints,
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_bookings_quarter")
this.orm.call('cleaning.management.dashboard', 'get_the_booking_quarter', [0]).then(function(result) {
self.total_booking_stage =
result['total_booking_stage_quarter'],
self.total_booking_stage_draft =
result['total_booking_stage_draft_quarter'],
self.total_booking_stage_booked =
result['total_booking_stage_booked_quarter'],
self.total_booking_stage_cleaned =
result['total_booking_stage_cleaned_quarter'],
self.total_booking_stage_canceled =
result['total_booking_stage_canceled_quarter']
const ctx = self.rootRef.el.querySelector("#activity_quarter")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['total_booking_stage_quarter'],
datasets: [{
label: 'Stage',
data: [
result['total_booking_stage_draft_quarter'],
result['total_booking_stage_booked_quarter'],
result['total_booking_stage_cleaned_quarter'],
result['total_booking_stage_canceled_quarter']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_quality_year")
this.orm.call('cleaning.management.dashboard', 'quality_year', [0]).then(function(result) {
self.quality_year = result['quality_year'],
self.cleaned_quality_year = result['cleaned_quality_year'],
self.dirty_quality_year = result['dirty_quality_year']
const ctx = self.rootRef.el.querySelector("#quality_year")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['quality_year'],
datasets: [{
label: 'Stage',
data: [result['cleaned_quality_year'],
result['dirty_quality_year']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_quality_week")
this.orm.call('cleaning.management.dashboard', 'quality_week', [0]).then(function(result) {
self.quality_week = result['quality_week'],
self.cleaned_quality_week = result['cleaned_quality_week'],
self.dirty_quality_week = result['dirty_quality_week']
const ctx = self.rootRef.el.querySelector("#quality_week")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['quality_week'],
datasets: [{
label: 'Stage',
data: [result['cleaned_quality_week'],
result['dirty_quality_week']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_quality_month")
this.orm.call('cleaning.management.dashboard', 'quality_month', [0]).then(function(result) {
self.quality_month = result['quality_month'],
self.cleaned_quality_month = result['cleaned_quality_month'],
self.dirty_quality_month = result['dirty_quality_month']
const ctx = self.rootRef.el.querySelector("#quality_month")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['quality_month'],
datasets: [{
label: 'Stage',
data: [result['cleaned_quality_month'],
result['dirty_quality_month']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
var self = this
var ctx = self.rootRef.el.querySelector(".total_quality_quarter")
this.orm.call('cleaning.management.dashboard', 'quality_quarter', [0]).then(function(result) {
self.quality_quarter = result['quality_quarter'];
self.cleaned_quality_year = result['cleaned_quality_quarter'];
self.dirty_quality_year = result['dirty_quality_quarter'];
const ctx = self.rootRef.el.querySelector("#quality_quarter")
new Chart(ctx, {
type: 'bar',
data: {
labels: result['quality_quarter'],
datasets: [{
label: 'Stage',
data: [result['cleaned_quality_quarter'],
result['dirty_quality_quarter']
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
})
})
}
// Show yearly result
OnclickThisYear(ev) {
console.log("OnclickThisYear")
self.$("#activity_week").hide();
self.$("#activity_month").hide();
self.$("#activity_quarter").hide();
self.$("#activity_year").show();
self.$("#quality_week").hide();
self.$("#quality_month").hide();
self.$("#quality_quarter").hide();
self.$("#quality_year").show();
}
// Show monthly result
OnclickThisMonth(ev) {
self.$("#activity_week").hide();
self.$("#activity_year").hide();
self.$("#activity_quarter").hide();
self.$("#activity_month").show();
self.$("#quality_week").hide();
self.$("#quality_month").show();
self.$("#quality_quarter").hide();
self.$("#quality_year").hide();
}
// Show weekly result
OnclickThisWeek(ev) {
self.$("#activity_year").hide();
self.$("#activity_month").hide();
self.$("#activity_quarter").hide();
self.$("#activity_week").show();
self.$("#quality_week").show();
self.$("#quality_month").hide();
self.$("#quality_quarter").hide();
self.$("#quality_year").hide();
}
// Show quarterly result
OnclickThisQuarter(ev) {
self.$("#activity_week").hide();
self.$("#activity_month").hide();
self.$("#activity_year").hide();
self.$("#activity_quarter").show();
self.$("#quality_week").hide();
self.$("#quality_month").hide();
self.$("#quality_quarter").show();
self.$("#quality_year").hide();
}
}
CleaningDashboard.template = "CleaningDashBoard";
actionRegistry.add("cleaning_dashboard_tags", CleaningDashboard);

99
cleaning_management/static/src/js/cleaning_management_website.js

@ -0,0 +1,99 @@
/** @odoo-module **/
import publicWidget from "@web/legacy/js/public/public_widget";
import { useService } from "@web/core/utils/hooks";
publicWidget.registry.portalDetails = publicWidget.Widget.extend({
/**
* Retrieve all the data from the table.
*/
selector: '.form',
events: {
'change select[name="cleaning_time"]': '_onChangeTime',
'change input[name="cleaning_date"]': '_onChangeDate',
},
/** Super start function to define orm **/
start: function() {
this._super.apply(this, arguments);
this.orm = this.bindService("orm");
},
/** Function to retrieve the team corresponding to the cleaning time **/
_onChangeTime: function() {
if (!this.$el.find('#cleaning_date').val()) {
const open_deactivate_modal = true;
const modalHTML = `
<div class="modal ${open_deactivate_modal ? 'show d-block' : ''}" id="popup_error_message" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="btn-close" data-dismiss="modal"></button>
</div>
<form class="oe_login_form modal-body" role="form">
Please Choose a Cleaning Date.
</form>
</div>
</div>
</div>
`;
console.log("$('body')", $("body"))
$("body").append(modalHTML);
$("body").find("#popup_error_message").find(".btn-close").on("click", function() {
$("body").find("#popup_error_message").remove();
});
} else {
this.$el.find('#cleaning_team_id').empty();
var self = this;
this.orm.call('cleaning.management.website', 'get_team_details', [0])
.then(function(result) {
result.team_list.forEach(element => {
var change_time = self.$el.find('#cleaning_time').val();
var change_date = self.$el.find('#cleaning_date').val();
if (change_time == element.duty) {
var domain = [
['cleaning_date', '=', change_date],
['cleaning_time', '=', change_time],
['state', '=', 'draft'],
];
self.orm.call('cleaning.team.duty', 'search_read', [domain, ['team_id']])
.then(function(res) {
if (res.length > 0) {
var team_array = [];
res.forEach(e => {
var domain_for_duty = [
['cleaning_duty_ids', 'not in', e.id],
['id', 'not in', e.team_id[0]],
];
self.orm.call('cleaning.team', 'search_read', [domain_for_duty, ['id', 'name']]).then(function(res) {
res.forEach(team => {
if (!team_array.includes(team.id)) {
team_array.push(team.id);
var option = $('<option>').val(team.id).text(team.name);
self.$el.find('#cleaning_team_id').append(option);
}
});
})
})
} else {
var change_time = self.$el.find('#cleaning_time').val();
var domain_for_time = [
['duty_type', '=', change_time],
];
self.orm.call('cleaning.team', 'search_read', [domain_for_time, ['id', 'name']]).then(function(res) {
res.forEach(team => {
var option = $('<option>').val(team.id).text(team.name);
self.$el.find('#cleaning_team_id').append(option);
})
})
}
});
}
})
});
}
},
_onChangeDate: function() {
this.$el.find('#cleaning_team_id').empty();
if (this.$el.find('#cleaning_time').val() != 'null') {
this.$el.find('#cleaning_time').val("");
}
},
});

159
cleaning_management/static/src/xml/cleaning_management_dashboard_template.xml

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates>
<t t-name="CleaningDashBoard" owl="1">
<div class="oh_dashboards" t-ref="main-content"
style="height:100%; overflow-y: scroll; overflow-x: hidden;">
<!-- Tiles -->
<div class="tiles">
<div class="row main-section">
<div class="col-sm-6 col-md-3-12 col-md-6 col-lg-3 cleaning_cooking cleaning cleaning_orders"
t-on-click="GetBookings">
<div class="cleaning-card">
<div class="cleaning-card-body">
<div class="stat-widget-one">
<div class="stat-icon">
<i class="fa fa-calendar"/>
</div>
<div class="stat-content">
<div class="stat-text">Bookings</div>
<div class="stat-digit"
id="bookings_count"/>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-md-3-12 col-md-6 col-lg-3 salon_spa_orders
cleaning cleaning_teams" t-on-click="GetTeams">
<div class="cleaning-card">
<div class="cleaning-card-body">
<div class="stat-widget-one">
<div class="stat-icon">
<i class="fa fa-users"/>
</div>
<div class="stat-content">
<div class="stat-text">Cleaning Teams
</div>
<div class="stat-digit"
id="teams_count"/>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-md-3-12 col-md-6 col-lg-3 salon_spa_sales
cleaning cleaning_count" t-on-click="GetCleaning">
<div class="cleaning-card">
<div class="cleaning-card-body">
<div class="stat-widget-one">
<div class="stat-icon">
<i class="fa fa-check"/>
</div>
<div class="stat-content">
<div class="stat-text">Cleaning Counts
</div>
<div class="stat-digit"
id="cleaning_count"/>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-md-3-12 col-md-6 col-lg-3 salon_spa_clients
cleaning dirty_count" t-on-click="GetDirty">
<div class="cleaning-card">
<div class="cleaning-card-body">
<div class="stat-widget-one">
<div class="stat-icon">
<i class="fa fa-times "/>
</div>
<div class="stat-content">
<div class="stat-text">Dirty Counts
</div>
<div class="stat-digit"
id="dirty_count"/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<section class="dashboard_main_section" id="main_section_login">
<div style="height:50px">
</div>
<div class="report">
<div class="col-sm-12 mb-4">
<div class="row">
<div class="col-12 col-sm-12 col-md-8">
<h2 class="section-header">Cleaning Reports</h2>
</div>
<div class="col-12 col-sm-12 col-md-4">
<form class="form-group">
<select id="filtration" class="form-control"
t-on-change="ChangeFiltration">
<option id="this_year"
value="this_year">
This Year
</option>
<option id="this_quarter"
value="this_quarter">This
Quarter
</option>
<option id="this_month"
value="this_month"
selected="">This Month
</option>
<option id="this_week"
value="this_week">
This Week
</option>
</select>
</form>
</div>
</div>
<hr/>
</div>
</div>
</section>
<div class="col-sm-12 mb-4">
<div class="row">
<div class="col-12 col-sm-12 col-md-8">
<div class="bookings" style="width:500px;">
<h2 class="cleaning_bookings">Bookings</h2>
<hr/>
<div class="graph_canvas">
<canvas class="total_bookings_year" width="200"
height="120" id="activity_year"/>
<canvas class="total_bookings_week" width="200"
height="120" id="activity_week"/>
<canvas class="total_bookings_month" width="200"
height="120" id="activity_month"/>
<canvas class="total_bookings_quarter"
width="200"
height="120" id="activity_quarter"/>
</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-4">
<div class="quality" style="width:500px;">
<h2 class="qty_header">Quality of work</h2>
<hr/>
<div class="graph_canvas">
<canvas class="total_quality_year" width="200"
height="120" id="quality_year"/>
<canvas class="total_quality_week" width="200"
height="120" id="quality_week"/>
<canvas class="total_quality_month" width="200"
height="120" id="quality_month"/>
<canvas class="total_quality_quarter"
width="200"
height="120" id="quality_quarter"/>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>

39
cleaning_management/views/building_type_views.xml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Allows users to view the building type as a tree and
provides a form for inputting building information.-->
<record id="building_type_action" model="ir.actions.act_window">
<field name="name">Building Type</field>
<field name="res_model">building.type</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Building Type
</p>
</field>
</record>
<!--Building type form view-->
<record id="building_type_view_form" model="ir.ui.view">
<field name="name">building.type.view.form</field>
<field name="model">building.type</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
<!--Building type tree view-->
<record id="building_type_view_tree" model="ir.ui.view">
<field name="name">building.type.view.tree</field>
<field name="model">building.type</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
</tree>
</field>
</record>
</odoo>

94
cleaning_management/views/cleaning_booking_views.xml

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Allows users to view the cleaning management as a tree and
provides a form for inputting cleaning management-->
<record id="cleaning_booking_action" model="ir.actions.act_window">
<field name="name">Cleaning Booking</field>
<field name="res_model">cleaning.booking</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Booking
</p>
</field>
</record>
<!--Cleaning management form view-->
<record id="cleaning_booking_view_form" model="ir.ui.view">
<field name="name">cleaning.booking.view.form</field>
<field name="model">cleaning.booking</field>
<field name="arch" type="xml">
<form duplicate="0">
<header>
<button name="action_booking" string="Confirm"
class="oe_highlight" type="object" invisible="not confirm_stage"/>
<button name="action_cancel" string="Cancel"
class="oe_highlight" type="object" invisible="cancel_stage"/>
<button name="action_create_invoice" type="object"
string="Create Invoice"
invisible="state != 'cleaned' or invoice_count > 0"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,booked,cleaned"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button type="object" name="action_view_invoice"
class="oe_stat_button"
icon="fa-pencil-square-o"
invisible="invoice_count == 0 or state in ['draft', 'booked', 'cancelled']">
<field name="invoice_count" widget="statinfo"
string="Invoice"/>
</button>
</div>
<group>
<group>
<field name="customer_name_id"/>
<field name="address"/>
<field name="building_type_id"/>
<field name="booking_date"/>
<field name="location_state_id"/>
<field name="place"/>
<field name="confirm_stage" invisible="1"/>
<field name="clean_stage" invisible="1"/>
<field name="cancel_stage" invisible="1"/>
</group>
<group position="right">
<field name="cleaning_date"/>
<field name="cleaning_time"/>
<field name="cleaning_team_ids" invisible="1"/>
<field name="cleaning_team_id" options="{'no_create': True}"/>
<field name="description"/>
<field name="unit_price"/>
<field name="total_hour_of_working" invisible="1"/>
</group>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids" widget="mail_activity"/>
</div>
</form>
</field>
</record>
<!--Cleaning management tree view-->
<record id="cleaning_booking_view_tree" model="ir.ui.view">
<field name="name">cleaning.booking.view.tree</field>
<field name="model">cleaning.booking</field>
<field name="arch" type="xml">
<tree>
<field name="customer_name_id"/>
<field name="address"/>
<field name="building_type_id"/>
<field name="booking_date"/>
<field name="cleaning_team_id"/>
<field name="cleaning_date"/>
<field name="cleaning_time"/>
<field name="state" widget="badge"
decoration-success="state == 'cleaned'"
decoration-info="state == 'booked'"
decoration-danger="state == 'cancelled'"
optional="show"/>
</tree>
</field>
</record>
</odoo>

65
cleaning_management/views/cleaning_inspection_views.xml

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Allows users to view the cleaning inspection as a tree and
provides a form for inputting cleaning inspection-->
<record id="cleaning_inspection_action" model="ir.actions.act_window">
<field name="name">Cleaning Inspection</field>
<field name="res_model">cleaning.inspection</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Cleaning Inspection
</p>
</field>
</record>
<!--View cleaning inspection form-->
<record id="cleaning_inspection_view_form" model="ir.ui.view">
<field name="name">cleaning.inspection.view.form</field>
<field name="model">cleaning.inspection</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_clean" type="object" string="Clean"
class="oe_highlight"
invisible="state == 'cleaned' or state == 'dirty'"/>
<button name="action_dirt" type="object" string="Dirt"
class="oe_highlight"
invisible="state == 'cleaned' or state == 'dirty'"/>
<button name="action_reclean" type="object" string="ReClean"
class="oe_highlight"
invisible="state == 'draft' or state == 'cleaned'"/>
<field name="state" widget="statusbar"
statusbar_visisble="draft,cleaned,dirty"/>
</header>
<sheet>
<group>
<field name="inspector_name_id"/>
<field name="inspection_date_and_time"/>
<field name="cleaning_team_id"/>
<field name="date_from"/>
<field name="date_to"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids" widget="mail_activity"/>
</div>
</form>
</field>
</record>
<!--Cleaning inspection tree view-->
<record id="cleaning_inspection_view_tree" model="ir.ui.view">
<field name="name">cleaning.inspection.view.tree</field>
<field name="model">cleaning.inspection</field>
<field name="arch" type="xml">
<tree>
<field name="inspector_name_id"/>
<field name="inspection_date_and_time"/>
<field name="cleaning_team_id"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>
</field>
</record>
</odoo>

8
cleaning_management/views/cleaning_management_dashboard_views.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Provide client action for dashboard menu-->
<record id="cleaning_management_dashboard_action" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">cleaning_dashboard_tags</field>
</record>
</odoo>

31
cleaning_management/views/cleaning_management_menus.xml

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Create menus for cleaning management-->
<menuitem id="cleaning_management_menu_root" name="Cleaning Management"
groups="cleaning_management.cleaning_management_group_manager,cleaning_management.cleaning_management_group_user"
web_icon="cleaning_management,static/description/icon.png">
<menuitem id="cleaning_management_menu_dashboard_datas" name="Dashboard"
action="cleaning_management_dashboard_action" sequence="0"/>
<menuitem id="cleaning_management_main_menu" name="Cleaning Management">
<menuitem id="cleaning_management_menu" name="Booking"
action="cleaning_booking_action" sequence="10"/>
<menuitem id="cleaning_team_duty_menu" name="Cleaning Team Duty"
action="cleaning_team_duty_action"
sequence="20"/>
<menuitem id="cleaning_inspection_menu" name="Inspection"
action="cleaning_inspection_action"
sequence="30"/>
</menuitem>
<menuitem id="cleaning_management_menu_configuration" name="Configuration">
<menuitem id="cleaning_shift_menu" name="Timeshift"
action="cleaning_shift_action" sequence="10"/>
<menuitem id="cleaning_management_menu_building_type" name="Building Type"
action="building_type_action" sequence="20"/>
<menuitem id="employee_details_menu" name="Employee Details"
action="employee_details_action"
sequence="30"/>
<menuitem id="cleaning_team_menu" name="Cleaning Team"
action="cleaning_team_action" sequence="40"/>
</menuitem>
</menuitem>
</odoo>

213
cleaning_management/views/cleaning_management_website_template.xml

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Website view for creating an order-->
<template id="cleaning_online_request" name="Cleaning Online Request Form">
<t t-call="website.layout">
<div id="wrap" class="website">
<div class="container">
<div style="height:20px"/>
<div>
<h1 id="heading" align="center">Cleaning Management</h1>
</div>
<div style="height:50px"/>
<form role="form" action="/cleaning/request/form/submit"
method="POST">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="form">
<div class="form-group">
<label for="customer_name_id"
class="control-label">
Customer Name :
</label>
<div class="col-sm">
<select name="customer_name_id"
class="form-control link-style"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;">
<t t-foreach="customer_name_rec"
t-as="customer_name">
<option t-esc="customer_name.name"
t-att-value="customer_name.id"/>
</t>
</select>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="address" class="control-label">
Address :
</label>
<div class="col-sm">
<input type="Char" name="address"
t-att-value="address"
id="cleaning_address"
class="form-control"
required="1"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;"/>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="building_type_id"
class="control-label">
Building Type :
</label>
<div class="col-sm">
<select name="building_type_id" required="1"
class="form-control link-style"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;">
<t t-foreach="building_type_rec"
t-as="building_type">
<option t-esc="building_type.name"
t-att-value="building_type.id"/>
</t>
</select>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="booking_date" class="control-label">
Booking Date :
</label>
<div class="col-sm">
<input type="Date" name="booking_date"
t-att-value="booking_date"
id="cleaning_booking_date"
class="form-control"
required="required"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;"/>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="cleaning_date"
class="control-label">
Cleaning Date :
</label>
<div class="col-sm">
<input type="Date" name="cleaning_date"
t-att-value="cleaning_date"
id="cleaning_date"
class="form-control"
required="1"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;"/>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="cleaning_time"
class="control-label">
Cleaning Time :
</label>
<div class="col-sm">
<select name="cleaning_time"
t-att-value="cleaning_time"
class="form-control link-style"
id="cleaning_time" required="1"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;">
<option value="null"> </option>
<option value="morning">Morning</option>
<option value="evening">Evening</option>
<option value="night">Night</option>
</select>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="cleaning_team_id"
class="control-label">
Cleaning Team :
</label>
<div class="col-sm">
<select name="cleaning_team_id"
class="form-control link-style"
id="cleaning_team_id" required="1"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;">
<option t-att-value=" "/>
<t t-foreach="cleaning_team_id"
t-as="team">
<option t-esc="team.name"
t-att-value="team.id"/>
</t>
</select>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="location_state_id"
class="control-label">Location :
</label>
<div class="col-sm">
<select name="location_state_id"
class="form-control link-style"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;">
<t t-foreach="location_state_id"
t-as="location">
<option t-esc="location.name"
t-att-value="location.id"/>
</t>
</select>
</div>
</div>
<div style="height:22px"/>
<div class="form-group">
<label for="description" class="control-label">
Description :
</label>
<div class="col-sm">
<input type="Char" name="description"
t-att-value="description"
id="cleaning_description"
class="form-control"
required="required"
style="width:81%;margin-left:20%;
margin-top:-3%;padding-bottom: 1%;"/>
</div>
</div>
<div style="height:22px"/>
<div class="form-group col-12 s_website_form_submit"
data-name="Submit Button">
<div style="width: 150px;"
class="s_website_form_label"/>
<button type="submit" class="btn btn-primary"
style="margin-left: 86%;width: 15%;">
Submit
</button>
</div>
<div style="height:10px"/>
</div>
</form>
</div>
</div>
</t>
</template>
<!--Receive a thankyou message upon submitting the form.-->
<template id="cleaning_online_thanks" name="Cleaning Online Request Form">
<t t-call="website.layout">
<div id="wrap" class="website">
<div style="height:100px"/>
<div class="container" style="border: 6px solid #454a45;
width: 655px;">
<div style="height:30px"/>
<h1 style="padding-left: 194px;">Thank You..!!</h1>
<div style="height:30px"/>
<p style="margin-left: 172px;background-color: #c0d79c;
width: fit-content;
font-family: ui-serif;font-size: 26px;;">
Your record has been saved
</p>
<div style="height:20px"/>
</div>
</div>
</t>
</template>
</odoo>

48
cleaning_management/views/cleaning_shift_views.xml

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Allows users to view the cleaning shift as tree and
provides a form for inputting cleaning shift-->
<record id="cleaning_shift_action" model="ir.actions.act_window">
<field name="name">Cleaning Shift</field>
<field name="res_model">cleaning.shift</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Cleaning Shift
</p>
</field>
</record>
<!--Cleaning shift view form-->
<record id="cleaning_shift_view_form" model="ir.ui.view">
<field name="name">>cleaning.shift.view.form</field>
<field name="model">cleaning.shift</field>
<field name="arch" type="xml">
<form create="False">
<sheet>
<group>
<field name="shift_type"/>
<field name="shift_time_from" widget="float_time"/>
<field name="shift_time_to" widget="float_time"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids" widget="mail_activity"/>
</div>
</form>
</field>
</record>
<!--Cleaning shift tree view-->
<record id="cleaning_shift_view_tree" model="ir.ui.view">
<field name="name">cleaning.shift.view.tree</field>
<field name="model">cleaning.shift</field>
<field name="arch" type="xml">
<tree create="false">
<field name="shift_type"/>
<field name="shift_time_from" widget="float_time"/>
<field name="shift_time_to" widget="float_time"/>
</tree>
</field>
</record>
</odoo>

81
cleaning_management/views/cleaning_team_duty_views.xml

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Allows users to view the cleaning team duty as a tree and
provides a form for inputting cleaning team duty-->
<record id="cleaning_team_duty_action" model="ir.actions.act_window">
<field name="name">Cleaning Team Duty</field>
<field name="res_model">cleaning.team.duty</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Cleaning Team Duty
</p>
</field>
</record>
<!--Cleaning team duty form view-->
<record id="cleaning_team_duty_view_form" model="ir.ui.view">
<field name="name">cleaning.team.duty.view.form</field>
<field name="model">cleaning.team.duty</field>
<field name="arch" type="xml">
<form create="False">
<header>
<button name="action_start" type="object" string="Start"
class="btn-success"
invisible="start_cleaning or state == 'cancelled'"/>
<button name="action_finish" type="object" string="Done"
class="btn-success"
invisible="end_cleaning or state == 'cancelled'"/>
<button name="action_inspection" type="object"
string="Inspection" class="oe_highlight" invisible="inspection_boolean"/>
<field name="state" widget="statusbar" statusbar_visible="draft"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button type="object" name="action_view_inspection"
class="oe_stat_button"
icon="fa-pencil-square-o">
<field name="inspection_count" widget="statinfo"
string="Inspection"/>
</button>
</div>
<group>
<field name="team_id"/>
<field name="team_leader_id"/>
<field name="cleaning_date"/>
<field name="cleaning_time"/>
<field name="members_ids" widget="many2many_tags"/>
<field name="customer_id"/>
<field name="location_state_id"/>
<field name="place"/>
<field name="inspection_boolean" invisible="1"/>
<field name="start_cleaning" invisible="1"/>
<field name="end_cleaning" invisible="1"/>
<field name="start_time"/>
<field name="end_time"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids" widget="mail_activity"/>
</div>
</form>
</field>
</record>
<!--Cleaning team duty tree view-->
<record id="cleaning_team_duty_view_tree" model="ir.ui.view">
<field name="name">cleaning.team.duty.view.tree</field>
<field name="model">cleaning.team.duty</field>
<field name="arch" type="xml">
<tree create="False">
<field name="team_id"/>
<field name="team_leader_id"/>
<field name="members_ids" widget="many2many_tags"/>
<field name="cleaning_date"/>
<field name="state" widget="badge"
decoration-success="state == 'cleaned'"
decoration-danger="state == 'dirty'" optional="show"/>
</tree>
</field>
</record>
</odoo>

52
cleaning_management/views/cleaning_team_views.xml

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Allows users to view the cleaning team as a tree and
provides a form for inputting cleaning team-->
<record id="cleaning_team_action" model="ir.actions.act_window">
<field name="name">Cleaning Team</field>
<field name="res_model">cleaning.team</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Cleaning Team
</p>
</field>
</record>
<!--Cleaning team form view-->
<record id="cleaning_team_view_form" model="ir.ui.view">
<field name="name">cleaning.team.view.form</field>
<field name="model">cleaning.team</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<field name="duty_type"/>
<field name="emp_detail_ids" invisible="1"/>
<field name="team_leader_id" options="{'no_create': True}"/>
<field name="members_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="cleaning_date" invisible="1"/>
<field name="cleaning_duty_ids" widget="many2many_tags" invisible="1"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids" widget="mail_activity"/>
</div>
</form>
</field>
</record>
<!--Cleaning team tree view-->
<record id="cleaning_team_view_tree" model="ir.ui.view">
<field name="name">cleaning.team.view.tree</field>
<field name="model">cleaning.team</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="team_leader_id"/>
<field name="members_ids" widget="many2many_tags"/>
</tree>
</field>
</record>
</odoo>

46
cleaning_management/views/employee_details_views.xml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Allows users to view the employee details as a tree and
provides a form for inputting employee details-->
<record id="employee_details_action" model="ir.actions.act_window">
<field name="name">Employee Details</field>
<field name="res_model">employee.details</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a New Employee Details
</p>
</field>
</record>
<!--Employee details form view-->
<record id="employee_details_view_form" model="ir.ui.view">
<field name="name">employee.details.view.form</field>
<field name="model">employee.details</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="employee_name_id"/>
<field name="time_shift_id" options="{'no_create': True}"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids" widget="mail_activity"/>
</div>
</form>
</field>
</record>
<!--Employee details tree view-->
<record id="employee_details_view_tree" model="ir.ui.view">
<field name="name">employee.details.view.tree</field>
<field name="model">employee.details</field>
<field name="arch" type="xml">
<tree>
<field name="employee_name_id"/>
<field name="time_shift_id"/>
</tree>
</field>
</record>
</odoo>
Loading…
Cancel
Save