diff --git a/quotation_handler/README.rst b/quotation_handler/README.rst new file mode 100644 index 000000000..a3f4632b5 --- /dev/null +++ b/quotation_handler/README.rst @@ -0,0 +1,19 @@ +Quotation Revised History v10 +============================= +The clients will require negotiations whenever we sent a quotation to them.In such cases we need to edit the +Quotation's order lines. We have included the 'Revise the Quotation' button for it. The order lines can be edited +by a click on this button. At the same time a history or copy of the quotation will also be generated automatically. +We can track the history using the smart buttons. +Every quotations have a expiration date which we set manually.On configuring the above shown settings we can +automatically set the expiration date very easily. + +Features +======== + +* Retrieve the History of Revised Quotations. +* Automatically Set Expiration Date For Quotation. +* Email Template Contains The Expiration Date. + +Credits +======= +Nikhil Krishnan @ cybrosys, nikhil@cybrosys.in \ No newline at end of file diff --git a/quotation_handler/__init__.py b/quotation_handler/__init__.py new file mode 100644 index 000000000..5b63ab14d --- /dev/null +++ b/quotation_handler/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2017-TODAY Cybrosys Technologies(). +# Author: Nikhil krishnan() +# 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/quotation_handler/__manifest__.py b/quotation_handler/__manifest__.py new file mode 100644 index 000000000..19a9cddef --- /dev/null +++ b/quotation_handler/__manifest__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2017-TODAY Cybrosys Technologies(). +# Author: Nikhil krishnan() +# 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': 'Quotation Revised History', + 'version': '10.0.1.0.0', + 'summary': """We Can Retrieve the History of Revised Quotations.""", + 'description': """ Negotiations with clients and we resent multiple Quotations, + This module store the History of Every Quotations""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'website': 'http://www.cybrosys.com', + 'category': 'Sales Management', + 'depends': ['sale'], + 'license': 'LGPL-3', + 'data': [ + 'views/quotation_handler.xml', + 'views/sale_config_settings_exp_date_views.xml', + 'data/mail_template_data_exp_date.xml', + ], + 'demo': [], + 'images': ['static/description/banner.jpg'], + 'installable': True, + 'auto_install': False, +} diff --git a/quotation_handler/data/mail_template_data_exp_date.xml b/quotation_handler/data/mail_template_data_exp_date.xml new file mode 100644 index 000000000..915d5f8fb --- /dev/null +++ b/quotation_handler/data/mail_template_data_exp_date.xml @@ -0,0 +1,63 @@ + + + + + + Sales Order - Send by Email + ${(object.user_id.email and '%s <%s>' % (object.user_id.name, object.user_id.email) or '')|safe} + ${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' }) + ${object.partner_id.id} + + + + ${(object.name or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''} + ${object.partner_id.lang} + Dear ${object.partner_id.name} +% set access_action = object.get_access_action() +% set doc_name = 'quotation' if object.state in ('draft', 'sent') else 'order confirmation' +% set is_online = access_action and access_action['type'] == 'ir.actions.act_url' +% set access_name = is_online and object.template_id and 'Accept and pay %s online' % doc_name or 'View %s' % doc_name +% set access_url = is_online and access_action['url'] or object.get_signup_url() + +% if object.partner_id.parent_id: + (${object.partner_id.parent_id.name}) +% endif +,

+

+Here is your ${doc_name} ${object.name} +% if object.origin: +(with reference: ${object.origin} ) +% endif +amounting in ${object.amount_total} ${object.pricelist_id.currency_id.name} +from ${object.company_id.name}. +

+ +

+% if is_online: +
+ ${access_name} +

+ (or view attached PDF) +
+
+% endif +% if object.state in ('draft', 'sent'): +% if object.state == "pre" or object.state == "draft" or object.state == "sent" : + % if object.validity_date: +

Your quotation will expire on ${object.validity_date}.

+ % endif +% endif +

You can reply to this email if you have any questions.

+ +

Thank you,

+ +

+% if object.user_id and object.user_id.signature: + ${object.user_id.signature | safe} +% endif +

+]]>
+
+
+
diff --git a/quotation_handler/models/__init__.py b/quotation_handler/models/__init__.py new file mode 100644 index 000000000..1c12e02d9 --- /dev/null +++ b/quotation_handler/models/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2017-TODAY Cybrosys Technologies(). +# Author: Nikhil krishnan() +# 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 sale +import sale_config_settings_exp_date_views diff --git a/quotation_handler/models/sale.py b/quotation_handler/models/sale.py new file mode 100644 index 000000000..7a08da2e6 --- /dev/null +++ b/quotation_handler/models/sale.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2017-TODAY Cybrosys Technologies(). +# Author: Nikhil krishnan() +# 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 odoo import models, fields, api, _ +from datetime import timedelta, datetime + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + name = fields.Char(string='Order Reference', required=True, copy=False, readonly=True, + index=True, default='New') + parent_so_id = fields.Many2one('sale.order', 'Parent SO') + revised_order_count = fields.Integer(string='# of Revised Orders', compute='_revised_count') + validity_date = fields.Date(string='Expiration Date', readonly=True, states={'draft': [('readonly', False)], + 'pre': [('readonly', False)]}, + help="Automatically expiration date of your quotation (offer) will set as 14 days " + "later, or it will set the date automatically based on the settings, We can set " + "it manually too.") + + state = fields.Selection([ + ('pre', 'Revised Quotation'), + ('draft', 'Quotation'), + ('sent', 'Quotation Sent'), + ('revised', 'Revised'), + ('sale', 'Sale Order'), + ('done', 'Done'), + ('cancel', 'Cancelled'), + ], string='Status', readonly=True, copy=False, index=True, track_visibility='onchange', default='draft') + revision_number = fields.Integer(string='Revision', copy=False, default=1) + org_name = fields.Char(string='Origin', copy=False) + + @api.model + def create(self, vals): + date_start = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_start') + if not date_start: + if 'validity_date' in vals: + if vals.get('validity_date'): + pass + else: + date_no = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_no') + date_today = fields.Date.today() + date_object = datetime.strptime(date_today, '%Y-%m-%d') + if not date_no: + pass + else: + v_date = date_object + timedelta(days=date_no) + vals['validity_date'] = v_date + + return super(SaleOrder, self).create(vals) + + @api.multi + def _revised_count(self): + for sale in self: + revised_count = sale.search([('parent_so_id', '=', sale.id)]) + sale.revised_order_count = len(revised_count) + + # ********************Overwrite the print button to give expiration date******************** + @api.multi + def print_quotation(self): + is_date = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date') + date_start = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_start') + date_no = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_no') + if is_date: + if date_start: + if date_no: + for doc in self: + if doc.state in ['pre', 'draft']: + date_today = fields.Date.today() + date_object = datetime.strptime(date_today, '%Y-%m-%d') + v_date = date_object + timedelta(days=date_no) + doc.validity_date = v_date + self.filtered(lambda s: s.state == 'pre').write({'state': 'sent'}) + return super(SaleOrder, self).print_quotation() + + @api.multi + def make_revision(self): + for rec in self: + if not rec.org_name: + namee = rec.name + '/R' + str(rec.revision_number) + rec.org_name = rec.name + else: + namee = rec.org_name + '/R' + str(rec.revision_number) + if not rec.org_name: + names = rec.name + else: + names = rec.org_name + vals = { + 'name': names + "-" + str(rec.revision_number), + 'state': 'revised', + 'parent_so_id': rec.id + } + new_so_copy = rec.copy(default=vals) + rec.state = 'pre' + rec.name = namee + rec.revision_number += 1 + date_start = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_start') + if date_start: + rec.validity_date = False + + +class MailComposeMessage(models.TransientModel): + _inherit = 'mail.compose.message' + + @api.multi + def send_mail(self, auto_commit=False): + if self._context.get('default_model') == 'sale.order' and self._context.get('default_res_id') and self._context.get('mark_so_as_sent'): + order = self.env['sale.order'].browse([self._context['default_res_id']]) + # ********************Email Sent action Change the State in pre stage too******************** + if order.state in ('draft', 'pre'): + order.state = 'sent' + date_start = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_start') + if date_start: + # ********************Email Sent action Set the Exp Date From settings******************** + date_no = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date_no') + date_today = fields.Date.today() + date_object = datetime.strptime(date_today, '%Y-%m-%d') + if date_no: + v_date = date_object + timedelta(days=date_no) + order.validity_date = v_date + + self = self.with_context(mail_post_autofollow=True) + return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit) + diff --git a/quotation_handler/models/sale_config_settings_exp_date_views.py b/quotation_handler/models/sale_config_settings_exp_date_views.py new file mode 100644 index 000000000..3c9c280c1 --- /dev/null +++ b/quotation_handler/models/sale_config_settings_exp_date_views.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2017-TODAY Cybrosys Technologies(). +# Author: Nikhil krishnan() +# 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 odoo import api, fields, models + + +class SaleConfiguration(models.TransientModel): + _inherit = 'sale.config.settings' + + so_expiration_date = fields.Selection([(0, "No Default Expiration Date"), (1, "Default 14 days"), + (2, 'Created as company rule')], "Expiration Date", + help="Allows you to set Expiration Date.") + so_expiration_date_start = fields.Selection([(0, "Quotation Creation Date"), + (1, "Quotation Sent Date")], + "Expiration Date Start from", + help="Allows you to set Expiration Date Start from.") + so_expiration_date_no = fields.Integer(string="No.of Days") + + @api.multi + def set_default_so_expiration_date(self): + return self.env['ir.values'].sudo().set_default( + 'sale.config.settings', 'so_expiration_date', self.so_expiration_date) + + @api.multi + def set_default_so_expiration_date_start(self): + return self.env['ir.values'].sudo().set_default( + 'sale.config.settings', 'so_expiration_date_start', self.so_expiration_date_start) + + @api.multi + def set_default_so_expiration_date_no(self): + a = self.env['ir.values'].get_default('sale.config.settings', 'so_expiration_date', self.so_expiration_date) + if a == 0: + so_expiration_date_no = False + elif a == 1: + so_expiration_date_no = 14 + else: + so_expiration_date_no = self.so_expiration_date_no + return self.env['ir.values'].sudo().set_default( + 'sale.config.settings', 'so_expiration_date_no', so_expiration_date_no) + + diff --git a/quotation_handler/static/description/banner.jpg b/quotation_handler/static/description/banner.jpg new file mode 100644 index 000000000..33b4c1ed7 Binary files /dev/null and b/quotation_handler/static/description/banner.jpg differ diff --git a/quotation_handler/static/description/cybro_logo.png b/quotation_handler/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/quotation_handler/static/description/cybro_logo.png differ diff --git a/quotation_handler/static/description/editable line.png b/quotation_handler/static/description/editable line.png new file mode 100644 index 000000000..8f023fa34 Binary files /dev/null and b/quotation_handler/static/description/editable line.png differ diff --git a/quotation_handler/static/description/email template.png b/quotation_handler/static/description/email template.png new file mode 100644 index 000000000..71029c6d3 Binary files /dev/null and b/quotation_handler/static/description/email template.png differ diff --git a/quotation_handler/static/description/icon.png b/quotation_handler/static/description/icon.png new file mode 100644 index 000000000..17aa2186d Binary files /dev/null and b/quotation_handler/static/description/icon.png differ diff --git a/quotation_handler/static/description/index.html b/quotation_handler/static/description/index.html new file mode 100644 index 000000000..b3acf358a --- /dev/null +++ b/quotation_handler/static/description/index.html @@ -0,0 +1,189 @@ +
+
+

Quotation Revised History

+

Retrieve the History of Revised Quotations

+

Author : Cybrosys Techno Solutions , www.cybrosys.com

+
+

Features:

+
    +
  •    Retrieve the history of revised quotations.
  • +
  •    Automatically set expiration date for quotation.
  • +
  •    Email template contains the expiration date.
  • +
+
+
+
+ +
+
+

Quotation in "Quotation Sent" state

+
+
+
+ +
+
+
+

+ The clients will require negotiations whenever we sent a quotation to them.In such cases we need to edit the Quotation's order lines. + We have included the 'Revise the Quotation' button for it. The order lines can be edited by a click on this button. + At the same time a history or copy of the quotation will also be generated automatically.We can track the history using the smart buttons. +

+
+ +
+
+
+ +
+
+

Quotation in "Revised Quotation" state

+
+
+
+ +
+
+
+

+ In the Revised Quotation state, we can edit the order lines and resent to the client. +

Quotation Sent ---> Revised Quotation ---> Quotation Sent

+

The smart button on the top right of the form will provide the entire revised history of quotation. + The revised quotation will be indicated by a change in name format as (initial name/R1) in a recurring manner.

+

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

+

In the Quotation Sent state we can't edit the Order lines. When we need to revise just click on + the "Revise the Quotation" button.

+

+
+
+
+ +
+

+

In the Revised Quotation state we can edit the order lines and resend it through mail or + take print out with new order lines.

+

+
+
+
+ +
+
+

Quotation History

+
+

+

    +
  • Smart button redirect you to the Quotation History.
  • +
+
    +
  • Tree view of the Revised Quotations of the particular parent quotation.
  • +
+

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

Quotation in "Revised" state

+
+
+
+ +
+
+
+

+ In this state we can't edit anything, it's just for a view. +

+
+ +
+
+
+ +
+
+

Quotation Email template

+
+
+ +
+
+
+

+ If we gave expiration date then it will mention in the Email +

+
+
+
+ +
+
+

Quotations & Sales Settings

+
+
+ +
+
+
+

+ Sales --> Configuration --> Settings ---> Quotations & Sales --> Expiration Date +

+

+ Every quotations have a expiration date which we set manually.On configuring the above shown settings we can + automatically set the expiration date very easily. + +

No Expiration date: No changes(we have to set the date manually)

+

Default 14 days: The expiration date calculated from Quotation Creation Date or form Quotation Sent Date is set for 14 days.

+

Created as company rule: The quotation will expire as per the custom days set by user from Quotation Creation Date or from the Quotation Sent Date .

+

+

Expiration Date Calculated From : Choose either of the two, Quotation Creation Date or Quotation Sent Date

+
+
+
+ +
+

Need Any Help?

+ +
diff --git a/quotation_handler/static/description/revised form view.png b/quotation_handler/static/description/revised form view.png new file mode 100644 index 000000000..2021b3742 Binary files /dev/null and b/quotation_handler/static/description/revised form view.png differ diff --git a/quotation_handler/static/description/revised state.png b/quotation_handler/static/description/revised state.png new file mode 100644 index 000000000..53314745a Binary files /dev/null and b/quotation_handler/static/description/revised state.png differ diff --git a/quotation_handler/static/description/revised tree.png b/quotation_handler/static/description/revised tree.png new file mode 100644 index 000000000..67043a9b1 Binary files /dev/null and b/quotation_handler/static/description/revised tree.png differ diff --git a/quotation_handler/static/description/sent state not editable.png b/quotation_handler/static/description/sent state not editable.png new file mode 100644 index 000000000..6a401a2df Binary files /dev/null and b/quotation_handler/static/description/sent state not editable.png differ diff --git a/quotation_handler/static/description/sent state.png b/quotation_handler/static/description/sent state.png new file mode 100644 index 000000000..feb650552 Binary files /dev/null and b/quotation_handler/static/description/sent state.png differ diff --git a/quotation_handler/static/description/settings.png b/quotation_handler/static/description/settings.png new file mode 100644 index 000000000..c69ebc26c Binary files /dev/null and b/quotation_handler/static/description/settings.png differ diff --git a/quotation_handler/views/quotation_handler.xml b/quotation_handler/views/quotation_handler.xml new file mode 100644 index 000000000..8c0e414d9 --- /dev/null +++ b/quotation_handler/views/quotation_handler.xml @@ -0,0 +1,112 @@ + + + + + Revised Quotation Orders + sale.order + form + tree,form + + [('parent_so_id', '=', active_id)] + +

+ Revised order against this Quotation Order. +

+
+
+ + + + + Sales Orders + ir.actions.act_window + sale.order + form + tree,kanban,form,calendar,pivot,graph + + {} + [('state', 'not in', ('draft','revised','sent', 'cancel'))] + +

+ Create a Quotation, the first step of a new sale. +

+ Once the quotation is confirmed, it becomes a sales order. + You'll be able to invoice it and collect payments. + From the Sales Orders menu, you can track delivery + orders or services. +

+
+
+ + + Quotations + ir.actions.act_window + sale.order + form + + tree,kanban,form,calendar,pivot,graph + {} + [('state','in',('pre','draft','sent','cancel'))] + + +

+ Create a Quotation, the first step of a new sale. +

+ Your next actions should flow efficiently: confirm the Quotation + to a Sale Order, then create the Invoice and collect the Payment. +

+ Note that once a Quotation becomes a Sale Order, it will be moved + from the Quotations list to the Sales Order list. +

+
+
+ + + sale.order.form + sale.order + + + + + + + + {'readonly': [('state', 'in', ('sent','revised','done','cancel'))]} + + + + {'readonly': [('state', 'in', ('sent','revised','done','cancel'))]} + + + + {'readonly': [('state', 'in', ('sent','revised','done','cancel'))]} + + + + {'readonly': [('state', 'in', ('sent','revised','done','cancel'))]} + + + + + + sale.order.tree + sale.order + + + + + + + +
+
\ No newline at end of file diff --git a/quotation_handler/views/sale_config_settings_exp_date_views.xml b/quotation_handler/views/sale_config_settings_exp_date_views.xml new file mode 100644 index 000000000..0aaf9da03 --- /dev/null +++ b/quotation_handler/views/sale_config_settings_exp_date_views.xml @@ -0,0 +1,29 @@ + + + + + sale settings + sale.config.settings + + + + + + + + \ No newline at end of file