diff --git a/fleet_car_workshop/README.rst b/fleet_car_workshop/README.rst new file mode 100644 index 000000000..cb574366d --- /dev/null +++ b/fleet_car_workshop/README.rst @@ -0,0 +1,18 @@ +Car Workshop v9 +=============== +Car Workshop Management helps to manage automobile workshop with +great ease. Keep track of everything, like vehicle owner details, +Works assigned, Bill details of service provided, etc. + +Features +======== +* User Friendly Interface. +* Effective Time management. +* Separate Journal Configuration. +* Integrated with Accounting. +* High Scalability. + + .. note:: + + # Its Working Domain Based on Project App. + # Mapped Fleet for Easy Method. diff --git a/fleet_car_workshop/__init__.py b/fleet_car_workshop/__init__.py new file mode 100644 index 000000000..f5faefb69 --- /dev/null +++ b/fleet_car_workshop/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 . +# +############################################################################## +import models + diff --git a/fleet_car_workshop/__openerp__.py b/fleet_car_workshop/__openerp__.py new file mode 100644 index 000000000..9d37f9e92 --- /dev/null +++ b/fleet_car_workshop/__openerp__.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 . +# +############################################################################## +{ + 'name': 'Car Workshop', + 'version': '9.0.1.0.0', + 'summary': 'Vehicle Workshop Operations', + 'description': 'Vehicle workshop operations & Its reports', + 'category': 'Industries', + 'author': 'Cybrosys Techno Solutions', + 'website': "http://www.cybrosys.com", + 'company': 'Cybrosys Techno Solutions', + 'depends': [ + 'base', + 'fleet', + 'account_accountant', + ], + 'data': [ + 'views/worksheet_views.xml', + 'views/car_dashboard.xml', + 'views/timesheet_view.xml', + 'views/worksheet_stages.xml', + 'views/vehicle.xml', + 'views/report.xml', + 'views/config_setting.xml', + 'views/workshop_data.xml', + 'security/workshop_security.xml', + 'security/ir.model.access.csv', + ], + + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': True, +} diff --git a/fleet_car_workshop/models/__init__.py b/fleet_car_workshop/models/__init__.py new file mode 100644 index 000000000..38924133d --- /dev/null +++ b/fleet_car_workshop/models/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 . +# +############################################################################## +import car_workshop +import timesheet +import dashboard +import config diff --git a/fleet_car_workshop/models/car_workshop.py b/fleet_car_workshop/models/car_workshop.py new file mode 100644 index 000000000..ff7b89b44 --- /dev/null +++ b/fleet_car_workshop/models/car_workshop.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 datetime import date +from dateutil.relativedelta import relativedelta +from openerp import models, api, fields, _ +from openerp.exceptions import UserError + + +class CarWorkshop(models.Model): + _name = 'car.workshop' + _inherit = ['mail.thread'] + + name = fields.Char(string='Title', track_visibility='onchange', required=True) + vehicle_id = fields.Many2one('car.car', string='Vehicle', track_visibility='onchange') + user_id = fields.Many2one('res.users', string='Assigned to', select=True) + active = fields.Boolean(string='Active') + partner_id = fields.Many2one('res.partner', string='Customer') + priority = fields.Selection([('0', 'Normal'), ('1', 'High')], 'Priority', select=True) + description = fields.Html(string='Description') + sequence = fields.Integer(string='Sequence', select=True, help="Gives the sequence order when displaying a list of tasks.") + tag_ids = fields.Many2many('worksheet.tags', string='Tags') + kanban_state = fields.Selection( + [('normal', 'In Progress'), ('done', 'Ready for next stage'), ('blocked', 'Blocked')], 'Kanban State', + help="A task's kanban state indicates special situations affecting it:\n" + " * Normal is the default situation\n" + " * Blocked indicates something is preventing the progress of this task\n" + " * Ready for next stage indicates the task is ready to be pulled to the next stage", + required=True, track_visibility='onchange', copy=False) + create_date = fields.Datetime(string='Create Date', readonly=True, select=True) + write_date = fields.Datetime(string='Last Modification Date', readonly=True, select=True) + date_start = fields.Datetime(string='Starting Date', select=True, copy=False) + date_end = fields.Datetime(string='Ending Date', select=True, copy=False) + date_assign = fields.Datetime(string='Assigning Date', select=True, copy=False, readonly=True) + date_deadline = fields.Datetime(string='Deadline', select=True, copy=False) + progress = fields.Integer(string="Working Time Progress(%)", copy=False, readonly=True) + date_last_stage_update = fields.Datetime(string='Last Stage Update', select=True, copy=False, readonly=True) + id = fields.Integer('ID', readonly=True) + company_id = fields.Many2many('res.company', string='Company Name') + color = fields.Integer(string='Color Index') + stage_id = fields.Many2one('worksheet.stages', string='Stage', track_visibility='onchange', copy=False) + state = fields.Selection([ + ('waiting', 'Ready'), + ('workshop_create_invoices', 'Invoiced'), + ('cancel', 'Invoice Canceled'), + ], string='Status', readonly=True, default='waiting', track_visibility='onchange', select=True) + attachment_ids = fields.One2many('ir.attachment', 'res_id', domain=lambda self: [('res_model', '=', self._name)], + auto_join=True, string='Attachments') + displayed_image_id = fields.Many2one('ir.attachment', + domain="[('res_model', '=', 'car.workshop'), ('res_id', '=', id)," + "\ ('mimetype', 'ilike', 'image')]", + string='Displayed Image') + planned_works = fields.One2many('planned.work', 'work_id', string='Planned/Ordered Works') + works_done = fields.One2many('planned.work', 'work_id', string='Work Done', domain=[('completed', '=', True)]) + materials_used = fields.One2many('material.used', 'material_id', string='Materials Used') + remaining_hour = fields.Float(string='Remaining Hour', readonly=True, compute="hours_left") + effective_hour = fields.Float(string='Hours Spent', readonly=True, compute="hours_spent") + amount_total = fields.Float(string='Total Amount', readonly=True, compute="amount_total1") + + def _get_default_stages(self, cr, uid, context=None): + """ Gives default stage_id """ + if context is None: + context = {} + default_vehicle_id = context.get('default_vehicle_id') + if not default_vehicle_id: + return False + return self.find_stage(cr, uid, [], default_vehicle_id, [('fold', '=', False)], context=context) + + _defaults = { + 'stage_id': _get_default_stages, + 'vehicle_id': lambda self, cr, uid, ctx=None: ctx.get('default_vehicle_id') if ctx is not None else False, + 'date_last_stage_update': fields.datetime.now(), + 'kanban_state': 'normal', + 'priority': '0', + 'sequence': 10, + 'active': True, + 'user_id': lambda obj, cr, uid, ctx=None: uid, + 'company_id': lambda self, cr, uid, ctx=None: self.pool.get('res.company')._company_default_get(cr, uid, + 'car.workshop', + context=ctx), + 'partner_id': lambda self, cr, uid, ctx=None: self._get_default_vehicle(cr, uid, context=ctx), + 'date_start': fields.datetime.now(), + } + + @api.depends('planned_works.work_cost', 'materials_used.price') + def amount_total1(self): + for records in self: + for hour in records: + amount_totall = 0.0 + for line in hour.planned_works: + amount_totall += line.work_cost + for line2 in hour.materials_used: + amount_totall += line2.price + records.amount_total = amount_totall + + @api.multi + def cancel(self): + self.state = 'cancel' + + @api.multi + def workshop_create_invoices(self): + + self.state = 'workshop_create_invoices' + inv_obj = self.env['account.invoice'] + inv_line_obj = self.env['account.invoice.line'] + customer = self.partner_id + if not customer.name: + raise UserError( + _( + 'Please select a Customer.')) + + company_id = self.env['res.users'].browse(1).company_id + currency_value = company_id.currency_id.id + self.ensure_one() + ir_values = self.env['ir.values'] + journal_id = ir_values.get_default('workshop.config.setting', 'invoice_journal_type') + if not journal_id: + journal_id = 1 + + inv_data = { + 'name': customer.name, + 'reference': customer.name, + 'account_id': customer.property_account_payable_id.id, + 'partner_id': customer.id, + 'currency_id': currency_value, + 'journal_id': journal_id, + 'origin': self.name, + 'company_id': company_id.id, + } + inv_id = inv_obj.create(inv_data) + for records in self.planned_works: + if records.planned_work.id : + income_account = records.planned_work.property_account_income_id.id + if not income_account: + raise UserError(_('There is no income account defined for this product: "%s".') % + (records.planned_work.name,)) + + inv_line_data = { + 'name': records.planned_work.name, + 'account_id': income_account, + 'price_unit': records.work_cost, + 'quantity': 1, + 'product_id': records.planned_work.id, + 'invoice_id': inv_id.id, + } + inv_line_obj.create(inv_line_data) + + for records in self.materials_used: + + inv_line_data = { + 'name': records.material.name, + 'account_id': records.material.property_account_income_id.id, + 'price_unit': records.price, + 'quantity': records.amount, + 'product_id': records.material.id, + 'invoice_id': inv_id.id, + } + inv_line_obj.create(inv_line_data) + + imd = self.env['ir.model.data'] + action = imd.xmlid_to_object('account.action_invoice_tree1') + list_view_id = imd.xmlid_to_res_id('account.invoice_tree') + form_view_id = imd.xmlid_to_res_id('account.invoice_form') + + result = { + 'name': action.name, + 'help': action.help, + 'type': 'ir.actions.act_window', + 'views': [[list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'kanban'], + [False, 'calendar'], [False, 'pivot']], + 'target': action.target, + 'context': action.context, + 'res_model': 'account.invoice', + } + if len(inv_id) > 1: + result['domain'] = "[('id','in',%s)]" % inv_id.ids + elif len(inv_id) == 1: + result['views'] = [(form_view_id, 'form')] + result['res_id'] = inv_id.ids[0] + else: + result = {'type': 'ir.actions.act_window_close'} + invoiced_records = self.env['car.workshop'] + + 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 + return result + + @api.depends('works_done.duration') + def hours_spent(self): + for hour in self: + effective_hour = 0.0 + for line in hour.works_done: + effective_hour += line.duration + self.effective_hour = effective_hour + + @api.depends('planned_works.time_spent') + def hours_left(self): + for hour in self: + remaining_hour = 0.0 + for line in hour.planned_works: + remaining_hour += line.time_spent + self.remaining_hour = remaining_hour-self.effective_hour + + def process_demo_scheduler_queue(self, cr, uid, context=None): + obj = self.pool.get('car.workshop') + obj1 = obj.search(cr, uid, []) + now = fields.Datetime.from_string(fields.Datetime.now()) + for obj2 in obj1: + obj3 = obj.browse(cr, uid, obj2, context=context) + if obj3.stage_id.name != 'Done' and obj3.stage_id.name != 'Cancelled' and obj3.stage_id.name != 'Verified': + start_date = fields.Datetime.from_string(obj3.date_start) + end_date = fields.Datetime.from_string(obj3.date_deadline) + if obj3.date_deadline and end_date > start_date: + if now < end_date: + diff1 = relativedelta(end_date, start_date) + if diff1.days == 0: + total_hr = int(diff1.minutes) + else: + total_hr = int(diff1.days) * 24 * 60 + int(diff1.minutes) + diff2 = relativedelta(now, start_date) + if diff2.days == 0: + current_hr = int(diff2.minutes) + else: + current_hr = int(diff2.days) * 24 * 60 + int(diff2.minutes) + if total_hr != 0: + obj3.progress = ((current_hr * 100) / total_hr) + else: + obj3.progress = 100 + else: + obj3.progress = 100 + else: + obj3.progress = 0 + + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'kanban_state' in init_values and record.kanban_state == 'blocked': + return 'fleet_car_workshop.mt_task_blocked' + elif 'kanban_state' in init_values and record.kanban_state == 'done': + return 'fleet_car_workshop.mt_task_ready' + elif 'user_id' in init_values and record.user_id: # assigned -> new + return 'fleet_car_workshop.mt_task_new' + elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: # start stage -> new + return 'fleet_car_workshop.mt_task_new' + elif 'stage_id' in init_values: + return 'fleet_car_workshop.mt_task_stage' + return super(CarWorkshop, self)._track_subtype(cr, uid, ids, init_values, context=context) + + def create(self, cr, uid, vals, context=None): + context = dict(context or {}) + + if vals.get('vehicle_id') and not context.get('default_vehicle_id'): + context['default_vehicle_id'] = vals.get('vehicle_id') + if vals.get('user_id'): + vals['date_assign'] = fields.datetime.now() + create_context = dict(context, mail_create_nolog=True) + work_id = super(CarWorkshop, self).create(cr, uid, vals, context=create_context) + return work_id + + def write(self, cr, uid, ids, vals, context=None): + if isinstance(ids, (int, long)): + ids = [ids] + if 'stage_id' in vals: + vals['date_last_stage_update'] = fields.datetime.now() + if vals.get('user_id'): + vals['date_assign'] = fields.datetime.now() + if vals and not'kanban_state' in vals and 'stage_id' in vals: + new_stage = vals.get('stage_id') + vals_reset_kstate = dict(vals, kanban_state='normal') + for t in self.browse(cr, uid, ids, context=context): + write_vals = vals_reset_kstate if t.stage_id.id != new_stage else vals + super(CarWorkshop, self).write(cr, uid, [t.id], write_vals, context=context) + result = True + else: + result = super(CarWorkshop, self).write(cr, uid, ids, vals, context=context) + return result + + def _read_group_stages(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): + if context is None: + context = {} + stage_obj = self.pool.get('worksheet.stages') + order = stage_obj._order + access_rights_uid = access_rights_uid or uid + if read_group_order == 'stage_id desc': + order = '%s desc' % order + if 'default_vehicle_id' in context: + search_domain = ['|', ('vehicle_ids', '=', context['default_vehicle_id']), ('id', 'in', ids)] + else: + search_domain = [('id', 'in', ids)] + stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) + result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) + result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) + + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + return result, fold + + _group_by_full = { + 'stage_id': _read_group_stages, + } + + @api.cr_uid_ids_context + def onchange_vehicle(self, cr, uid, id, vehicle_id, context=None): + values = {} + if vehicle_id: + vehicle = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context) + if vehicle.exists(): + values['partner_id'] = vehicle.partner_id.id + values['stage_id'] = self.find_stage(cr, uid, [], vehicle_id, [('fold', '=', False)], context=context) + else: + values['stage_id'] = False + return {'value': values} + + def _get_default_vehicle(self, cr, uid, context=None): + if context is None: + context = {} + if 'default_vehicle_id' in context: + vehicle = self.pool.get('car.car').browse(cr, uid, context['default_vehicle_id'], context=context) + if vehicle and vehicle.partner_id: + return vehicle.partner_id.id + return False + + def find_stage(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None): + """ Override of the base.stage method + Parameter of the stage search taken from the lead: + - section_id: if set, stages must belong to this section or + be a default stage; if not set, stages must be default + stages + """ + if isinstance(cases, (int, long)): + cases = self.browse(cr, uid, cases, context=context) + section_ids = [] + if section_id: + section_ids.append(section_id) + for task in cases: + if task.vehicle_id: + section_ids.append(task.vehicle_id.id) + search_domain = [] + if section_ids: + search_domain = [('|')] * (len(section_ids) - 1) + for section_id in section_ids: + search_domain.append(('vehicle_ids', '=', section_id)) + search_domain += list(domain) + stage_ids = self.pool.get('worksheet.stages').search(cr, uid, search_domain, order=order, context=context) + if stage_ids: + return stage_ids[0] + return False diff --git a/fleet_car_workshop/models/config.py b/fleet_car_workshop/models/config.py new file mode 100644 index 000000000..e7e87bdf4 --- /dev/null +++ b/fleet_car_workshop/models/config.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 openerp import fields, models, api +from openerp.tools.translate import _ + + +class WorkshopSetting(models.Model): + _name = "workshop.config.setting" + + invoice_journal_type = fields.Many2one('account.journal', string="Car Workshop Journal") + + @api.multi + def execute(self): + return self.env['ir.values'].sudo().set_default( + 'workshop.config.setting', 'invoice_journal_type', self.invoice_journal_type.id) + + def cancel(self, cr, uid, ids, context=None): + act_window = self.pool['ir.actions.act_window'] + action_ids = act_window.search(cr, uid, [('res_model', '=', self._name)]) + if action_ids: + return act_window.read(cr, uid, action_ids[0], [], context=context) + return {} + + +class WorksheetTags(models.Model): + + _name = "worksheet.tags" + _description = "Tags of vehicles's tasks, issues..." + + name = fields.Char('Name', required=True) + color = fields.Integer('Color Index') + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Tag name already exists !"), + ] + + +class WorksheetStages(models.Model): + _name = 'worksheet.stages' + _description = 'worksheet Stage' + _order = 'sequence' + + name = fields.Char(string='Stage Name', required=True) + description = fields.Text(string='Description', translate=True) + sequence = fields.Integer(string='Sequence') + vehicle_ids = fields.Many2many('car.car', 'worksheet_type_rel', 'type_id', 'vehicle_id', string='Vechicles') + fold = fields.Boolean('Folded in Tasks Pipeline', + help='This stage is folded in the kanban view when ' + 'there are no records in that stage to display.') + + def _get_default_vehicle_ids(self, cr, uid, ctx=None): + if ctx is None: + ctx = {} + default_vehicle_id = ctx.get('default_vehicle_id') + return [default_vehicle_id] if default_vehicle_id else None + + _defaults = { + 'sequence': 1, + 'vehicle_ids': _get_default_vehicle_ids, + } + _order = 'sequence' + + +class Services(models.Model): + _inherit = 'product.template' + + type = fields.Selection([('consu', _('Consumable')), ('service', _('Service')), ('product', _('Stockable Product'))], 'Product Type', required=True, + help="A consumable is a product for which you don't manage stock," + " a service is a non-material product provided by a company or an individual.") + + _defaults = { + 'type': 'service', + + } diff --git a/fleet_car_workshop/models/dashboard.py b/fleet_car_workshop/models/dashboard.py new file mode 100644 index 000000000..7ae783955 --- /dev/null +++ b/fleet_car_workshop/models/dashboard.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 openerp.tools.translate import _ +from openerp.osv import fields, osv + + +class CarVehicle(osv.osv): + _name = 'car.car' + _inherit = ['mail.thread'] + + def _get_visibility_selection_id(self, cr, uid, context=None): + """ Overriden in portal_project to offer more options """ + return [('portal', _('Customer Project: visible in portal if the customer is a follower')), + ('employees', _('All Employees Project: all employees can access')), + ('followers', _('Private Project: followers only'))] + + _visibility_selections = lambda self, *args, **kwargs: self._get_visibility_selection_id(*args, **kwargs) + + def get_task_count(self, cr, uid, ids, field_name, arg, context=None): + if context is None: + context = {} + res = {} + for vehicle in self.browse(cr, uid, ids, context=context): + res[vehicle.id] = len(vehicle.task_ids) + return res + + def _get_all_attached_docs(self, cr, uid, ids, field_name, arg, context): + res = {} + attachment = self.pool.get('ir.attachment') + worksheet = self.pool.get('car.workshop') + for id in ids: + project_attachments = attachment.search(cr, uid, [('res_model', '=', 'car.car'), + ('res_id', '=', id)], context=context, count=True) + task_ids = worksheet.search(cr, uid, [('vehicle_id', '=', id)], context=context) + task_attachments = attachment.search(cr, uid, [('res_model', '=', 'car.workshop'), + ('res_id', 'in', task_ids)], context=context, count=True) + res[id] = (project_attachments or 0) + (task_attachments or 0) + return res + + def attachment_tree_views(self, cr, uid, ids, context): + task_ids = self.pool.get('car.workshop').search(cr, uid, [('vehicle_id', 'in', ids)]) + domain = [ + '|', + '&', ('res_model', '=', 'car.car'), ('res_id', 'in', ids), + '&', ('res_model', '=', 'car.workshop'), ('res_id', 'in', task_ids)] + res_id = ids and ids[0] or False + return { + 'name': _('Attachments'), + 'domain': domain, + 'res_model': 'ir.attachment', + 'type': 'ir.actions.act_window', + 'view_id': False, + 'view_mode': 'kanban,tree,form', + 'view_type': 'form', + 'help': _('''

+ Documents are attached to the tasks and issues of your project.

+ Send messages or log internal notes with attachments to link + documents to your project. +

'''), + 'limit': 80, + 'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, res_id) + } + + _columns = { + 'active': fields.boolean('Active'), + 'name': fields.many2one('fleet.vehicle', string='Vehicle Name', track_visibility='onchange', required=True), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of Projects."), + + 'label_tasks': fields.char(string='Use Tasks as', help="Gives label to tasks on project's kanban view."), + 'worksheet': fields.one2many('car.workshop', 'vehicle_id', string="Task Activities"), + + 'type_ids': fields.many2many('worksheet.stages', 'car_workshop_type_rel', + 'vehicle_id', 'type_id', string='Worksheet Stages', + states={'close': [('readonly', True)], 'cancelled': [('readonly', True)]}), + 'task_count': fields.function(get_task_count, type='integer', string="Tasks", ), + 'task_ids': fields.one2many('car.workshop', 'vehicle_id', + domain=['|', ('stage_id.fold', '=', False), ('stage_id', '=', False)]), + 'doc_count': fields.function(_get_all_attached_docs, string="Number of documents attached", type='integer'), + 'color': fields.integer(string='Color Index'), + 'partner_id': fields.many2one('res.partner', string='Customer'), + 'state': fields.selection([('draft', 'New'), + ('open', 'In Progress'), + ('cancelled', 'Cancelled'), + ('pending', 'Pending'), + ('close', 'Closed')], string='Status', required=True, + track_visibility='onchange', copy=False), + + 'date_start': fields.date(string='Start Date'), + 'date': fields.date(string='Expiration Date', select=True, track_visibility='onchange'), + 'use_tasks': fields.boolean(string='Tasks'), + 'image_medium': fields.related('name', 'image_medium', type="binary", string="Logo (medium)"), + } + + _defaults = { + 'active': True, + 'use_tasks': True, + 'label_tasks': 'Tasks', + 'state': 'open', + + } + + def on_change_vehicle(self): + if not self.name: + return {} + model = self.pool.get('fleet.vehicle').browse(self.name) + return { + 'value': { + 'image_medium': model.image_medium, + } + } diff --git a/fleet_car_workshop/models/timesheet.py b/fleet_car_workshop/models/timesheet.py new file mode 100644 index 000000000..7130b4ff7 --- /dev/null +++ b/fleet_car_workshop/models/timesheet.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2008-TODAY Cybrosys Technologies(). +# Author: Nilmar Shereef() +# 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 openerp import fields, models, api + + +class PlannedWork (models.Model): + _name = 'planned.work' + + planned_work = fields.Many2one('product.template', string='Planned work', domain=[('type', '=', 'service')]) + time_spent = fields.Float(string='Estimated Time') + work_date = fields.Datetime(string='Date') # Date of work planned:planned date + responsible = fields.Many2one('res.users', string='Responsible') + work_id = fields.Many2one('car.workshop', string="Work id") + work_cost = fields.Float(string="Service Cost") + completed = fields.Boolean(string="Completed") + duration = fields.Float(string='Duration') + work_date2 = fields.Datetime(string='Date') # Date of work completed/done:completed date + + @api.onchange('planned_work') + def get_price(self): + self.work_cost = self.planned_work.lst_price + + +class MaterialUsed (models.Model): + _name = 'material.used' + + material = fields.Many2one('product.template', string='Products') + amount = fields.Integer(string='Quantity') + price = fields.Float(string='Unit Price') + material_id = fields.Many2one('car.workshop') + _defaults = { + 'amount': 1, } + + @api.onchange('material') + def get_price(self): + self.price = self.material.lst_price diff --git a/fleet_car_workshop/security/ir.model.access.csv b/fleet_car_workshop/security/ir.model.access.csv new file mode 100644 index 000000000..9f2ca804d --- /dev/null +++ b/fleet_car_workshop/security/ir.model.access.csv @@ -0,0 +1,22 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink + + + + + + +access_car_car_user,car.car.user,fleet_car_workshop.model_car_car,fleet.group_fleet_user,1,0,0,0 +access_car_workshop_user,car.workshop.user,fleet_car_workshop.model_car_workshop,fleet.group_fleet_user,1,0,0,0 +access_worksheet_tags_all_user,car.car_tags_all,fleet_car_workshop.model_worksheet_tags,fleet.group_fleet_user,1,0,0,0 +access_worksheet_stages_user,worksheet.stages.user,fleet_car_workshop.model_worksheet_stages,fleet.group_fleet_user,1,0,0,0 +access_service_product_user,car.car.products.user,fleet_car_workshop.model_product_template,fleet.group_fleet_user,0,0,0,0 +access_material_used_user,material.used.user,fleet_car_workshop.model_material_used,fleet.group_fleet_user,1,0,0,0 +access_planned_work_user,planned.work.user,fleet_car_workshop.model_planned_work,fleet.group_fleet_user,1,0,0,0 + +access_car_car_manager,car.car.manager,fleet_car_workshop.model_car_car,fleet.group_fleet_manager,1,1,1,1 +access_car_workshop_manager,car.workshop.manager,fleet_car_workshop.model_car_workshop,fleet.group_fleet_manager,1,1,1,1 +access_worksheet_tags_all_manager,car.car_tags_all.manager,fleet_car_workshop.model_worksheet_tags,fleet.group_fleet_manager,1,1,1,1 +access_worksheet_stages_manager,worksheet.stages.manager,fleet_car_workshop.model_worksheet_stages,fleet.group_fleet_manager,1,1,1,1 +access_service_product_manager,car.car.products.manager,fleet_car_workshop.model_product_template,fleet.group_fleet_manager,1,1,1,1 +access_material_used_manager,material.used.manager,fleet_car_workshop.model_material_used,fleet.group_fleet_manager,1,1,1,1 +access_planned_work_manager,planned.work.manager,fleet_car_workshop.model_planned_work,fleet.group_fleet_manager,1,1,1,1 \ No newline at end of file diff --git a/fleet_car_workshop/security/workshop_security.xml b/fleet_car_workshop/security/workshop_security.xml new file mode 100644 index 000000000..5caa40c8c --- /dev/null +++ b/fleet_car_workshop/security/workshop_security.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + Manager has all rights on vehicle + + + + + + + diff --git a/fleet_car_workshop/static/description/1.png b/fleet_car_workshop/static/description/1.png new file mode 100644 index 000000000..6c5af70d5 Binary files /dev/null and b/fleet_car_workshop/static/description/1.png differ diff --git a/fleet_car_workshop/static/description/4.png b/fleet_car_workshop/static/description/4.png new file mode 100644 index 000000000..8f56d846e Binary files /dev/null and b/fleet_car_workshop/static/description/4.png differ diff --git a/fleet_car_workshop/static/description/Screenshot from 2017-04-12 11:28:25.png b/fleet_car_workshop/static/description/Screenshot from 2017-04-12 11:28:25.png new file mode 100644 index 000000000..7db70ee05 Binary files /dev/null and b/fleet_car_workshop/static/description/Screenshot from 2017-04-12 11:28:25.png differ diff --git a/fleet_car_workshop/static/description/banner.jpg b/fleet_car_workshop/static/description/banner.jpg new file mode 100644 index 000000000..bd1041950 Binary files /dev/null and b/fleet_car_workshop/static/description/banner.jpg differ diff --git a/fleet_car_workshop/static/description/calendar.png b/fleet_car_workshop/static/description/calendar.png new file mode 100644 index 000000000..fd7c04056 Binary files /dev/null and b/fleet_car_workshop/static/description/calendar.png differ diff --git a/fleet_car_workshop/static/description/cybro_logo.png b/fleet_car_workshop/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/fleet_car_workshop/static/description/cybro_logo.png differ diff --git a/fleet_car_workshop/static/description/icon.png b/fleet_car_workshop/static/description/icon.png new file mode 100644 index 000000000..a17017510 Binary files /dev/null and b/fleet_car_workshop/static/description/icon.png differ diff --git a/fleet_car_workshop/static/description/index.html b/fleet_car_workshop/static/description/index.html new file mode 100644 index 000000000..6337dbdd0 --- /dev/null +++ b/fleet_car_workshop/static/description/index.html @@ -0,0 +1,166 @@ +
+
+

Car Workshop Management

+

+ +

+Car Workshop Management is Cybrosys Generic Module to manage automobile workshop with +great ease. Keep track of everything, like vehicle owner details, + Works assigned, Bill details of service provided,etc..
Some other features are as below: +

+
    +
  •    User Friendly Interface.
  • +
  •    Effective Time management.
  • +
  •    Separate Journal Configuration..
  • +
  •    Integrated with Accounting.
  • +
  •    High Scalability.
  • +
+
+
+ +
+
+
+

Dashboard/All Vehicles

+

Whole Workshop Analysis

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

All works

+
+

+

Goto Car Workshop -->Search -->Worksheet

+

Organise all the works according to their Status. Works/Tasks assigned works using the kanban view and + control deadlines in the calendar view.Each Work may have it's own stages. +

+
+
+

Kanban

+ + +
+
+

Calendar

+ +
+
+
+ +
+
+
+

Worksheet View

+

All details related to a work/task

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

Timesheet

+
+

+

Goto Car Workshop >Search >Worksheet >Timesheet

+

+

* In timesheet ,Planned work is the sub works related to main Work. By ticking + 'Completed' that work will be automatically updated to Work done.

+

* Work done is the details of completed works.

+

* Hour spent is time taken for completed work(Work Done).

+

* Remaining Hour is total time left(Difference between Total Time and Hour Spent).

+

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

Report

+

Tabular Details of Vehicles

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

Settings

+

Configure Preferred journal for Invoicing

+
+
+

+

Goto Car Workshop >Confiouration >Settings

+

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

Separate Journal

+

Invoices from the car workshop will be saved to Separate journal.

+
+
+

+

Goto Car Workshop >Search >Worksheet >Create Invoices

+

+
+
+ +
+
+
+
+ +
+
+

You Looking for a free Documentation of this Application.?

+

Give a Request Mail to:    odoo@cybrosys.com

+
+
+
+ +
+

Need Any Help?

+ + +
+ + diff --git a/fleet_car_workshop/static/description/invoice.png b/fleet_car_workshop/static/description/invoice.png new file mode 100644 index 000000000..a8d17b209 Binary files /dev/null and b/fleet_car_workshop/static/description/invoice.png differ diff --git a/fleet_car_workshop/static/description/journal.png b/fleet_car_workshop/static/description/journal.png new file mode 100644 index 000000000..9568f3eba Binary files /dev/null and b/fleet_car_workshop/static/description/journal.png differ diff --git a/fleet_car_workshop/static/description/kanban.png b/fleet_car_workshop/static/description/kanban.png new file mode 100644 index 000000000..e98220f66 Binary files /dev/null and b/fleet_car_workshop/static/description/kanban.png differ diff --git a/fleet_car_workshop/static/description/report.png b/fleet_car_workshop/static/description/report.png new file mode 100644 index 000000000..d751d543b Binary files /dev/null and b/fleet_car_workshop/static/description/report.png differ diff --git a/fleet_car_workshop/static/description/timesheet.png b/fleet_car_workshop/static/description/timesheet.png new file mode 100644 index 000000000..fe6cc7d47 Binary files /dev/null and b/fleet_car_workshop/static/description/timesheet.png differ diff --git a/fleet_car_workshop/static/description/worksheet.png b/fleet_car_workshop/static/description/worksheet.png new file mode 100644 index 000000000..ed7546ca4 Binary files /dev/null and b/fleet_car_workshop/static/description/worksheet.png differ diff --git a/fleet_car_workshop/static/src/css/vehicles.css b/fleet_car_workshop/static/src/css/vehicles.css new file mode 100644 index 000000000..c6e6d31c5 --- /dev/null +++ b/fleet_car_workshop/static/src/css/vehicles.css @@ -0,0 +1,40 @@ + +.oe_kanban_project_avatars img { + width: 30px; + border-radius: 2px; + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} +.oe_form_gantt_avatars:after { + font-family: "mnmliconsRegular" !important; + font-size: 21px; + font-weight: 300 !important; + content: "y"; + top: 3px; + position: relative; +} + +.openerp .oe_kanban_view .oe_kanban_project { + width: 250px; + min-height: 160px !important; + cursor: default; +} + +.openerp .oe_percent strong:after { + content: "%"; +} + +.openerp .oe_margin_top_8 { + margin-top: 8px; +} +.openerp .oe_kanban_project .oe_kanban_project_list .col-md-6 a{ + margin-left: 5px; +} + +/* Kanban status as label in project stage form view */ +.openerp label.oe_project_kanban_legend { + min-width: inherit !important; + margin-top: 6px; + margin-right: 8px; +} diff --git a/fleet_car_workshop/static/src/less/car_dashboard.less b/fleet_car_workshop/static/src/less/car_dashboard.less new file mode 100644 index 000000000..36c48bb9c --- /dev/null +++ b/fleet_car_workshop/static/src/less/car_dashboard.less @@ -0,0 +1,98 @@ +.o_kanban_view.o_kanban_dashboard.o_project_kanban { + + .o_kanban_record { + .o-flex-display(); + position: relative; + } + + .o_project_kanban_main { + .o-flex(0, 0, auto); + padding: @odoo-horizontal-padding; + width: 70%; // for IE11 overflow issue + + // give space for manage section at the bottom + margin-bottom: @odoo-horizontal-padding/2 + 19px; + + .o_kanban_card_content { + font-size: 12px; + .o_primary { + font-size: larger; + } + } + + .o_project_kanban_manage { + .o-position-absolute(@bottom: 0, @left: 0); + padding: @odoo-horizontal-padding/2 @odoo-horizontal-padding; + } + .o_kanban_card_manage_pane .o_kanban_card_manage_title { + margin: 0; + } + } + + .o_project_kanban_boxes { + width: 30%; + .o-flex(0, 0, auto); + + .o-flex-display(); + .o-flex-flow(column, nowrap); + + > a:hover { + text-decoration: none; + } + + .o_value { + font-size: x-large; + color: white; + display: block; + } + .o_label { + color: white; + } + .o_needaction{ + position: absolute; + top: 5px; + right: 5px; + color: white; + font-size: small; + opacity: 0.5; + &:hover{ + opacity:1; + } + &::before { + content: "\f086"; + font: normal normal normal 14px/1 FontAwesome; + } + } + + .o_project_kanban_box { + position: relative; + text-align: center; + padding: 15px 0 15px 0; + .o-flex(1, 1, auto); + .o-flex-display(); + .o-align-items(center); + .o-flex-flow(column, nowrap); + .o-justify-content(center); + } + .o_project_kanban_box:nth-child(even) { + background-color: grey; + } + .o_project_kanban_box:nth-child(odd) { + background-color: @odoo-brand-optional; + } + } +} + +.o_kanban_task_cover_image { + .o-columns(200px, 4); + > img { + cursor: pointer; + margin-bottom: 1em; + border: 1px solid transparent; + + &.o_selected { + border-color: @odoo-brand-secondary; + box-shadow: 0px 0px 2px 2px @odoo-brand-secondary; + } + } +} diff --git a/fleet_car_workshop/views/car_dashboard.xml b/fleet_car_workshop/views/car_dashboard.xml new file mode 100644 index 000000000..8c5a4a55c --- /dev/null +++ b/fleet_car_workshop/views/car_dashboard.xml @@ -0,0 +1,194 @@ + + + + car.car.form + car.car + +
+
+ +
+ +
+ + + + + +
+ +
+

+ +

+
+
+ +
+
+
+ + + + + + + + +
+
+ + +
+
+
+
+ + + + + car.car.select + car.car + + + + + + + + + + + + + + + + + + car.car.kanban + car.car + + + + + + + + + + + +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ Settings +
+ +
+
+
    +
+
+
+ More +
+
+ + +
+
+
+
+
+
+ + + car.car.tree + car.car + child_ids + + + + + + + + + + + Vehicles + car.car + form + [] + kanban,form + + {'search_default_Current': 1} + +

+ Create a new project. +

+

+ Organize your activities (plan tasks, track issues, invoice timesheets) for internal, personal or customer projects. +

+
+
+ + + + Vehicles + car.car + form + [] + tree,form + + {'search_default_Current': 1} + +

+ Create a new vehicle. +

+ +
+
+ + + +
\ No newline at end of file diff --git a/fleet_car_workshop/views/config_setting.xml b/fleet_car_workshop/views/config_setting.xml new file mode 100644 index 000000000..84652438e --- /dev/null +++ b/fleet_car_workshop/views/config_setting.xml @@ -0,0 +1,32 @@ + + + workshop settings + workshop.config.setting + +
+
+
+ +
+ + +
+
+
+
+ + + Settings + ir.actions.act_window + workshop.config.setting + + form + inline + + + + +
\ No newline at end of file diff --git a/fleet_car_workshop/views/report.xml b/fleet_car_workshop/views/report.xml new file mode 100644 index 000000000..bf2831b9c --- /dev/null +++ b/fleet_car_workshop/views/report.xml @@ -0,0 +1,16 @@ + + + Worksheets + car.workshop + pivot,graph + +

+ Odoo's car workshop management allows you to manage the pipeline of your work efficiently. You can track progress, discuss on works, attach documents, etc. +

+
+
+ + + + +
\ No newline at end of file diff --git a/fleet_car_workshop/views/timesheet_view.xml b/fleet_car_workshop/views/timesheet_view.xml new file mode 100644 index 000000000..b6a9fbbba --- /dev/null +++ b/fleet_car_workshop/views/timesheet_view.xml @@ -0,0 +1,59 @@ + + + + + + planned.work.form + planned.work + +
+ + + + + + + + + + + +
+
+
+ + + material.used.form + material.used + +
+ + + + + + + + + +
+
+
+ + + material.used.tree + material.used + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/fleet_car_workshop/views/vehicle.xml b/fleet_car_workshop/views/vehicle.xml new file mode 100644 index 000000000..0c8f8f82f --- /dev/null +++ b/fleet_car_workshop/views/vehicle.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/fleet_car_workshop/views/worksheet_stages.xml b/fleet_car_workshop/views/worksheet_stages.xml new file mode 100644 index 000000000..6abb7fa2d --- /dev/null +++ b/fleet_car_workshop/views/worksheet_stages.xml @@ -0,0 +1,90 @@ + + + + worksheet.stages.form + worksheet.stages + +
+ + + + + + + + + + +

+ You can also add a description to help your coworkers understand the meaning and purpose of the stage. +

+ +
+ + + +
+
+
+ + worksheet.stages.tree + worksheet.stages + + + + + + + + + + + Stages + worksheet.stages + form + + + + Tags + worksheet.tags + +
+ + + +
+
+
+ + Tags + worksheet.tags + form + +

+ Click to add a new tag. +

+
+
+ + + + Service-Products + ir.actions.act_window + product.template + kanban,tree,form + form + {"search_default_services":'service'} + +

+ Click to define a new product. +

+
+
+ + + + + + +
+
diff --git a/fleet_car_workshop/views/worksheet_views.xml b/fleet_car_workshop/views/worksheet_views.xml new file mode 100644 index 000000000..ffcfce17a --- /dev/null +++ b/fleet_car_workshop/views/worksheet_views.xml @@ -0,0 +1,289 @@ + + + + + Demo scheduler + + 1 + minutes + -1 + + + + + + + worksheet.form.view + car.workshop + +
+
+
+ + + +
+

+ + +

+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + car.workshop.kanban + car.workshop + + + + + + + + + + + + + + + +
+ + +
+
+ +
+
+ +
+
+ + oe_kanban_text_red + +
+
+ + +
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + + worksheet.tree.view + car.workshop + + + + + + + + + + + + + + worksheet.calender.view + car.workshop + + + + + + + + + + + car.workshop.pivot + car.workshop + + + + + + + + + + car.workshop.graph + car.workshop + + + + + + + + + + Worksheets + car.workshop + {'search_default_vehicle_id': active_id} + kanban,tree,form,calendar,pivot,graph + +

+ Odoo's car workshop management allows you to manage the pipeline of your work efficiently. You can track progress, discuss on works, attach documents, etc. +

+
+
+ + + + + car.workshop.search.form + car.workshop + + + + + + + + + + + + + + + + + + + + + + + + + + + + Worksheets + car.workshop + kanban,tree,form,calendar,pivot,graph + +

+ Odoo's car workshop management allows you to manage the pipeline of your work efficiently. You can track progress, discuss on works, attach documents, etc. +

+
+
+ + + + + + + + \ No newline at end of file diff --git a/fleet_car_workshop/views/workshop_data.xml b/fleet_car_workshop/views/workshop_data.xml new file mode 100644 index 000000000..3b1add472 --- /dev/null +++ b/fleet_car_workshop/views/workshop_data.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + Car + car.car + + + + Car Worksheet + car.workshop + + + + + + + + + + + + + + + + + + Task Opened + car.workshop + + + Task opened + + + Task Blocked + car.workshop + + Task blocked + + + Task Ready + car.workshop + + Task ready for Next Stage + + + Stage Changed + car.workshop + + Stage changed + + + + Task Opened + 10 + car.car + + + vehicle_id + + + Task Blocked + 11 + car.car + + + vehicle_id + + + Task Ready + 12 + car.car + + + vehicle_id + + + Task Stage Changed + 13 + car.car + + + vehicle_id + + + + + + + + + + + + + + + \ No newline at end of file