diff --git a/salon_management/README.rst b/salon_management/README.rst
new file mode 100644
index 000000000..e69ba3cd0
--- /dev/null
+++ b/salon_management/README.rst
@@ -0,0 +1,39 @@
+Beauty Spa Management v13
+=========================
+This Spa management system developed by Cybrosys Techno Solutions helps
+your customers to do the online booking for using the service. This module
+integrates with other Odoo modules like accounting and website.
+
+Configuration
+=============
+* No additional configurations needed
+
+Company
+-------
+* `Cybrosys Techno Solutions `__
+
+Credits
+-------
+* Developer: AVINASH N K @ cybrosys, Contact: odoo@cybrosys.com
+ Version 13: Vaishnavi B@cybrosys,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 `__
+
+Further information
+===================
+HTML Description: ``__
diff --git a/salon_management/__init__.py b/salon_management/__init__.py
new file mode 100644
index 000000000..b41ccadc2
--- /dev/null
+++ b/salon_management/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Avinash N K (odoo@cybrosys.com)
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from . import models
+from . import controllers
diff --git a/salon_management/__manifest__.py b/salon_management/__manifest__.py
new file mode 100644
index 000000000..caf2e1270
--- /dev/null
+++ b/salon_management/__manifest__.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Avinash N K (odoo@cybrosys.com)
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+{
+ 'name': 'Beauty Spa Management',
+ 'summary': """Beauty Parlour Management with Online Booking System""",
+ 'version': '13.0.1.0.0',
+ 'author': 'Cybrosys Techno Solutions',
+ 'website': "https://www.cybrosys.com",
+ 'company': 'Cybrosys Techno Solutions',
+ "category": "Industries",
+ 'depends': ['base', 'account', 'mail', 'website'],
+ 'data': [
+ 'security/salon_security.xml',
+ 'security/ir.model.access.csv',
+ 'data/data_chair.xml',
+ 'data/data_booking.xml',
+ 'views/salon_holiday.xml',
+ 'views/js_view.xml',
+ 'views/salon_data.xml',
+ 'views/salon_management_chair.xml',
+ 'views/salon_management_services.xml',
+ 'views/salon_order_view.xml',
+ 'views/salon_management_dashboard.xml',
+ 'views/booking_backend.xml',
+ 'views/salon_email_template.xml',
+ 'views/salon_config.xml',
+ 'views/working_hours.xml',
+ 'templates/salon_booking_templates.xml',
+ ],
+ 'images': ['static/description/banner.jpg'],
+ 'license': 'AGPL-3',
+ 'installable': True,
+ 'application': True,
+}
diff --git a/salon_management/controllers/__init__.py b/salon_management/controllers/__init__.py
new file mode 100644
index 000000000..40a235a09
--- /dev/null
+++ b/salon_management/controllers/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies ().
+#
+# Author: AVINASH NK()
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from . import main
diff --git a/salon_management/controllers/main.py b/salon_management/controllers/main.py
new file mode 100644
index 000000000..2f9f72334
--- /dev/null
+++ b/salon_management/controllers/main.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies ().
+#
+# Author: AVINASH NK()
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import json
+from datetime import datetime, date
+from odoo import http
+from odoo.http import request
+
+
+class SalonBookingWeb(http.Controller):
+
+ @http.route('/page/salon_details', csrf=False, type="http", methods=['POST', 'GET'], auth="public", website=True)
+ def salon_details(self, **kwargs):
+ name = kwargs['name']
+ dates = kwargs['date']
+ time = kwargs['time']
+ phone = kwargs['phone']
+ email = kwargs['email']
+ chair = kwargs['chair']
+ j = 0
+ service_list = []
+ while j < (int(kwargs['number'])):
+ item = "list_service["+str(j)+"][i]"
+ service_list.append(int(kwargs[item]))
+ j += 1
+ salon_service_obj = request.env['salon.service'].search([('id', 'in', service_list)])
+ dates_time = dates+" "+time+":00"
+ date_and_time = datetime.strptime(dates_time, '%m/%d/%Y %H:%M:%S')
+ salon_booking = request.env['salon.booking']
+ booking_data = {
+ 'name': name,
+ 'phone': phone,
+ 'time': date_and_time,
+ 'email': email,
+ 'chair_id': chair,
+ 'services': [(6, 0, [x.id for x in salon_service_obj])],
+ }
+ salon_booking.create(booking_data)
+ return json.dumps({'result': True})
+
+ @http.route('/page/salon_check_date', type='json', auth="public", website=True)
+ def salon_check(self, **kwargs):
+ date_check = str(kwargs.get('check_date'))
+ order_obj = request.env['salon.order'].search([('chair_id.active_booking_chairs', '=', True),
+ ('stage_id', 'in', [1, 2, 3]),
+ ('start_date_only', '=', date_check)])
+ order_details = {}
+ for orders in order_obj:
+ data = {
+ 'number': orders.id,
+ 'start_time_only': orders.start_time_only,
+ 'end_time_only': orders.end_time_only
+ }
+ if orders.chair_id.id not in order_details:
+ order_details[orders.chair_id.id] = {'name': orders.chair_id.name, 'orders': [data]}
+ else:
+ order_details[orders.chair_id.id]['orders'].append(data)
+ return order_details
+
+ @http.route('/page/salon_management.salon_booking_thank_you', type='http', auth="public", website=True)
+ def thank_you(self, **post):
+ return request.render('salon_management.salon_booking_thank_you', {})
+
+ @http.route('/page/salon_management/salon_booking_form', type='http', auth="public", website=True)
+ def chair_info(self, **post):
+ salon_service_obj = request.env['salon.service'].search([])
+ salon_working_hours_obj = request.env['salon.working.hours'].search([])
+ salon_holiday_obj = request.env['salon.holiday'].search([('holiday', '=', True)])
+ date_check = date.today()
+ chair_obj = request.env['salon.chair'].search([('active_booking_chairs', '=', True)])
+ order_obj = request.env['salon.order'].search([('chair_id.active_booking_chairs', '=', True),
+ ('stage_id', 'in', [1, 2, 3])])
+ order_obj = order_obj.search([('start_date_only', '=', date_check)])
+ return request.render('salon_management.salon_booking_form',
+ {'chair_details': chair_obj, 'order_details': order_obj,
+ 'salon_services': salon_service_obj, 'date_search': date_check,
+ 'holiday': salon_holiday_obj,
+ 'working_time': salon_working_hours_obj
+ })
diff --git a/salon_management/data/data_booking.xml b/salon_management/data/data_booking.xml
new file mode 100644
index 000000000..9731ebf47
--- /dev/null
+++ b/salon_management/data/data_booking.xml
@@ -0,0 +1,10 @@
+
+
+
+ Bookings
+ /page/salon_management/salon_booking_form
+
+ 80
+
+
+
\ No newline at end of file
diff --git a/salon_management/data/data_chair.xml b/salon_management/data/data_chair.xml
new file mode 100644
index 000000000..9a9c43ea5
--- /dev/null
+++ b/salon_management/data/data_chair.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ Collection Today
+
+
+ model.collection_today_updater()
+
+ 1
+ days
+ -1
+
+
+
+
+
+
\ No newline at end of file
diff --git a/salon_management/doc/RELEASE_NOTES.md b/salon_management/doc/RELEASE_NOTES.md
new file mode 100644
index 000000000..3b78ca9fb
--- /dev/null
+++ b/salon_management/doc/RELEASE_NOTES.md
@@ -0,0 +1,6 @@
+## Module
+
+#### 20.12.2019
+#### Version 13.0.1.0.0
+##### ADD
+- Initial Commit salon_management
diff --git a/salon_management/models/__init__.py b/salon_management/models/__init__.py
new file mode 100644
index 000000000..0c450ae0b
--- /dev/null
+++ b/salon_management/models/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Avinash N k (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 .
+#
+#############################################################################
+from . import salon_management
+from . import salon_booking
+from . import salon_config
+
diff --git a/salon_management/models/salon_booking.py b/salon_management/models/salon_booking.py
new file mode 100644
index 000000000..effba3cdc
--- /dev/null
+++ b/salon_management/models/salon_booking.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies ().
+#
+# Author: AVINASH NK()
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from datetime import date
+from odoo import models, fields, api
+
+
+class SalonBookingBackend(models.Model):
+ _name = 'salon.booking'
+
+ name = fields.Char(string="Name")
+ state = fields.Selection([('draft', 'Draft'), ('approved', 'Approved'), ('rejected', 'Rejected')], default="draft")
+ time = fields.Datetime(string="Date")
+ phone = fields.Char(string="Phone")
+ email = fields.Char(string="E-Mail")
+ services = fields.Many2many('salon.service', string="Services")
+ chair_id = fields.Many2one('salon.chair', string="Chair")
+ company_id = fields.Many2one('res.company', 'Company',
+ default=lambda self: self.env['res.company'].browse(1))
+ lang = fields.Many2one('res.lang', 'Language',
+ default=lambda self: self.env['res.lang'].browse(1))
+
+ def all_salon_orders(self):
+ if self.time:
+ date_only = str(self.time)[0:10]
+ else:
+ date_only = date.today()
+ all_salon_service_obj = self.env['salon.order'].search([('chair_id', '=', self.chair_id.id),
+ ('start_date_only', '=', date_only)])
+ self.filtered_orders = [(6, 0, [x.id for x in all_salon_service_obj])]
+
+ filtered_orders = fields.Many2many('salon.order', string="Salon Orders", compute="all_salon_orders")
+
+ def booking_approve(self):
+ salon_order_obj = self.env['salon.order']
+ salon_service_obj = self.env['salon.order.lines']
+ order_data = {
+ 'customer_name': self.name,
+ 'chair_id': self.chair_id.id,
+ 'start_time': self.time,
+ 'date': date.today(),
+ 'stage_id': 1,
+ 'booking_identifier': True,
+ }
+ order = salon_order_obj.create(order_data)
+ for records in self.services:
+ service_data = {
+ 'service_id': records.id,
+ 'time_taken': records.time_taken,
+ 'price': records.price,
+ 'price_subtotal': records.price,
+ 'salon_order': order.id,
+ }
+ salon_service_obj.create(service_data)
+ template = self.env.ref('salon_management.salon_email_template_approved')
+ self.env['mail.template'].browse(template.id).send_mail(self.id)
+ self.state = "approved"
+
+ def booking_reject(self):
+ template = self.env.ref('salon_management.salon_email_template_rejected')
+ self.env['mail.template'].browse(template.id).send_mail(self.id)
+ self.state = "rejected"
+
diff --git a/salon_management/models/salon_config.py b/salon_management/models/salon_config.py
new file mode 100644
index 000000000..f3390069f
--- /dev/null
+++ b/salon_management/models/salon_config.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies ().
+#
+# Author: AVINASH NK()
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from odoo import models, fields, api
+
+
+class SalonWorkingHours(models.Model):
+ _name = 'salon.working.hours'
+
+ name = fields.Char(string="Name")
+ from_time = fields.Float(string="Starting Time")
+ to_time = fields.Float(string="Closing Time")
+
+
+class SalonHoliday(models.Model):
+ _name = 'salon.holiday'
+
+ name = fields.Char(string="Name")
+ holiday = fields.Boolean(string="Holiday")
+
+
+class ConfigurationSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ @api.model
+ def booking_chairs(self):
+ return self.env['salon.chair'].search([('active_booking_chairs', '=', True)])
+
+ @api.model
+ def holidays(self):
+ return self.env['salon.holiday'].search([('holiday', '=', True)])
+
+ salon_booking_chairs = fields.Many2many('salon.chair', string="Booking Chairs", default=booking_chairs)
+ salon_holidays = fields.Many2many('salon.holiday', string="Holidays", default=holidays)
+
+ def execute(self):
+ salon_chair_obj = self.env['salon.chair'].search([])
+ book_chair = []
+ for chairs in self.salon_booking_chairs:
+ book_chair.append(chairs.id)
+ for records in salon_chair_obj:
+ if records.id in book_chair:
+ records.active_booking_chairs = True
+ else:
+ records.active_booking_chairs = False
+
+ salon_holiday_obj = self.env['salon.holiday'].search([])
+ holiday = []
+ for days in self.salon_holidays:
+ holiday.append(days.id)
+ for records in salon_holiday_obj:
+ if records.id in holiday:
+ records.holiday = True
+ else:
+ records.holiday = False
diff --git a/salon_management/models/salon_management.py b/salon_management/models/salon_management.py
new file mode 100644
index 000000000..cdec30c18
--- /dev/null
+++ b/salon_management/models/salon_management.py
@@ -0,0 +1,368 @@
+# -*- coding: utf-8 -*-
+###################################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies ().
+#
+# Author: AVINASH NK()
+#
+# This program is free software: you can modify
+# it under the terms of the GNU Affero General Public License (AGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# 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 for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from datetime import date, datetime, timedelta
+from odoo import models, fields, api
+from odoo.tools.translate import _
+from odoo.exceptions import UserError, ValidationError
+
+
+class PartnerSalon(models.Model):
+ _inherit = 'res.partner'
+
+ partner_salon = fields.Boolean(string="Is a Salon Partner")
+
+
+class SequenceUpdaterSalon(models.Model):
+ _name = 'salon.sequence.updater'
+
+ sequence_salon = fields.Char(string="Salon Sequence")
+
+
+class UserSalon(models.Model):
+ _inherit = 'res.users'
+
+ user_salon_active = fields.Boolean(string="Active Salon Users")
+
+
+class SalonChair(models.Model):
+ _name = 'salon.chair'
+
+ name = fields.Char(string="Chair", required=True,
+ default=lambda self: self.env['salon.sequence.updater'].browse(1).sequence_salon or "Chair-1")
+ number_of_orders = fields.Integer(string="No.of Orders")
+ collection_today = fields.Float(string="Today's Collection")
+ user_of_chair = fields.Many2one('res.users', string="User", readonly=True,
+ help="You can select the user from the Users Tab."
+ "Last user from the Users Tab will be selected as the Current User.")
+ date = fields.Datetime(string="Date", readonly=True)
+ user_line = fields.One2many('salon.chair.user', 'salon_chair', string="Users")
+ total_time_taken_chair = fields.Float(string="Time Reserved(Hrs)")
+ active_booking_chairs = fields.Boolean(string="Active booking chairs")
+ chair_created_user = fields.Integer(string="Salon Chair Created User",
+ default=lambda self: self._uid)
+
+ @api.model
+ def create(self, values):
+ sequence_code = 'chair.sequence'
+ sequence_number = self.env['ir.sequence'].next_by_code(sequence_code)
+ self.env['salon.sequence.updater'].browse(1).write({'sequence_salon': sequence_number})
+ if 'user_line' in values.keys():
+ if values['user_line']:
+ date_changer = []
+ for elements in values['user_line']:
+ date_changer.append(elements[2]['start_date'])
+ number = 0
+ for elements in values['user_line']:
+ number += 1
+ if len(values['user_line']) == number:
+ break
+ elements[2]['end_date'] = date_changer[number]
+ values['user_of_chair'] = values['user_line'][len((values['user_line'])) - 1][2]['user_id']
+ values['date'] = values['user_line'][len((values['user_line'])) - 1][2]['start_date']
+ return super(SalonChair, self).create(values)
+
+ def write(self, values):
+ if 'user_line' in values.keys():
+ if values['user_line']:
+ date_changer = []
+ for elements in values['user_line']:
+ if elements[1] is False:
+ date_changer.append(elements[2]['start_date'])
+ number = 0
+ num = 0
+ for records in self.user_line:
+ if records.end_date is False:
+ records.end_date = date_changer[0]
+ for elements in values['user_line']:
+ number += 1
+ if elements[2] is not False:
+ num += 1
+ if len(values['user_line']) == number:
+ break
+ elements[2]['end_date'] = date_changer[num]
+ values['user_of_chair'] = values['user_line'][len((values['user_line'])) - 1][2]['user_id']
+ values['date'] = values['user_line'][len((values['user_line'])) - 1][2]['start_date']
+ return super(SalonChair, self).write(values)
+
+ def collection_today_updater(self, val):
+ salon_chair = self.env['salon.chair']
+ for values in self.search([]):
+ chair_obj = salon_chair.browse(values.ids)
+ invoiced_records = chair_obj.env['salon.order'].search([('stage_id', 'in', [3, 4]),
+ ('chair_id', '=', chair_obj.id)])
+ total = 0
+ for rows in invoiced_records:
+ invoiced_date = rows.date
+ invoiced_date = invoiced_date[0:10]
+ if invoiced_date == str(date.today()):
+ total = total + rows.price_subtotal
+ chair_obj.collection_today = total
+
+
+class SalonChairUserLines(models.Model):
+ _name = 'salon.chair.user'
+
+ read_only_checker = fields.Boolean(string="Checker", default=False)
+ user_id = fields.Many2one('res.users', string="User", required=True)
+ start_date = fields.Datetime(string="Start Date", default=datetime.today(), required=True)
+ end_date = fields.Datetime(string="End Date", readonly=True, default=False)
+ salon_chair = fields.Many2one('salon.chair', string="Chair", required=True, ondelete='cascade',
+ index=True, copy=False)
+
+ @api.model
+ def create(self, val):
+ chairs = self.env['salon.chair'].search([])
+ all_active_users = []
+ for records in chairs:
+ if records.user_of_chair:
+ all_active_users.append(records.user_of_chair.id)
+ records.user_of_chair.write({'user_salon_active': True})
+ users = self.env['res.users'].search([('id', 'not in', all_active_users)])
+ for records in users:
+ records.write({'user_salon_active': False})
+ val['read_only_checker'] = True
+ return super(SalonChairUserLines, self).create(val)
+
+
+class SalonOrder(models.Model):
+ _name = 'salon.order'
+
+ @api.depends('order_line.price_subtotal')
+ def sub_total_update(self):
+ for order in self:
+ amount_untaxed = 0.0
+ total_time_taken = 0.0
+ for line in order.order_line:
+ amount_untaxed += line.price_subtotal
+ total_time_taken += line.time_taken
+ self.price_subtotal = amount_untaxed
+ time_takes = total_time_taken
+ hours = int(time_takes)
+ minutes = (time_takes - hours) * 60
+ start_time_store = datetime.strptime(str(self.start_time).split(".")[0], "%Y-%m-%d %H:%M:%S")
+ self.write({'end_time': start_time_store + timedelta(hours=hours, minutes=minutes),
+ 'time_taken_total': total_time_taken})
+ if self.end_time:
+ self.write({'end_time_only': str(self.end_time)[11:16]})
+ if self.start_time:
+ salon_start_time = str(self.start_time)
+ salon_start_time_date = salon_start_time[0:10]
+ self.write({'start_date_only': salon_start_time_date})
+ self.write({'start_time_only': str(self.start_time)[11:16]})
+
+ name = fields.Char(string='Salon', required=True, copy=False, readonly=True,
+ default='Draft Salon Order')
+ start_time = fields.Datetime(string="Start time", default=datetime.now(), required=True)
+ end_time = fields.Datetime(string="End time")
+ date = fields.Datetime(string="Date", required=True, default=datetime.now())
+ color = fields.Integer(string="Colour", default=6)
+ partner_id = fields.Many2one('res.partner', string="Customer", required=False,
+ help="If the customer is a regular customer, "
+ "then you can add the customer in your database")
+ customer_name = fields.Char(string="Name", required=True)
+ amount = fields.Float(string="Amount")
+ chair_id = fields.Many2one('salon.chair', string="Chair", required=True)
+ price_subtotal = fields.Float(string='Total', compute='sub_total_update', readonly=True, store=True)
+ time_taken_total = fields.Float(string="Total time taken")
+ note = fields.Text('Terms and conditions')
+ order_line = fields.One2many('salon.order.lines', 'salon_order', string="Order Lines")
+ stage_id = fields.Many2one('salon.stages', string="Stages", default=1)
+ inv_stage_identifier = fields.Boolean(string="Stage Identifier")
+ invoice_number = fields.Integer(string="Invoice Number")
+ validation_controller = fields.Boolean(string="Validation controller", default=False)
+ start_date_only = fields.Date(string="Date Only")
+ booking_identifier = fields.Boolean(string="Booking Identifier")
+ start_time_only = fields.Char(string="Start Time Only")
+ end_time_only = fields.Char(string="End Time Only")
+ chair_user = fields.Many2one('res.users', string="Chair User")
+ salon_order_created_user = fields.Integer(string="Salon Order Created User",
+ default=lambda self: self._uid)
+
+ @api.onchange('start_time')
+ def start_date_change(self):
+ salon_start_time = str(self.start_time)
+ salon_start_time_date = salon_start_time[0:10]
+ self.write({'start_date_only': salon_start_time_date})
+
+ def action_view_invoice_salon(self):
+ imd = self.env['ir.model.data']
+ action = imd.xmlid_to_object('account.action_move_out_invoice_type')
+ list_view_id = imd.xmlid_to_res_id('account.view_move_tree')
+ form_view_id = imd.xmlid_to_res_id('account.view_move_form ')
+ result = {
+ 'name': action.name,
+ 'help': action.help,
+ 'type': action.type,
+ 'views': [[form_view_id, 'form'], [list_view_id, 'tree'], [False, 'graph'], [False, 'kanban'],
+ [False, 'calendar'], [False, 'pivot']],
+ 'target': action.target,
+ 'context': action.context,
+ 'res_model': action.res_model,
+ 'res_id': self.invoice_number,
+ }
+ return result
+
+ def write(self, values):
+ if 'stage_id' in values.keys():
+ if self.stage_id.id == 3 and values['stage_id'] != 4:
+ raise ValidationError(_("You can't perform that move !"))
+ if self.stage_id.id == 1 and values['stage_id'] not in [2, 5]:
+ raise ValidationError(_("You can't perform that move!"))
+ if self.stage_id.id == 4:
+ raise ValidationError(_("You can't move a salon order from closed stage !"))
+ if self.stage_id.id == 5:
+ raise ValidationError(_("You can't move a salon order from cancel stage !"))
+ if self.stage_id.id == 2 and (values['stage_id'] == 1 or values['stage_id'] == 4):
+ raise ValidationError(_("You can't perform that move !"))
+ if self.stage_id.id == 2 and values['stage_id'] == 3 and self.inv_stage_identifier is False:
+ self.salon_invoice_create()
+ if 'stage_id' in values.keys() and self.name == "Draft Salon Order":
+ if values['stage_id'] == 2:
+ self.salon_confirm()
+ return super(SalonOrder, self).write(values)
+
+ def salon_confirm(self):
+ sequence_code = 'salon.order.sequence'
+ order_date = str(self.date)
+ order_date = order_date[0:10]
+ self.name = self.env['ir.sequence'].with_context(ir_sequence_date=order_date).next_by_code(sequence_code)
+ if self.partner_id:
+ self.partner_id.partner_salon = True
+ self.stage_id = 2
+ self.chair_id.number_of_orders = len(self.env['salon.order'].search([("chair_id", "=", self.chair_id.id),
+ ("stage_id", "in", [2, 3])]))
+ self.chair_id.total_time_taken_chair = (self.chair_id.total_time_taken_chair + self.time_taken_total)
+ self.chair_user = self.chair_id.user_of_chair
+
+ def salon_validate(self):
+ self.validation_controller = True
+
+ def salon_close(self):
+ self.stage_id = 4
+ self.chair_id.number_of_orders = len(self.env['salon.order'].search([("chair_id", "=", self.chair_id.id),
+ ("stage_id", "in", [2, 3])]))
+ self.chair_id.total_time_taken_chair = (self.chair_id.total_time_taken_chair - self.time_taken_total)
+
+ def salon_cancel(self):
+ self.stage_id = 5
+ self.chair_id.number_of_orders = len(self.env['salon.order'].search([("chair_id", "=", self.chair_id.id),
+ ("stage_id", "in", [2, 3])]))
+ if self.stage_id.id != 1:
+ self.chair_id.total_time_taken_chair = (self.chair_id.total_time_taken_chair - self.time_taken_total)
+
+ def button_total_update(self):
+ for order in self:
+ amount_untaxed = 0.0
+ for line in order.order_line:
+ amount_untaxed += line.price_subtotal
+ order.price_subtotal = amount_untaxed
+
+ @api.onchange('chair_id')
+ def onchange_chair(self):
+ if 'active_id' in self._context.keys():
+ self.chair_id = self._context['active_id']
+
+ def salon_invoice_create(self):
+ product_id = self.env['product.product'].search([("name", "=", "Salon Service")])
+ for records in self.order_line:
+ if product_id.property_account_income_id.id:
+ income_account = product_id.property_account_income_id.id
+ elif product_id.categ_id.property_account_income_categ_id.id:
+ income_account = product_id.categ_id.property_account_income_categ_id.id
+ else:
+ raise UserError(_('Please define income account for this product: "%s" (id:%d).') % (product_id.name,
+ product_id.id))
+ inv = self.env['account.move'].create({
+ 'type': 'out_invoice',
+ 'partner_id': self.partner_id.id,
+ 'invoice_user_id': self.env.user.id,
+ 'invoice_origin': self.name,
+ 'invoice_line_ids': [
+ (0, 0, {
+ 'name': self.name,
+ 'account_id': income_account,
+ 'price_unit': self.order_line.price,
+ 'quantity': 1,
+ 'product_id': product_id.id,
+ })],
+ })
+
+ imd = self.env['ir.model.data']
+ action = imd.xmlid_to_object('account.action_move_out_invoice_type')
+ result = {
+ 'name': action.name,
+ 'type': 'ir.actions.act_window',
+ 'views': [[False, 'form']],
+ 'target': 'current',
+ 'res_id':inv.id,
+ 'res_model': 'account.move',
+ }
+ print(action.read([]))
+ return result
+
+ def unlink(self):
+ for order in self:
+ if order.stage_id.id == 3 or order.stage_id.id == 4:
+ raise UserError(_("You can't delete an invoiced salon order!"))
+ return super(SalonOrder, self).unlink()
+
+
+class SalonServices(models.Model):
+ _name = 'salon.service'
+
+ name = fields.Char(string="Name")
+ price = fields.Float(string="Price")
+ time_taken = fields.Float(string="Time Taken", help="Approximate time taken for this service in Hours")
+
+
+class SalonOrderLine(models.Model):
+ _name = 'salon.order.lines'
+
+ service_id = fields.Many2one('salon.service', string="Service")
+ price = fields.Float(string="Price")
+ salon_order = fields.Many2one('salon.order', string="Salon Order", required=True, ondelete='cascade',
+ index=True, copy=False)
+ price_subtotal = fields.Float(string='Subtotal')
+ time_taken = fields.Float(string='Time Taken')
+
+ @api.onchange('service_id')
+ def onchange_service(self):
+ self.price = self.service_id.price
+ self.price_subtotal = self.service_id.price
+ self.time_taken = self.service_id.time_taken
+ print(self.time_taken, "self.time_taken")
+
+ @api.onchange('price')
+ def onchange_price(self):
+ self.price_subtotal = self.price
+
+ @api.onchange('price_subtotal')
+ def onchange_subtotal(self):
+ self.price = self.price_subtotal
+
+
+class SalonStages(models.Model):
+ _name = 'salon.stages'
+
+ name = fields.Char(string="Name", required=True, translate=True)
diff --git a/salon_management/security/ir.model.access.csv b/salon_management/security/ir.model.access.csv
new file mode 100644
index 000000000..c549b8da8
--- /dev/null
+++ b/salon_management/security/ir.model.access.csv
@@ -0,0 +1,32 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+view_salon_order,view.salon.order,model_salon_order,salon_management.group_salon_user,1,1,1,0
+view_salon_order_lines,view.salon.order.lines,model_salon_order_lines,salon_management.group_salon_user,1,1,1,0
+view_salon_stages,view.salon.stages,model_salon_stages,salon_management.group_salon_user,1,1,1,0
+view_salon_chair,view.salon.chair,model_salon_chair,salon_management.group_salon_user,1,1,0,0
+view_salon_service,view.salon.service,model_salon_service,salon_management.group_salon_user,1,1,0,0
+
+view_salon_order2,view.salon.order3,model_salon_order,salon_management.group_salon_manager,1,1,1,1
+view_salon_order_lines2,view.salon.order3.lines,model_salon_order_lines,salon_management.group_salon_manager,1,1,1,1
+view_salon_stages2,view.salon.stages3,model_salon_stages,salon_management.group_salon_manager,1,1,1,1
+view_salon_chair2,view.salon.chair3,model_salon_chair,salon_management.group_salon_manager,1,1,1,1
+view_salon_service2,view.salon.service3,model_salon_service,salon_management.group_salon_manager,1,1,1,1
+view_salon_booking2,view.salon.booking3,model_salon_booking,salon_management.group_salon_manager,1,1,1,1
+view_salon_working_hours2,view.salon.working.hours3,model_salon_working_hours,salon_management.group_salon_manager,1,1,1,1
+view_salon_holiday2,view.salon.holiday3,model_salon_holiday,salon_management.group_salon_manager,1,1,1,1
+view_res_partner2,view.res.partner3,model_res_partner,salon_management.group_salon_manager,1,1,1,1
+view_res_users2,view.res.users3,model_res_users,salon_management.group_salon_manager,1,1,1,1
+view_salon_chair_user2,view.salon.chair.user3,model_salon_chair_user,salon_management.group_salon_manager,1,1,1,1
+view_salon_sequence_updater2,view.salon.sequence.updater3,model_salon_sequence_updater,salon_management.group_salon_manager,1,1,1,1
+
+view_salon_order3,view.salon.order3,model_salon_order,base.group_public,1,0,0,0
+view_salon_order_lines3,view.salon.order3.lines,model_salon_order_lines,base.group_public,1,0,0,0
+view_salon_stages3,view.salon.stages3,model_salon_stages,base.group_public,1,0,0,0
+view_salon_chair3,view.salon.chair3,model_salon_chair,base.group_public,1,0,0,0
+view_salon_service3,view.salon.service3,model_salon_service,base.group_public,1,0,0,0
+view_salon_booking3,view.salon.booking3,model_salon_booking,base.group_public,1,1,1,0
+view_salon_working_hours3,view.salon.working.hours3,model_salon_working_hours,base.group_public,1,0,0,0
+view_salon_holiday3,view.salon.holiday3,model_salon_holiday,base.group_public,1,0,0,0
+view_res_partner3,view.res.partner3,model_res_partner,base.group_public,1,0,0,0
+view_res_users3,view.res.users3,model_res_users,base.group_public,1,0,0,0
+view_salon_chair_user3,view.salon.chair.user3,model_salon_chair_user,base.group_public,1,0,0,0
+view_salon_sequence_updater3,view.salon.sequence.updater3,model_salon_sequence_updater,base.group_public,1,0,0,0
diff --git a/salon_management/security/salon_security.xml b/salon_management/security/salon_security.xml
new file mode 100644
index 000000000..15301ccb3
--- /dev/null
+++ b/salon_management/security/salon_security.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Salon Management
+ Helps you handle your salon needs
+ 5
+
+
+
+ Chair User
+
+
+
+
+
+ Salon Manager
+
+
+
+
+
+
+
diff --git a/salon_management/static/description/1.png b/salon_management/static/description/1.png
new file mode 100644
index 000000000..858734349
Binary files /dev/null and b/salon_management/static/description/1.png differ
diff --git a/salon_management/static/description/10.png b/salon_management/static/description/10.png
new file mode 100644
index 000000000..0ba61e6e0
Binary files /dev/null and b/salon_management/static/description/10.png differ
diff --git a/salon_management/static/description/2.png b/salon_management/static/description/2.png
new file mode 100644
index 000000000..578302093
Binary files /dev/null and b/salon_management/static/description/2.png differ
diff --git a/salon_management/static/description/3.png b/salon_management/static/description/3.png
new file mode 100644
index 000000000..8c1c30931
Binary files /dev/null and b/salon_management/static/description/3.png differ
diff --git a/salon_management/static/description/4.png b/salon_management/static/description/4.png
new file mode 100644
index 000000000..a2a0be8e8
Binary files /dev/null and b/salon_management/static/description/4.png differ
diff --git a/salon_management/static/description/5.png b/salon_management/static/description/5.png
new file mode 100644
index 000000000..85b3d57b7
Binary files /dev/null and b/salon_management/static/description/5.png differ
diff --git a/salon_management/static/description/6.png b/salon_management/static/description/6.png
new file mode 100644
index 000000000..f2b6de0ea
Binary files /dev/null and b/salon_management/static/description/6.png differ
diff --git a/salon_management/static/description/7.png b/salon_management/static/description/7.png
new file mode 100644
index 000000000..dd4e70bc3
Binary files /dev/null and b/salon_management/static/description/7.png differ
diff --git a/salon_management/static/description/8.png b/salon_management/static/description/8.png
new file mode 100644
index 000000000..0c0469a32
Binary files /dev/null and b/salon_management/static/description/8.png differ
diff --git a/salon_management/static/description/9.png b/salon_management/static/description/9.png
new file mode 100644
index 000000000..05369c69d
Binary files /dev/null and b/salon_management/static/description/9.png differ
diff --git a/salon_management/static/description/banner.jpg b/salon_management/static/description/banner.jpg
new file mode 100644
index 000000000..9cdd77b3b
Binary files /dev/null and b/salon_management/static/description/banner.jpg differ
diff --git a/salon_management/static/description/icon.png b/salon_management/static/description/icon.png
new file mode 100644
index 000000000..bdfa70f4f
Binary files /dev/null and b/salon_management/static/description/icon.png differ
diff --git a/salon_management/static/description/index.html b/salon_management/static/description/index.html
new file mode 100644
index 000000000..8e70c79f2
--- /dev/null
+++ b/salon_management/static/description/index.html
@@ -0,0 +1,443 @@
+
+
+
+ Cybrosys Techno Solution’s Spa Management System envisions in giving a complete
+ new experience to client in availing their spa services.
+ The Spa Management System helps the customer in processing their online booking
+ for spa services. The module comes integrated with Odoo Accounting and Website, offering
+ comprehensive plethora of functions.
+
+
+ One of the significant advantage of using the Spa Management System is that, it offers your
+ customer their booking information via email. The client is equipped with the status of
+ their booking, enhancing the customary experience and brand building of your business.
+
+
+
+
+
+
+
+
+
+ Features
+
+
+
+ Online booking facility.
+
+
+
+ Accounting facility.
+
+
+ Customer notification via email.
+
+
+
+ User interactive dashboard.
+
+
+ Customer can view the available chairs and order details.
+
+
+ Different access levels for users and administrator.
+
+
+ Track the chair user by date.
+
+
+
+
+
+
+
+ Dashboard
+
+
+ Dashboard view is shown in the below image.
+ Your customer can book the service in two ways, either via direct booking or online booking.
+ The chairs with Black font's denote the ones available for online booking and the White ones
+ for direct booking. From dashboard, one get all information about the chair, whether the
+ particular chair is free or is in currently use. Also one can see the
+ active orders of that chair. If the end user clicks in , it will directly navigate to the
+ active orders of that chair. In case, if the end user want to change the
+ settings of chair, they can just click the settings button in the dashboard.
+
+
+
+
+ Chair
+
+
+ Go to Salon --> Chair
+
+ Here you can create and edit details of each chair. You can assign a user to the chair by adding new user to the users tab in the form view of chair(Check the image shown below). The last added user will be turned to the current user of the chair. Using this tab, you can track which user is active on the Chair on a particular date or time.
+
+
+
+
+
+ Services
+
+
Go to Salon --> Services
+
+ List of services provided by the Spa. You can create and edit services here.
+
+
+
+
+
+ Salon Orders
+
+
Go to Salon --> Salon Orders
+ Here you have all the orders in the Spa.
+
+
+
+
+ Salon Order Form View
+
+
Go to Salon --> Salon Orders --> Form View
+ Form view of the salon order is shown in the image. You will get the corresponding invoice details of the order by clicking the Invoice button in the upper right side of the form view.
+
+
+
+
+ Booking
+
+
Go to Salon --> Bookings
+ Here you can see all the online bookings. You can either approve a booking or reject a booking. In both cases the notification is send to the customer mail address. The approved bookings will be changed to salon orders.
+
+
+
+
+ Booking Form View
+
+
Go to Salon --> Bookings --> Form View
+ Here you can see all existing salon orders on the corresponding date on that chair. After checking the time availability, you can decide to approve/reject the booking.
+
+
+
+
+ Settings
+
+
Go to Salon --> Configuration --> Settings
+ Here you can assign the chairs for booking purpose. Only the assigned chairs can be booked by a customer through online. Also you can select the holidays here
+
+
+
+
+
+
+
+ salon_order_form.form
+ salon.order
+
+
+
+
+
+ Salon
+
+ salon.order
+ kanban,tree,form
+
+
+ Salon Orders
+ salon.order
+ kanban,tree,form
+ [('chair_id', '=', active_id)]
+
+
+
+
\ No newline at end of file
diff --git a/salon_management/views/working_hours.xml b/salon_management/views/working_hours.xml
new file mode 100644
index 000000000..3441b40cc
--- /dev/null
+++ b/salon_management/views/working_hours.xml
@@ -0,0 +1,67 @@
+
+
+
+
+ salon_working_form.form
+ salon.working.hours
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ salon_working_tree.tree
+ salon.working.hours
+
+
+
+
+
+
+
+
+
+
+ Working Hours
+ salon.working.hours
+ tree,form
+
+
+
+
+ Sunday
+
+
+ Monday
+
+
+ Tuesday
+
+
+ Wednesday
+
+
+ Thursday
+
+
+ Friday
+
+
+ Saturday
+
+
+
\ No newline at end of file
diff --git a/timesheets_by_employee/README.rst b/timesheets_by_employee/README.rst
new file mode 100644
index 000000000..2942371b5
--- /dev/null
+++ b/timesheets_by_employee/README.rst
@@ -0,0 +1,43 @@
+.. image:: https://img.shields.io/badge/licence-AGPL--1-blue.svg
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+
+Timesheets by Employees v13
+===========================
+
+This module allows to print the timesheets of selected employee.
+
+Configuration
+=============
+* No additional configurations needed
+
+Company
+-------
+* `Cybrosys Techno Solutions `__
+
+Credits
+-------
+* Developer: Kavya Raveendran @ cybrosys, Contact: odoo@cybrosys.com
+ Version 13: Vaishnavi B@cybrosys,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 `__
+
+Further information
+===================
+HTML Description: ``__
+
diff --git a/timesheets_by_employee/__init__.py b/timesheets_by_employee/__init__.py
new file mode 100644
index 000000000..cbb15e031
--- /dev/null
+++ b/timesheets_by_employee/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Kavya Raveendran (odoo@cybrosys.com)
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+from . import report
+from . import wizard
diff --git a/timesheets_by_employee/__manifest__.py b/timesheets_by_employee/__manifest__.py
new file mode 100644
index 000000000..2e6acf5d3
--- /dev/null
+++ b/timesheets_by_employee/__manifest__.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Kavya Raveendran (odoo@cybrosys.com)
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+{
+ 'name': 'Timesheet PDF Report',
+ 'version': '13.0.1.0.0',
+ "category": "Generic Modules/Human Resources",
+ 'sequence': 25,
+ 'summary': 'Timesheet PDF Report of Employees',
+ 'description': 'Timesheet PDF Report of Employees',
+ 'author': 'Cybrosys Techno Solutions',
+ 'company': 'Cybrosys Techno Solutions',
+ 'maintainer': 'Cybrosys Techno Solutions',
+ 'website': 'https://www.cybrosys.com',
+ 'depends': ['hr_timesheet'],
+ 'data': [
+ 'report/report_timesheets.xml',
+ 'report/timesheet_pdf.xml',
+ 'wizard/timesheet_wizard.xml',
+ ],
+ 'images': ['static/description/banner.png'],
+ 'license': 'LGPL-3',
+ 'installable': True,
+ 'auto_install': False,
+ 'application': False,
+}
diff --git a/timesheets_by_employee/doc/RELEASE_NOTES.md b/timesheets_by_employee/doc/RELEASE_NOTES.md
new file mode 100644
index 000000000..a8c3c5c53
--- /dev/null
+++ b/timesheets_by_employee/doc/RELEASE_NOTES.md
@@ -0,0 +1,6 @@
+## Module
+
+#### 25.11.2019
+#### Version 13.0.1.0.0
+##### ADD
+Initial commit for timesheet_by_employee
diff --git a/timesheets_by_employee/report/__init__.py b/timesheets_by_employee/report/__init__.py
new file mode 100644
index 000000000..3def8d3bf
--- /dev/null
+++ b/timesheets_by_employee/report/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Kavya Raveendran (odoo@cybrosys.com)
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+from . import report_timesheets
diff --git a/timesheets_by_employee/report/report_timesheets.py b/timesheets_by_employee/report/report_timesheets.py
new file mode 100644
index 000000000..6c94d83c6
--- /dev/null
+++ b/timesheets_by_employee/report/report_timesheets.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Kavya Raveendran (odoo@cybrosys.com)
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+from odoo import models, fields, api
+
+
+class ReportTimesheet(models.AbstractModel):
+ _name = 'report.timesheets_by_employee.report_timesheets'
+
+ def get_timesheets(self, docs):
+ """input : name of employee and the starting date and ending date
+ output: timesheets by that particular employee within that period and the total duration"""
+
+ if docs.from_date and docs.to_date:
+ rec = self.env['account.analytic.line'].search([('user_id', '=', docs.employee[0].id),
+ ('date', '>=', docs.from_date),
+ ('date', '<=', docs.to_date)])
+ elif docs.from_date:
+ rec = self.env['account.analytic.line'].search([('user_id', '=', docs.employee[0].id),
+ ('date', '>=', docs.from_date)])
+ elif docs.to_date:
+ rec = self.env['account.analytic.line'].search([('user_id', '=', docs.employee[0].id),
+ ('date', '<=', docs.to_date)])
+ else:
+ rec = self.env['account.analytic.line'].search([('user_id', '=', docs.employee[0].id)])
+ records = []
+ total = 0
+ for r in rec:
+ vals = {'project': r.project_id.name,
+ 'user': r.user_id.partner_id.name,
+ 'duration': r.unit_amount,
+ 'date': r.date,
+ }
+ total += r.unit_amount
+ records.append(vals)
+ return [records, total]
+
+ @api.model
+ def _get_report_values(self, docids, data=None):
+ """we are overwriting this function because we need to show values from other models in the report
+ we pass the objects in the docargs dictionary"""
+ docs = self.env['timesheet.wizard'].browse(self.env.context.get('active_id'))
+ identification = []
+ for i in self.env['hr.employee'].search([('user_id', '=', docs.employee[0].id)]):
+ if i:
+ identification.append({'id': i.id, 'name': i.name})
+ timesheets = self.get_timesheets(docs)
+ company_name = self.env['res.company'].search([('name', '=', docs.employee[0].company_id.name)])
+ period = None
+ if docs.from_date and docs.to_date:
+ period = "From " + str(docs.from_date) + " To " + str(docs.to_date)
+ elif docs.from_date:
+ period = "From " + str(docs.from_date)
+ elif docs.from_date:
+ period = " To " + str(docs.to_date)
+ if len(identification) > 1:
+ return {
+ 'doc_ids': self.ids,
+ # 'doc_model': self.model,
+ 'docs': docs,
+ 'timesheets': timesheets[0],
+ 'total': timesheets[1],
+ 'company': company_name,
+ 'identification': identification,
+ 'period': period,
+ }
+ else:
+ return {
+ 'doc_ids': self.ids,
+ # 'doc_model': self.model,
+ 'docs': docs,
+ 'timesheets': timesheets[0],
+ 'total': timesheets[1],
+ 'identification': identification,
+ 'company': company_name,
+ 'period': period,
+ }
diff --git a/timesheets_by_employee/report/report_timesheets.xml b/timesheets_by_employee/report/report_timesheets.xml
new file mode 100644
index 000000000..69bd1e989
--- /dev/null
+++ b/timesheets_by_employee/report/report_timesheets.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
TimeSheet Report by Employees
+
+
+
Employee Name
+
+
+
Company Name
+
+
+
Timesheet Period
+
+
Total Working Hours
+
+
+
+
+
+
Date
+
Project
+
Worked Time
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/timesheets_by_employee/report/timesheet_pdf.xml b/timesheets_by_employee/report/timesheet_pdf.xml
new file mode 100644
index 000000000..9474b86c3
--- /dev/null
+++ b/timesheets_by_employee/report/timesheet_pdf.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/timesheets_by_employee/static/description/banner.png b/timesheets_by_employee/static/description/banner.png
new file mode 100644
index 000000000..c749b5f01
Binary files /dev/null and b/timesheets_by_employee/static/description/banner.png differ
diff --git a/timesheets_by_employee/static/description/cybro_logo.png b/timesheets_by_employee/static/description/cybro_logo.png
new file mode 100644
index 000000000..bb309114c
Binary files /dev/null and b/timesheets_by_employee/static/description/cybro_logo.png differ
diff --git a/timesheets_by_employee/static/description/icon.png b/timesheets_by_employee/static/description/icon.png
new file mode 100644
index 000000000..b4d838a0b
Binary files /dev/null and b/timesheets_by_employee/static/description/icon.png differ
diff --git a/timesheets_by_employee/static/description/icosadan.png b/timesheets_by_employee/static/description/icosadan.png
new file mode 100644
index 000000000..ee52f55b3
Binary files /dev/null and b/timesheets_by_employee/static/description/icosadan.png differ
diff --git a/timesheets_by_employee/static/description/index.html b/timesheets_by_employee/static/description/index.html
new file mode 100644
index 000000000..393594f65
--- /dev/null
+++ b/timesheets_by_employee/static/description/index.html
@@ -0,0 +1,346 @@
+
+
+ This module crafted by Cybrosys Technologies, allows the end user to print the timesheets of selected employee. The module will further group all timesheet lines of selected employee in wizard based on its period.
+
+
+ Configuration
+
+
+ No additional configuration required.
+
+
+
+
+
+
+
+ Features
+
+
+
+ Print the timesheets of selected employees.
+
+
+
+ Perform period wise grouping of timesheet lines.
+
+
+
+
+
+
+
+ Screenshots
+
+
+
+ Timesheets -> Reporting -> Print Timesheets
+
+
+
+
+
+
+ Generate Timesheet Report Wizard with Dates.
+
+
diff --git a/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-1.png b/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-1.png
new file mode 100644
index 000000000..341895446
Binary files /dev/null and b/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-1.png differ
diff --git a/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-2.png b/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-2.png
new file mode 100644
index 000000000..34fa94122
Binary files /dev/null and b/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-2.png differ
diff --git a/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-3.png b/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-3.png
new file mode 100644
index 000000000..c1789a893
Binary files /dev/null and b/timesheets_by_employee/static/description/timesheet-by-employee-cybrosys-3.png differ
diff --git a/timesheets_by_employee/wizard/__init__.py b/timesheets_by_employee/wizard/__init__.py
new file mode 100644
index 000000000..6c2d54310
--- /dev/null
+++ b/timesheets_by_employee/wizard/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Kavya Raveendran (odoo@cybrosys.com)
+# you can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# It is forbidden to publish, distribute, sublicense, or sell copies
+# of the Software or modified copies of the Software.
+#
+# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+from . import timesheet_employee
diff --git a/timesheets_by_employee/wizard/timesheet_employee.py b/timesheets_by_employee/wizard/timesheet_employee.py
new file mode 100644
index 000000000..b581b2a54
--- /dev/null
+++ b/timesheets_by_employee/wizard/timesheet_employee.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Kavya Raveendran (odoo@cybrosys.com)
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+from odoo import models, fields
+
+
+class EmployeeTimesheet(models.TransientModel):
+ _name = 'timesheet.wizard'
+
+ employee = fields.Many2one('res.users', string="Employee", required=True)
+ from_date = fields.Date(string="Starting Date")
+ to_date = fields.Date(string="Ending Date")
+
+ def print_timesheet(self):
+ """Redirects to the report with the values obtained from the wizard
+ 'data['form']': name of employee and the date duration"""
+ data = {
+ 'start_date': self.from_date,
+ 'end_date': self.to_date,
+ 'employee': self.employee.id
+ }
+ return self.env.ref('timesheets_by_employee.action_report_print_timesheets').report_action(self, data=data)
+
diff --git a/timesheets_by_employee/wizard/timesheet_wizard.xml b/timesheets_by_employee/wizard/timesheet_wizard.xml
new file mode 100644
index 000000000..e0d7d77ad
--- /dev/null
+++ b/timesheets_by_employee/wizard/timesheet_wizard.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ timesheet.wizard
+ timesheet.wizard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ timesheet.wizard
+ timesheet.wizard
+ ir.actions.act_window
+ form
+
+ new
+
+
+
+
+
+
+
diff --git a/website_sale_advanced_search/README.rst b/website_sale_advanced_search/README.rst
new file mode 100644
index 000000000..801d3bcd5
--- /dev/null
+++ b/website_sale_advanced_search/README.rst
@@ -0,0 +1,48 @@
+E-commerce Advanced Search v13
+==============================
+
+* E-commerce product search
+* Can Select Category
+* New drop down with product website categories
+* Find all products related to searching content according to category selected
+* Display results as drop down
+* Selection of results redirects to product description page
+
+Depends
+=======
+[website] addon Odoo
+[website_sale] addon Odoo
+
+Tech
+====
+* [jQuery] - Search AutoComplete
+* [Python] - Controllers
+* [XML] - Odoo website templates
+
+Installation
+============
+- www.odoo.com/documentation/13.0/setup/install.html
+- Install our custom addon
+
+
+Bug Tracker
+===========
+Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
+
+Credits
+=======
+* Cybrosys Techno Solutions
+
+Author
+------
+
+Developer: Hilar AK @ cybrosys, odoo@cybrosys.com
+Developer: v11 & v12 Milind @ cybrosys
+DEveloper: V13 Vaishnavi
+
+Maintainer
+----------
+
+This module is maintained by Cybrosys Technologies.
+
+For support and more information, please visit https://www.cybrosys.com.
diff --git a/website_sale_advanced_search/__init__.py b/website_sale_advanced_search/__init__.py
new file mode 100644
index 000000000..d22dd6d62
--- /dev/null
+++ b/website_sale_advanced_search/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Hilar AK()
+# you can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+from . import controllers
+
diff --git a/website_sale_advanced_search/__manifest__.py b/website_sale_advanced_search/__manifest__.py
new file mode 100644
index 000000000..c1ed0ce97
--- /dev/null
+++ b/website_sale_advanced_search/__manifest__.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+# Copyright (C) 2019-TODAY Cybrosys Technologies().
+# Author: Hilar AK()
+# you can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
+# If not, see .
+#
+##############################################################################
+
+{
+ 'name': "Advanced Search in E-commerce ",
+ 'version': '13.0.1.0.0',
+ 'summary': """E-commerce Advanced Search.""",
+ 'description': """
+ Odoo e-commerce advanced search. Autocomplete search product with category and display name
+ """,
+ 'author': 'Cybrosys Techno Solutions',
+ 'company': 'Cybrosys Techno Solutions',
+ 'website': "https://www.cybrosys.com",
+ 'category': 'eCommerce',
+ 'depends': ['base',
+ 'website',
+ 'website_sale',
+ ],
+ 'data': [
+ 'views/assets.xml',
+ 'views/template.xml'
+ ],
+ 'demo': [],
+ 'images': ['static/description/banner.jpg'],
+ 'license': 'LGPL-3',
+ 'installable': True,
+ 'auto_install': False,
+}
diff --git a/website_sale_advanced_search/controllers/__init__.py b/website_sale_advanced_search/controllers/__init__.py
new file mode 100644
index 000000000..65a8c1201
--- /dev/null
+++ b/website_sale_advanced_search/controllers/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import main
diff --git a/website_sale_advanced_search/controllers/main.py b/website_sale_advanced_search/controllers/main.py
new file mode 100644
index 000000000..28378489d
--- /dev/null
+++ b/website_sale_advanced_search/controllers/main.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+import json
+from odoo import http
+from odoo.http import request
+
+
+class WebsiteSearch(http.Controller):
+
+ @http.route('/shop/search', csrf=False, type="http", methods=['POST', 'GET'], auth="public", website=True)
+ def search_contents(self, **kw):
+ """
+ Searches products according to the category selected on front,
+ :param kw: dict contains the category and search key
+ :return: Dict with params as name, res_id, value
+ """
+ strings = '%' + kw.get('name') + '%'
+ category = int(kw.get('category')) if not kw.get('category') == 'all' else ''
+ try:
+ domain = [('public_categ_ids', 'child_of', [category])] if category else []
+ domain.append(('website_published','=', True))
+ product_as_category = request.env['product.template'].search(domain)
+ sql = """select id as res_id, name as name, name as value from product_template where name ILIKE %s"""
+ extra_query = ''
+ if product_as_category:
+ extra_query = " and id in %s"
+ limit = " limit 15"
+ request.cr.execute(sql+extra_query+limit,\
+ (tuple([strings]), tuple(product_as_category and product_as_category.ids)))
+ name = request.cr.dictfetchall()
+ except:
+ name = {'name': 'None', 'value': 'None'}
+ return json.dumps(name)
diff --git a/website_sale_advanced_search/doc/RELEASE_NOTES.md b/website_sale_advanced_search/doc/RELEASE_NOTES.md
new file mode 100644
index 000000000..82f6fbfe6
--- /dev/null
+++ b/website_sale_advanced_search/doc/RELEASE_NOTES.md
@@ -0,0 +1,9 @@
+## Module
+
+#### 20.12.2019
+#### Version 13.0.1.0.0
+##### ADD
+
+- Initial Commit for E-commerce Advanced Search
+
+
diff --git a/website_sale_advanced_search/static/description/advanced-search-in-ecommerce1.png b/website_sale_advanced_search/static/description/advanced-search-in-ecommerce1.png
new file mode 100644
index 000000000..a5ead50ba
Binary files /dev/null and b/website_sale_advanced_search/static/description/advanced-search-in-ecommerce1.png differ
diff --git a/website_sale_advanced_search/static/description/advanced-search-in-ecommerce2.png b/website_sale_advanced_search/static/description/advanced-search-in-ecommerce2.png
new file mode 100644
index 000000000..7a4e50999
Binary files /dev/null and b/website_sale_advanced_search/static/description/advanced-search-in-ecommerce2.png differ
diff --git a/website_sale_advanced_search/static/description/advanced-search-in-ecommerce3.png b/website_sale_advanced_search/static/description/advanced-search-in-ecommerce3.png
new file mode 100644
index 000000000..6c13e25d3
Binary files /dev/null and b/website_sale_advanced_search/static/description/advanced-search-in-ecommerce3.png differ
diff --git a/website_sale_advanced_search/static/description/banner.jpg b/website_sale_advanced_search/static/description/banner.jpg
new file mode 100644
index 000000000..ec8264c0b
Binary files /dev/null and b/website_sale_advanced_search/static/description/banner.jpg differ
diff --git a/website_sale_advanced_search/static/description/cybro_logo.png b/website_sale_advanced_search/static/description/cybro_logo.png
new file mode 100644
index 000000000..bb309114c
Binary files /dev/null and b/website_sale_advanced_search/static/description/cybro_logo.png differ
diff --git a/website_sale_advanced_search/static/description/icon.png b/website_sale_advanced_search/static/description/icon.png
new file mode 100644
index 000000000..ec2597cd4
Binary files /dev/null and b/website_sale_advanced_search/static/description/icon.png differ
diff --git a/website_sale_advanced_search/static/description/index.html b/website_sale_advanced_search/static/description/index.html
new file mode 100644
index 000000000..c660cc466
--- /dev/null
+++ b/website_sale_advanced_search/static/description/index.html
@@ -0,0 +1,365 @@
+
+
+
+ Advanced Search in ECommerce
+
+
+ Gives search results as drop down while typing on e-commerce search bar.
+