diff --git a/salon_management/README.rst b/salon_management/README.rst new file mode 100644 index 000000000..b38004b8f --- /dev/null +++ b/salon_management/README.rst @@ -0,0 +1,40 @@ +Beauty Spa Management v14 +========================= +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 + Version 14: JIBIN JAMES, 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..3a5a44dfc --- /dev/null +++ b/salon_management/__manifest__.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2020-TODAY Cybrosys Technologies(). +# +# 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': '14.0.1.0.0', + 'author': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'company': 'Cybrosys Techno Solutions', + "category": "Industries", + 'depends': ['base','base_setup', '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.png'], + '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..83f1fe36a --- /dev/null +++ b/salon_management/controllers/main.py @@ -0,0 +1,101 @@ +# -*- 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', '=', datetime.strptime(date_check, '%m/%d/%Y').strftime('%Y-%m-%d'))]) + 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..c3a0dceb3 --- /dev/null +++ b/salon_management/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 20.10.2020 +#### Version 14.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..0a4527ab3 --- /dev/null +++ b/salon_management/models/salon_management.py @@ -0,0 +1,411 @@ +# -*- 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 str(elements[1]).startswith('v'): + date_changer.append(elements[2]['start_date']) + number = 0 + num = 0 + for records in self.user_line: + if records.end_date is False: + if date_changer: + 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): + 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 = str(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, group_expand='_read_group_stage_ids') + 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}) + + count = fields.Integer(string='Delivery Orders', + compute='_compute_invoice_saloon_ids') + + def _compute_invoice_saloon_ids(self): + """getting saloon invoice count""" + for orders in self: + orders.count = self.env['account.move']. \ + search_count([('invoice_origin', '=', self.name)]) + + + # if orders.count != 0: + # orders.inv_stage_identifier = True + # orders.stage_id = 3 + + def action_view_invoice_salon(self): + + return { + 'name': 'Invoices', + 'domain': [('invoice_origin', '=', self.name)], + 'res_model': 'account.move', + 'view_id': False, + 'view_mode': 'tree,form', + 'type': 'ir.actions.act_window', + } + + @api.model + def _read_group_stage_ids(self, stages, domain, order): + stage_ids = self.env['salon.stages'].search([]) + return stage_ids + + 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): + if self.partner_id: + supplier = self.partner_id + else: + supplier = self.partner_id.search([("name", "=", "Salon Default Customer")]) + lines = [] + 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)) + + value = (0, 0, { + 'name': records.service_id.name, + 'account_id': income_account, + 'price_unit': records.price, + 'quantity': 1, + 'product_id': product_id.id, + }) + lines.append(value) + + invoice_line = { + 'move_type': 'out_invoice', + 'partner_id': supplier.id, + 'l10n_in_gst_treatment': 'consumer', + 'invoice_user_id': self.env.user.id, + 'invoice_origin': self.name, + 'invoice_line_ids': lines, + } + inv = self.env['account.move'].create(invoice_line) + + + 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', + } + + self.inv_stage_identifier = True + self.stage_id = 3 + invoiced_records = self.env['salon.order'].search([('stage_id', 'in', [3, 4]), + ('chair_id', '=', self.chair_id.id)]) + total = 0 + for rows in invoiced_records: + invoiced_date = str(rows.date) + invoiced_date = invoiced_date[0:10] + if invoiced_date == str(date.today()): + total = total + rows.price_subtotal + self.chair_id.collection_today = total + self.chair_id.number_of_orders = len(self.env['salon.order'].search([("chair_id", "=", self.chair_id.id), + ("stage_id", "in", [2, 3])])) + + 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 + + @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' + _order = "sequence" + + name = fields.Char(string="Name", required=True, translate=True) + sequence = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.") 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.png b/salon_management/static/description/banner.png new file mode 100644 index 000000000..a78b5cca0 Binary files /dev/null and b/salon_management/static/description/banner.png differ diff --git a/salon_management/static/description/icon.png b/salon_management/static/description/icon.png new file mode 100644 index 000000000..d8b55f54d 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 @@ + +
+
+

+ Beauty Spa Management +

+

+ Easy to manage Beauty Spa +

+
+ Cybrosys Technologies +
+ +
+ cybrosys technologies
+
+
+ +
+
+
+

+ Overview +

+

+
+

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

+ +

+

+ Online Booking +

+

Go to Website --> Bookings

+
+ +
+

+ Check the availability of Chair +

+

Go to Website --> Bookings

+
+ +
+

+

+
+
+ +
+
+ cybrosys technologies +
+
+
+
+

+ Our Services +

+ +
+
+
+
+

+ Our Industries +

+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + Trading + +

+

+ Easily procure and sell your products. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + Manufacturing + +

+

+ Plan, track and schedule your operations. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + Restaurant + +

+

+ Run your bar or restaurant methodical. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + POS + +

+

+ Easy configuring and convivial selling. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + E-commerce & Website + +

+

+ Mobile friendly, awe-inspiring product pages. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + Hotel Management + +

+

+ An all-inclusive hotel management application. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + Education + +

+

+ A Collaborative platform for educational management. +

+
+
+
+ +
+
+ + Odoo Industry + +
+
+
+

+ + Service Management + +

+

+ Keep track of services and invoice accordingly. +

+
+
+
+
+
+ +
+ +
diff --git a/salon_management/static/src/css/salon_dashboard.css b/salon_management/static/src/css/salon_dashboard.css new file mode 100644 index 000000000..a6bf4b46c --- /dev/null +++ b/salon_management/static/src/css/salon_dashboard.css @@ -0,0 +1,59 @@ +.style_chair { + text-align: center; + color: white; + font-size: 17px; + background: #0f101000; + padding: 0 !important; + text-transform: uppercase; + font-weight: 600; +} +.style_chair_booking{ + text-align: center; + color: #000000; + font-size: 17px; + background: #0f101000; + padding: 0 !important; + text-transform: uppercase; + font-weight: 600; +} +.style_collection { + color: black !important; + font-size: 12px; + text-align: center; + background: #29292900 !important; + padding: 0 !important; +} +.style_collection_booking{ + color: black !important; + font-size: 12px; + text-align: center; + background: #29292900 !important; + padding: 0 !important; +} +.no_of_people { + text-align: center; + font-size: 41px; + padding: 0 !important; + background: #4f535600; + color: white; + margin-top: 9px; + margin-bottom: 30px; +} +span.o_primary.style_collection { + color: #fff !important; +} +.no_of_people_booking{ + text-align: center; + font-size: 41px; + padding: 0 !important; + background: #4f535600; + color: #000000; + margin-top: 9px; + margin-bottom: 30px; +} +.button_background{ + background-color: Transparent; + border: transparent; + cursor: pointer; +} + diff --git a/salon_management/static/src/css/salon_website.css b/salon_management/static/src/css/salon_website.css new file mode 100644 index 000000000..548b7e72a --- /dev/null +++ b/salon_management/static/src/css/salon_website.css @@ -0,0 +1,46 @@ +.chair_box_website{ + text-align: center; + border: 1px solid #666; + padding: 10px 15px; + box-shadow: 7px 8px 5px #888888; + background-color: #337ab7; + color: #000000; +} +.field_booking_chair{ + font-weight: bold; +} +input[type="text"]{ + width: 100%; +} +input[type="email"]{ + width: 100%; +} +input[type=checkbox] + label { + color: #7c7bad; + font-style: italic; +} +input[type=checkbox]:checked + label { + color: #00a09d; + font-style: normal; +} +select{ + width: 100%; +} +.selection_service{ + height: 6em; + overflow: auto; + border: 1px solid #b3b3b3; + border-radius: 5px; + background-color: #fcfcfc; +} +#booking_chair_details{ + height: 200px!important; + text-align: center; + border: 1px solid #666; + padding: 15px 0px; + box-shadow: 7px 8px 5px #888888; + background-color:#7c7bad; + border-radius:58px; + color:#fff; + margin-bottom: 10px; +} diff --git a/salon_management/static/src/js/salon_booking.js b/salon_management/static/src/js/salon_booking.js new file mode 100644 index 000000000..07a5522a7 --- /dev/null +++ b/salon_management/static/src/js/salon_booking.js @@ -0,0 +1,105 @@ +odoo.define('salon_management.website_salon_booking_system', function (require) { +'use strict'; + var ajax = require('web.ajax'); + var base = require('web_editor.base'); + var core = require('web.core'); + var _t = core._t; + var rpc = require('web.rpc'); + var QWeb = core.qweb; + $(document).on('click',"#submit_button",function() { + var name = $("#name").val(); + var date = $("#date").val(); + var time = $("#time").val(); + var phone = $("#phone").val(); + var email = $("#email").val(); + var service = $('.check_box_salon:checkbox:checked'); + var chair = $( "#chair" ).val(); + console.log(date,time) + var list_service = []; + var number = service.length; + for (var i=0; i<(service.length); i++){ + var k = {i : service[i].attributes['service-id'].value}; + list_service.push(k); + }; + if (name == "" || date == "" || time == "" || phone == "" || email == "" || list_service.length == 0){ + alert("All fields are mandatory"); + } + else{ + var time_left_char = time.substring(0, 2); + var time_right_char = time.substring(3, 5); + var time_separator = time.substring(2,3); + if (isNaN(time_left_char) || isNaN(time_right_char) || time_separator != ":"){ + alert("Select a valid Time"); + } + else{ + var time_left = parseInt(time_left_char); + var time_right = parseInt(time_right_char); + console.log(time_left,time_right) + if ((time_left < 24) && (time_right < 60) && (time_left >= 0) && (time_right >= 0)){ + var booking_record = {'name': name, 'date': date, 'time': time, 'phone': phone, 'email': email, 'list_service':list_service,'chair': chair,'number': number }; + $.ajax({ + url: "/page/salon_details", + type: "POST", + dataType: "json", + data: booking_record, + type: 'POST', + success: function( data ) { + window.location.href = "/page/salon_management.salon_booking_thank_you"; + }, + error: function (error) { + alert('error: ' + error); + } + }); + } + else { + alert("Select a valid time"); + } + } + } + + }); + + $(document).on('click',"#check_button",function() { + var check_date = $( "#check_date" ).val(); + if (check_date != ""){ + ajax.jsonRpc("/page/salon_check_date", 'call', {'check_date':check_date }) + .then(function(order_details){ + var x; + var total_orders = ""; + var order = ""; + var chair_name; + for (x in order_details) { + var chair_name = order_details[x]['name'] + var i; + var lines = ""; + for (i = 0; i < order_details[x]['orders'].length; i++) { + lines += '' + order_details[x]['orders'][i]['number'] + + '' + order_details[x]['orders'][i]['start_time_only'] + + '' + order_details[x]['orders'][i]['end_time_only'] + '' + } + order += '
' + + '' + chair_name + '' + + '
Order Details' + + '
' + + '' + + '
' + + lines + '
Order No.Start TimeEnd Time
' + + } + total_orders += '
' + order + '
' + var res = document.getElementById('booking_chair_div') + res.innerHTML = ""; + res.innerHTML = total_orders; + var date_value = 'DATE : ' + check_date + '' + var date_field = document.getElementById('searched_date') + date_field.innerHTML = ""; + date_field.innerHTML = date_value; + }) + } + else { + alert("Fill the Field"); + } + }); + +}); \ No newline at end of file diff --git a/salon_management/static/src/less/salon_dashboard.css b/salon_management/static/src/less/salon_dashboard.css new file mode 100644 index 000000000..68033b431 --- /dev/null +++ b/salon_management/static/src/less/salon_dashboard.css @@ -0,0 +1,39 @@ +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record { + width: 180px; + height: 200px; + border-radius: 90px 90px 20px 20px; + background: #cc1f2f; + background: linear-gradient(#cc1f2f 0%, #cc1f2f 63%, #6d0b0b 63%, #cc1f2f 73%); + position: relative; + margin: 25px; + display: inline-block; +} +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record:after, +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record:before { + content: ""; + display: block; + width: 33.33333333px; + height: 111.11111111px; + position: absolute; + background: #cc1f2f; + border-radius: 20px; + border: 1px solid #000; + bottom: 25px; + right: 0; + left: 185px; + top: 64px; +} +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record:before { + left: -20px; +} +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record:after { + right: -20px; +} +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record.notvacant { + background: #ccc; + background: linear-gradient(#ccc 0%, #ccc 70%, #000 70%, #ccc 77%); +} +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record.notvacant:before, +.o_kanban_view.o_kanban_dashboard.o_salon_kanban .o_kanban_record.notvacant:after { + background: #ccc; +} diff --git a/salon_management/static/src/less/salon_dashboard.less b/salon_management/static/src/less/salon_dashboard.less new file mode 100644 index 000000000..2c48ac3e4 --- /dev/null +++ b/salon_management/static/src/less/salon_dashboard.less @@ -0,0 +1,42 @@ + +.o_kanban_view.o_kanban_dashboard.o_salon_kanban { + + .o_kanban_record { + width: 180px; + height: 200px; + border-radius: 90px 90px 20px 20px; + background: #cc1f2f; + background: linear-gradient(#cc1f2f 0%, #cc1f2f 63%, #6d0b0b 63%, #cc1f2f 73%); + position: relative; + margin: 25px; + display: inline-block; + } + .o_kanban_record:after, .o_kanban_record:before { + content: ""; + display: block; + width: 33.3333333333px; + height: 111.1111111111px; + position: absolute; + background: #cc1f2f; + border-radius: 20px; + border: 1px solid #000; + bottom: 25px; + right: 0; + left: 185px; + top: 64px; + } + .o_kanban_record:before { + left: -20px; + } + .o_kanban_record:after { + right: -20px; + } + .o_kanban_record.notvacant { + background: #ccc; + background: linear-gradient(#ccc 0%, #ccc 70%, #000 70%, #ccc 77%); + } + .o_kanban_record.notvacant:before, .o_kanban_record.notvacant:after { + background: #ccc; + } + +} \ No newline at end of file diff --git a/salon_management/templates/salon_booking_templates.xml b/salon_management/templates/salon_booking_templates.xml new file mode 100644 index 000000000..1ab472466 --- /dev/null +++ b/salon_management/templates/salon_booking_templates.xml @@ -0,0 +1,248 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/salon_management/views/booking_backend.xml b/salon_management/views/booking_backend.xml new file mode 100644 index 000000000..f1568df94 --- /dev/null +++ b/salon_management/views/booking_backend.xml @@ -0,0 +1,59 @@ + + + + + salon_booking_form.form + salon.booking + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+ + salon_booking_tree.tree + salon.booking + + + + + + + + + + + + Salon Bookings + salon.booking + tree,form + + + +
+
\ No newline at end of file diff --git a/salon_management/views/js_view.xml b/salon_management/views/js_view.xml new file mode 100644 index 000000000..d1564f79b --- /dev/null +++ b/salon_management/views/js_view.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/salon_management/views/salon_config.xml b/salon_management/views/salon_config.xml new file mode 100644 index 000000000..8eb4e4b20 --- /dev/null +++ b/salon_management/views/salon_config.xml @@ -0,0 +1,64 @@ + + + + + + res.config.settings.salon + res.config.settings + + + + +
+ +

Booking Chairs(Choose the chair when its free)

+
+
+
+
+
+
+
+
+
+
+
+ +

Holidays

+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + + + + + Settings + ir.actions.act_window + res.config.settings + form + inline + {'module' : 'salon_management'} + + + + + + diff --git a/salon_management/views/salon_data.xml b/salon_management/views/salon_data.xml new file mode 100644 index 000000000..e2fc2082d --- /dev/null +++ b/salon_management/views/salon_data.xml @@ -0,0 +1,61 @@ + + + + + salon_stages_form.form + salon.stages + +
+ +

+ + +

+
+
+
+
+ + Draft + 1 + + + + Open + 2 + + + + Invoiced + 3 + + + + Closed + 4 + + + + Cancelled + 5 + + + + Salon Default Customer + 5 + + + + Salon Service + + service + + +
+ + + + Chair-1 + + +
diff --git a/salon_management/views/salon_email_template.xml b/salon_management/views/salon_email_template.xml new file mode 100644 index 000000000..5082d1f19 --- /dev/null +++ b/salon_management/views/salon_email_template.xml @@ -0,0 +1,41 @@ + + + + + Salon e-mail template + ${object.company_id and object.company_id.email or ''} + Congratz ${object.name} + ${object.email|safe} + ${object.lang} + + + + Dear ${(object.name)},

+ Thank you for choosing ${(object.company_id.name)}, your booking was registered!

+ Regards,
+ ${(object.company_id.name)} + ]]> +
+
+ + Salon e-mail template + ${object.company_id and object.company_id.email or ''} + We are Sorry ${object.name} + ${object.email|safe} + ${object.lang} + + + + Dear ${(object.name)},

+ Thank you for choosing ${(object.company_id.name)}, but your booking was rejected!
+ there is no seats available for the time you chosen.
+ better luck next time.

+ Regards,
+ ${(object.company_id.name)} + ]]> +
+
+
+
\ No newline at end of file diff --git a/salon_management/views/salon_holiday.xml b/salon_management/views/salon_holiday.xml new file mode 100644 index 000000000..5c004428a --- /dev/null +++ b/salon_management/views/salon_holiday.xml @@ -0,0 +1,27 @@ + + + + + Sunday + True + + + Monday + + + Tuesday + + + Wednesday + + + Thursday + + + Friday + + + Saturday + + + \ No newline at end of file diff --git a/salon_management/views/salon_management_chair.xml b/salon_management/views/salon_management_chair.xml new file mode 100644 index 000000000..913ebd962 --- /dev/null +++ b/salon_management/views/salon_management_chair.xml @@ -0,0 +1,76 @@ + + + + + Chair Sequence + chair.sequence + Chair- + 2 + 1 + 1 + + + salon_chair_form.form + salon.chair + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + salon_chair_tree.tree + salon.chair + + + + + + + + + Dashboard + salon.chair + kanban,form + {} + + + Chair + salon.chair + tree,form + + + + + +
+
\ No newline at end of file diff --git a/salon_management/views/salon_management_dashboard.xml b/salon_management/views/salon_management_dashboard.xml new file mode 100644 index 000000000..da90f7b64 --- /dev/null +++ b/salon_management/views/salon_management_dashboard.xml @@ -0,0 +1,96 @@ + + + + + salon_management_kanban.dashboard + salon.chair + kanban + + + + + + + +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+
+ + Today's Collection : + + + + +
+
+
+
+ + Today's Collection : + + + + +
+
+
+
+ + Free After (Hours) : + + + + +
+
+
+
+ + Reserved For Booking + +
+
+
+
+
+
+
+
+ +
+
\ No newline at end of file diff --git a/salon_management/views/salon_management_services.xml b/salon_management/views/salon_management_services.xml new file mode 100644 index 000000000..53057a99d --- /dev/null +++ b/salon_management/views/salon_management_services.xml @@ -0,0 +1,37 @@ + + + + + salon_service_form.form + salon.service + +
+ + + + + + + +
+
+
+ + salon_service_tree.tree + salon.service + + + + + + + + + + Services + salon.service + tree,form + + +
+
\ No newline at end of file diff --git a/salon_management/views/salon_order_view.xml b/salon_management/views/salon_order_view.xml new file mode 100644 index 000000000..dfa814e36 --- /dev/null +++ b/salon_management/views/salon_order_view.xml @@ -0,0 +1,224 @@ + + + + + partner_salon_boolean.view + res.partner + + + + + + + + + Salon Order Sequence + salon.order.sequence + SALON/%(range_year)s/ + + + + 4 + + + salon_order_tree.tree + salon.order + + + + + + + + + + + + + + salon_order_kanban.kanban + salon.order + + + + + + + +
+ + + + +
+
+ +
+
+ Customer : + +
+ Chair : + +
+
+
+ Amount : + +
+ +
+ Date : + +
+
+
+
+
+
+
+
+
+ + 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..86d68f9f4 --- /dev/null +++ b/salon_management/views/working_hours.xml @@ -0,0 +1,72 @@ + + + + + + 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