@ -0,0 +1,48 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Tender Management Sales |
|||
======================= |
|||
This module allows to manage different tenders in odoo along with option for comparing different sale order lines and |
|||
choose best one. |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (AGPL v3). |
|||
(https://www.odoo.com/documentation/user/16.0/legal/licenses/licenses.html) |
|||
|
|||
Configuration |
|||
============= |
|||
No additional configuration required |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
Developers: Nikhil M @cybrosys, |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if |
|||
your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import models |
|||
from . import wizard |
@ -0,0 +1,54 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
{ |
|||
'name': 'Tender Management Sales', |
|||
'version': "16.0.1.0.0", |
|||
'summary': 'Tender Management in Sales and Option to Compare Orders', |
|||
'description': """Tender Management in Sales and Option to Compare Orders""", |
|||
'category': 'Sale', |
|||
'author': 'Cybrosys Techno solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['sale_management'], |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'data/sale_tender_data.xml', |
|||
'views/tender_sales_views.xml', |
|||
'views/sale_order_views.xml', |
|||
'wizard/tender_sales_create_alternative.xml', |
|||
], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'tender_management_sales/static/src/widgets/sale_order_alternatives_widget.js', |
|||
'tender_management_sales/static/src/widgets/sale_order_alternatives_widget.scss', |
|||
'tender_management_sales/static/src/widgets/sale_order_alternatives_widget.xml', |
|||
'tender_management_sales/static/src/views/list/sale_order_line_compare_list_renderer.js', |
|||
'tender_management_sales/static/src/views/list/sale_order_line_compare_list_view.js', |
|||
], |
|||
}, |
|||
'license': 'AGPL-3', |
|||
'images': ['static/description/banner.png'], |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Agreement sample data --> |
|||
<data noupdate="1"> |
|||
<record id="seq_sale_tender" model="ir.sequence"> |
|||
<field name="name">Sale Tender</field> |
|||
<field name="code">sale.tender.order</field> |
|||
<field name="prefix">ST</field> |
|||
<field name="padding">5</field> |
|||
<field name="company_id" eval="False"/> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,6 @@ |
|||
## Module <tender_management_sales> |
|||
|
|||
#### 02.09.2024 |
|||
#### Version 16.0.1.0.0 |
|||
#### ADD |
|||
- Initial Commit for Tender Management Sales |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import sale_order |
|||
from . import tender_sales |
@ -0,0 +1,296 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models, _, Command |
|||
from collections import defaultdict |
|||
|
|||
|
|||
class SaleOrderGroup(models.Model): |
|||
"""Creating a model to group all the orders for tender management.""" |
|||
_name = 'sale.order.group' |
|||
_description = "Technical model to group Orders for tender management." |
|||
|
|||
order_ids = fields.One2many('sale.order', 'sale_group_id',help='Sale orders') |
|||
|
|||
def write(self, vals): |
|||
"""Remove records where the length of the order_ids is less than |
|||
or equal to 1""" |
|||
res = super().write(vals) |
|||
self.filtered(lambda g: len(g.order_ids) <= 1).unlink() |
|||
return res |
|||
|
|||
|
|||
class SaleOrder(models.Model): |
|||
_inherit = 'sale.order' |
|||
|
|||
tender_id = fields.Many2one('sale.tender', |
|||
string='Sale Agreement', |
|||
copy=False, help='Tender being used in order') |
|||
sale_group_id = fields.Many2one('sale.order.group', |
|||
help='Sale Group') |
|||
alternative_so_ids = fields.One2many( |
|||
'sale.order', related='sale_group_id.order_ids', readonly=False, |
|||
domain="[('id', '!=', id), ('state', 'in', ['draft', 'sent'])]", |
|||
string="Alternative SOs", check_company=True, |
|||
help="Other potential sale orders for selling products") |
|||
|
|||
@api.onchange('sale_order_template_id') |
|||
def _onchange_sale_order_template_id(self): |
|||
""" |
|||
This method is triggered when the 'sale_order_template_id' field is changed. |
|||
It overrides the default behavior to check for the presence of a 'tender_id' field: |
|||
""" |
|||
# Call the parent class method using super() |
|||
if self.tender_id: |
|||
if self.sale_order_template_id: |
|||
super(SaleOrder, self)._onchange_sale_order_template_id() |
|||
else: |
|||
super(SaleOrder, self)._onchange_sale_order_template_id() |
|||
|
|||
def write(self, vals): |
|||
"""Overriding the write function to add the cases in case of tender |
|||
is added.""" |
|||
if vals.get('sale_group_id', False): |
|||
orig_sale_group = self.sale_group_id |
|||
result = super(SaleOrder,self).write(vals) |
|||
if vals.get('alternative_so_ids', False): |
|||
if not self.sale_group_id and len(self.alternative_so_ids + self) > len(self): |
|||
# create/merge a new group |
|||
self.env['sale.order.group'].create({'order_ids': [Command.set(self.ids + self.alternative_so_ids.ids)]}) |
|||
elif self.sale_group_id and len(self.alternative_so_ids + self) <= 1: |
|||
# write in purchase group isn't called so we have to manually |
|||
# unlink obsolete groups here |
|||
self.sale_group_id.unlink() |
|||
if vals.get('sale_group_id', False): |
|||
additional_groups = orig_sale_group - self.sale_group_id |
|||
if additional_groups: |
|||
additional_sos = (additional_groups.order_ids - self.saled_group_id.order_ids) |
|||
additional_groups.unlink() |
|||
if additional_sos: |
|||
self.sale_group_id.order_ids |= additional_sos |
|||
|
|||
return result |
|||
|
|||
@api.model_create_multi |
|||
def create(self, vals_list): |
|||
"""Overriding the Create function to update vals in case of tender id.""" |
|||
orders = super().create(vals_list) |
|||
if self.env.context.get('origin_so_id'): |
|||
origin_so_id = self.env['sale.order'].browse( |
|||
self.env.context.get('origin_so_id')) |
|||
if origin_so_id.sale_group_id: |
|||
origin_so_id.sale_group_id.order_ids |= orders |
|||
else: |
|||
self.env['sale.order.group'].create({'order_ids': [Command.set(origin_so_id.ids + orders.ids)]}) |
|||
return orders |
|||
|
|||
def action_confirm(self): |
|||
"""Updated the state of other related orders and the agreement state on confirming the order.""" |
|||
res = super(SaleOrder, self).action_confirm() |
|||
for so in self: |
|||
if not so.tender_id: |
|||
continue |
|||
if so.tender_id.type_id.exclusive == 'exclusive': |
|||
others_so = so.tender_id.mapped('sale_order_ids').filtered(lambda r: r.id != so.id) |
|||
others_so.action_cancel() |
|||
if so.state not in ['draft', 'sent']: |
|||
so.tender_id.action_done() |
|||
return res |
|||
|
|||
@api.onchange('tender_id') |
|||
def _onchange_tender_id(self): |
|||
"""Function to update all the values from the tender from agreement to the sale order.""" |
|||
if not self.tender_id: |
|||
return |
|||
if self.sale_order_template_id: |
|||
self.sale_order_template_id = False |
|||
data = [fields.Command.clear()] |
|||
self.sale_order_option_ids = data |
|||
self.order_line = data |
|||
|
|||
self = self.with_company(self.company_id) |
|||
tender = self.tender_id |
|||
if self.partner_id: |
|||
partner = self.partner_id |
|||
else: |
|||
partner = tender.customer_id |
|||
|
|||
FiscalPosition = self.env['account.fiscal.position'] |
|||
fpos = FiscalPosition.with_company( |
|||
self.company_id)._get_fiscal_position(partner) |
|||
self.partner_id = partner.id |
|||
self.company_id = tender.company_id.id |
|||
self.currency_id = tender.currency_id.id |
|||
if not self.origin or tender.name not in self.origin.split(', '): |
|||
if self.origin: |
|||
if tender.name: |
|||
self.origin = self.origin + ', ' + tender.name |
|||
else: |
|||
self.origin = tender.name |
|||
self.note = tender.description |
|||
self.date_order = fields.Datetime.now() |
|||
|
|||
if tender.type_id.line_copy != 'copy': |
|||
return |
|||
|
|||
# Create SO lines if necessary |
|||
order_lines = [] |
|||
for line in tender.line_ids: |
|||
# Compute name |
|||
product_lang = line.product_id.with_context( |
|||
lang=partner.lang or self.env.user.lang, |
|||
partner_id=partner.id |
|||
) |
|||
name = product_lang.display_name |
|||
if product_lang.description_purchase: |
|||
name += '\n' + product_lang.description_purchase |
|||
|
|||
# Compute taxes |
|||
taxes_ids = fpos.map_tax(line.product_id.supplier_taxes_id.filtered(lambda tax: tax.company_id == tender.company_id)).ids |
|||
# Compute quantity and price_unit |
|||
if line.product_uom_id != line.product_id.uom_id: |
|||
product_qty = line.product_uom_id._compute_quantity(line.product_qty, line.product_id.uom_id) |
|||
price_unit = line.product_uom_id._compute_price(line.price_unit, line.product_id.uom_id) |
|||
else: |
|||
product_qty = line.product_qty |
|||
price_unit = line.price_unit |
|||
|
|||
if tender.type_id.quantity_copy != 'copy': |
|||
product_qty = 0 |
|||
|
|||
# Create SO line |
|||
order_line_values = line._prepare_sale_order_line( |
|||
name=name, product_qty=product_qty, price_unit=price_unit, |
|||
taxes_ids=taxes_ids) |
|||
order_lines.append((0, 0, order_line_values)) |
|||
self.order_line = order_lines |
|||
|
|||
def action_compare_alternative_lines(self): |
|||
"""" Function to return to the view of line compare tree.""" |
|||
ctx = dict( |
|||
self.env.context, |
|||
search_default_product=True, |
|||
sale_order_id=self.id, |
|||
) |
|||
view_id = self.env.ref('tender_management_sales.sale_order_line_compare_tree').id |
|||
return { |
|||
'name': _('Compare Order Lines'), |
|||
'type': 'ir.actions.act_window', |
|||
'view_mode': 'list', |
|||
'res_model': 'sale.order.line', |
|||
'views': [(view_id, "list")], |
|||
'domain': [('order_id', 'in', (self | self.alternative_so_ids).ids), ('display_type', '=', False)], |
|||
'context': ctx, |
|||
} |
|||
|
|||
def action_create_alternative(self): |
|||
"""Function to create alternative orders.""" |
|||
ctx = dict(**self.env.context, default_origin_so_id=self.id) |
|||
return { |
|||
'name': _('Create alternative'), |
|||
'type': 'ir.actions.act_window', |
|||
'view_mode': 'form', |
|||
'res_model': 'sale.tender.create.alternative', |
|||
'view_id': self.env.ref('tender_management_sales.sale_tender_create_alternative_form').id, |
|||
'target': 'new', |
|||
'context': ctx, |
|||
} |
|||
|
|||
def get_tender_best_lines(self): |
|||
"""" Function to get the best order lines from grouped orders.""" |
|||
product_to_best_price_line = defaultdict(lambda: self.env['sale.order.line']) |
|||
product_to_best_price_unit = defaultdict(lambda: self.env['sale.order.line']) |
|||
so_alternatives = self | self.alternative_so_ids |
|||
|
|||
multiple_currencies = False |
|||
if len(so_alternatives.currency_id) > 1: |
|||
multiple_currencies = True |
|||
|
|||
for line in so_alternatives.order_line: |
|||
if not line.product_uom_qty or not line.price_subtotal or line.state in ['cancel', 'purchase', 'done']: |
|||
continue |
|||
|
|||
if not product_to_best_price_line[line.product_id]: |
|||
product_to_best_price_line[line.product_id] = line |
|||
product_to_best_price_unit[line.product_id] = line |
|||
else: |
|||
price_subtotal = line.price_subtotal |
|||
price_unit = line.price_unit |
|||
current_price_subtotal = product_to_best_price_line[line.product_id][0].price_subtotal |
|||
current_price_unit = product_to_best_price_unit[line.product_id][0].price_unit |
|||
if multiple_currencies: |
|||
price_subtotal /= line.order_id.currency_rate |
|||
price_unit /= line.order_id.currency_rate |
|||
current_price_subtotal /= product_to_best_price_line[line.product_id][0].order_id.currency_rate |
|||
current_price_unit /= product_to_best_price_unit[line.product_id][0].order_id.currency_rate |
|||
if current_price_subtotal < price_subtotal: |
|||
product_to_best_price_line[line.product_id] = line |
|||
elif current_price_subtotal == price_subtotal: |
|||
product_to_best_price_line[line.product_id] |= line |
|||
|
|||
if current_price_unit < price_unit: |
|||
product_to_best_price_unit[line.product_id] = line |
|||
elif current_price_unit == price_unit: |
|||
product_to_best_price_unit[line.product_id] |= line |
|||
|
|||
best_price_ids = set() |
|||
best_price_unit_ids = set() |
|||
for lines in product_to_best_price_line.values(): |
|||
best_price_ids.update(lines.ids) |
|||
for lines in product_to_best_price_unit.values(): |
|||
best_price_unit_ids.update(lines.ids) |
|||
return list(best_price_ids), list(best_price_unit_ids) |
|||
|
|||
|
|||
class SaleOrderLine(models.Model): |
|||
_inherit = 'sale.order.line' |
|||
|
|||
def action_clear_quantities(self): |
|||
"""" Function to clear quantities from the current line.""" |
|||
zeroed_lines = self.filtered(lambda l: l.state not in ['cancel', 'sale', 'done']) |
|||
zeroed_lines.write({'product_uom_qty': 0}) |
|||
if len(self) > len(zeroed_lines): |
|||
return { |
|||
'type': 'ir.actions.client', |
|||
'tag': 'display_notification', |
|||
'params': { |
|||
'title': _("Some not cleared"), |
|||
'message': _("Some quantities were not cleared because their status is not a Draft."), |
|||
'sticky': False, |
|||
} |
|||
} |
|||
return False |
|||
|
|||
def action_choose(self): |
|||
"""" Function to choose a line among different order lines so that other lines quantities will update to null.""" |
|||
order_lines = (self.order_id | self.order_id.alternative_so_ids).mapped('order_line') |
|||
order_lines = order_lines.filtered(lambda l: l.product_uom_qty and l.product_id.id in self.product_id.ids and l.id not in self.ids) |
|||
if order_lines: |
|||
return order_lines.action_clear_quantities() |
|||
return { |
|||
'type': 'ir.actions.client', |
|||
'tag': 'display_notification', |
|||
'params': { |
|||
'title': _("Nothing to clear"), |
|||
'message': _("There are no quantities to clear."), |
|||
'sticky': False, |
|||
} |
|||
} |
@ -0,0 +1,182 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models, _ |
|||
from odoo.exceptions import UserError |
|||
|
|||
SALE_TENDER_STATES = [ |
|||
('draft', 'Draft'), |
|||
('ongoing', 'Ongoing'), |
|||
('in_progress', 'Confirmed'), |
|||
('open', 'Bid Selection'), |
|||
('done', 'Closed'), |
|||
('cancel', 'Cancelled') |
|||
] |
|||
|
|||
|
|||
class SaleTenderType(models.Model): |
|||
"""Creating a model to store the tender type.""" |
|||
|
|||
_name = "sale.tender.type" |
|||
_description = "Sale Tender Type" |
|||
|
|||
name = fields.Char(string='Agreement Type', required=True, translate=True,help="Name") |
|||
exclusive = fields.Selection([ |
|||
('exclusive', 'Select only one Quotation (exclusive)'), ('multiple', 'Select multiple Quotation (non-exclusive)')], |
|||
string='Agreement Selection Type', required=True, default='multiple', |
|||
help="""Select only one Quotation (exclusive): when a sale order is confirmed, cancel the remaining sale order.\n |
|||
Select multiple Quotation (non-exclusive): allows multiple sale orders. On confirmation of a sale order it does not cancel the remaining orders""") |
|||
quantity_copy = fields.Selection([ |
|||
('copy', 'Use quantities of agreement'), ('none', 'Set quantities manually')], |
|||
string='Quantities', required=True, default='none',help="To copy quantity") |
|||
line_copy = fields.Selection([ |
|||
('copy', 'Use lines of agreement'), ('none', 'Do not create Quotations lines automatically')], |
|||
string='Lines', required=True, default='copy',help="To copy line") |
|||
|
|||
|
|||
class SaleTender(models.Model): |
|||
"""Creating a model to record all the sale tender agreements.""" |
|||
_name = "sale.tender" |
|||
_description = "Sale Tender" |
|||
_order = 'id desc' |
|||
_inherit = ['mail.thread', 'mail.activity.mixin'] |
|||
|
|||
@api.depends('sale_order_ids') |
|||
def _compute_orders_number(self): |
|||
"""Function to compute the number of associated orders for the agreement.""" |
|||
for tender in self: |
|||
tender.order_count = len(tender.sale_order_ids) |
|||
|
|||
def action_in_progress(self): |
|||
"""Function to activate the current agreement.""" |
|||
self.ensure_one() |
|||
if not self.line_ids: |
|||
raise UserError(_("You cannot confirm agreement '%s' because there is no product line.", self.name)) |
|||
for tender_line in self.line_ids: |
|||
if tender_line.price_unit <= 0.0: |
|||
raise UserError(_('You cannot confirm the Tender without price.')) |
|||
if tender_line.product_qty <= 0.0: |
|||
raise UserError(_('You cannot confirm the Tender order without quantity.')) |
|||
self.write({'state': 'ongoing'}) |
|||
# Set the sequence number regarding the tender type |
|||
if self.name == 'New': |
|||
self.name = self.env['ir.sequence'].next_by_code('sale.tender.order') |
|||
|
|||
def action_cancel(self): |
|||
"""Function to cancel the current agreement.""" |
|||
self.write({'state': 'cancel'}) |
|||
|
|||
def action_done(self): |
|||
""" |
|||
Generate all sale order based on selected lines, should only be called on one agreement at a time |
|||
""" |
|||
if any(sale_order.state in ['draft', 'sent'] for sale_order in self.mapped('sale_order_ids')): |
|||
raise UserError(_('You have to cancel or validate every Order before closing the sale tender.')) |
|||
self.write({'state': 'done'}) |
|||
|
|||
name = fields.Char('Reference', required=True, copy=False, default='New',readonly=True,help="Name") |
|||
|
|||
order_count = fields.Integer(compute='_compute_orders_number', |
|||
string='Number of Orders',help="Count of orders") |
|||
customer_id = fields.Many2one('res.partner', string="Customer", |
|||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",help="Customer Choosen") |
|||
type_id = fields.Many2one('sale.tender.type', |
|||
string="Agreement Type", required=True,help="Type") |
|||
ordering_date = fields.Date(string="Ordering Date", tracking=True,help="Order Date") |
|||
date_end = fields.Datetime(string='Agreement Deadline', tracking=True,help="End Date") |
|||
schedule_date = fields.Date(string='Delivery Date', index=True, |
|||
help="The expected and scheduled delivery date where all the products are received", |
|||
tracking=True) |
|||
user_id = fields.Many2one( |
|||
'res.users', string='Sales Representative', |
|||
default=lambda self: self.env.user, check_company=True,help="Username") |
|||
description = fields.Html(help="Description") |
|||
company_id = fields.Many2one('res.company', string='Company', required=True, |
|||
default=lambda self: self.env.company,help="Company") |
|||
sale_order_ids = fields.One2many('sale.order', 'tender_id', |
|||
string='Sale Orders', |
|||
states={'done': [('readonly', True)]},help="Sale orders") |
|||
line_ids = fields.One2many('sale.tender.line', 'tender_id', |
|||
string='Products to Sell', |
|||
states={'done': [('readonly', True)]}, copy=True,help="Tender Lines") |
|||
|
|||
state = fields.Selection(SALE_TENDER_STATES, |
|||
'Status', tracking=True, required=True, |
|||
copy=False, default='draft',help="Status") |
|||
|
|||
currency_id = fields.Many2one('res.currency', 'Currency', required=True, |
|||
default=lambda |
|||
self: self.env.company.currency_id.id,help="Currency") |
|||
|
|||
|
|||
class SaleTender_idLine(models.Model): |
|||
"""Creating the model to store the lines in the tender.""" |
|||
_name = "sale.tender.line" |
|||
_inherit = 'analytic.mixin' |
|||
_description = "Sale Tender Line" |
|||
_rec_name = 'product_id' |
|||
|
|||
@api.onchange('product_id') |
|||
def _onchange_product_id(self): |
|||
"""Function to update the line price.""" |
|||
self.price_unit = self.product_id.list_price |
|||
self.product_qty = 1 |
|||
|
|||
@api.model_create_multi |
|||
def create(self, vals_list): |
|||
"""Overriding the create function to update the uom value and also add restrictions.""" |
|||
for vals in vals_list: |
|||
if not vals.get('product_uom_id'): |
|||
vals['product_uom_id'] = self.env["product.product"].browse( |
|||
vals.get('product_id')).uom_id.id |
|||
lines = super().create(vals_list) |
|||
for line, vals in zip(lines, vals_list): |
|||
if line.tender_id.state not in ['draft', 'cancel','done'] and line.tender_id.type_id.is_quantity_copy == 'none': |
|||
if vals['price_unit'] <= 0.0: |
|||
raise UserError( |
|||
_('You cannot confirm the blanket order without price.')) |
|||
return lines |
|||
|
|||
product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], required=True,help="Product") |
|||
product_uom_id = fields.Many2one('uom.uom', string='Product Unit of Measure', domain="[('category_id', '=', product_uom_category_id)]",help="Uom") |
|||
product_uom_category_id = fields.Many2one(related='product_id.uom_id.category_id',help="Uom Category") |
|||
product_qty = fields.Float(string='Quantity', digits='Product Unit of Measure',help="Quantity") |
|||
product_description_variants = fields.Char('Custom Description',help="Variants Description") |
|||
price_unit = fields.Float(string='Unit Price', digits='Product Price',help="Price Unit") |
|||
tender_id = fields.Many2one('sale.tender', required=True, string='Sale Agreement', ondelete='cascade',help="Tender") |
|||
company_id = fields.Many2one('res.company', related='tender_id.company_id', string='Company', store=True, readonly=True,help="Company") |
|||
schedule_date = fields.Date(string='Scheduled Date',help="Scheduled Date") |
|||
|
|||
def _prepare_sale_order_line(self, name, product_qty=0.0, price_unit=0.0, taxes_ids=False): |
|||
"""Function to prepare the required values for sale order lines.""" |
|||
self.ensure_one() |
|||
if self.product_description_variants: |
|||
name += '\n' + self.product_description_variants |
|||
res = { |
|||
'name': name, |
|||
'product_id': self.product_id.id, |
|||
'product_uom': self.product_uom_id.id, |
|||
'product_uom_qty': product_qty, |
|||
'price_unit': price_unit, |
|||
'tax_id': [(6, 0, taxes_ids)], |
|||
'analytic_distribution': self.analytic_distribution, |
|||
} |
|||
return res |
|
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 411 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 142 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 307 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,683 @@ |
|||
<div style="background-color: #714B67; height: 810px; width: 100%; padding: 15px; position: relative;"> |
|||
<!-- TITLE BAR --> |
|||
<div class="d-flex align-items-center justify-content-between" |
|||
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;"> |
|||
<img src="assets/misc/cybrosys-logo.png" width="42" height="42" |
|||
style="width: 42px; height: 42px;"/> |
|||
<div> |
|||
<div |
|||
style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Community |
|||
</div> |
|||
<div |
|||
style="color: #875A7B; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Enterprise |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF TITLE BAR --> |
|||
<div class="container"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-12 col-lg-12"> |
|||
<!-- APP HERO --> |
|||
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;"> |
|||
Tender Management Sales</h1> |
|||
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;"> |
|||
Tender Management Sales for managing agreements in sale orders </p> |
|||
<!-- END OF APP HERO --> |
|||
<img src="assets/screenshots/hero.gif" class="img-responsive" |
|||
style="width: 100%; margin-left: auto; margin-right: auto;"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
|
|||
<!-- NAVIGATION SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/compass.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Explore This |
|||
Module</h2> |
|||
</div> |
|||
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;"> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#overview"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span> |
|||
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn |
|||
more about this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#features"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span> |
|||
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
|||
features of this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#screenshots"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span> |
|||
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
|||
screenshots for this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<!-- END OF NAVIGATION SECTION --> |
|||
|
|||
<!-- OVERVIEW SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="overview"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/pie-chart.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Overview |
|||
</h2> |
|||
</div> |
|||
<div class="row" |
|||
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
|||
<div class="col-sm-12 py-4"> |
|||
This module allows to manage different tenders in odoo along with |
|||
option for comparing different sale order lines and choose best one. |
|||
</div> |
|||
</div> |
|||
<!-- END OF OVERVIEW SECTION --> |
|||
|
|||
<!-- FEATURES SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="features"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/features.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Features |
|||
</h2> |
|||
</div> |
|||
<div class="row" |
|||
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Add different agreements for sale order. |
|||
</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Compare different sale order lines. |
|||
</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Choose the best among the order lines.</span> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<!-- END OF FEATURES SECTION --> |
|||
|
|||
<!-- SCREENSHOTS SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" |
|||
id="screenshots"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/pictures.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Screenshots |
|||
</h2> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
The menu to create sale agreements will be available from here. |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
The menu to create sale agreements will be available from here.</p> |
|||
<img src="assets/screenshots/2.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Agreement Form. |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
In the agreements form we can create the agreement |
|||
types |
|||
and choose other options as we needed.</p> |
|||
<img src="assets/screenshots/3.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Agreement Form |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
Once the agreement type is selected we can add lines and other details from here. |
|||
After that we can create different sale orders from the form and all the created |
|||
orders |
|||
can be accessed from the smart tab.</p> |
|||
<img src="assets/screenshots/4.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Sale order. |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
For the sale orders created from the agreement form the agreement will be |
|||
automatically |
|||
updated in the sale order form.</p> |
|||
<img src="assets/screenshots/5.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Create alternatives. |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
This module allows to create alternative sale orders from the alternatives tab in |
|||
the sale order tab. The option to use the copy of the current sale order will also |
|||
be available.</p> |
|||
<img src="assets/screenshots/6.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Compare Lines |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
Once the alternative orders are created the option to compare the order |
|||
lines from the sale order will be available.</p> |
|||
<img src="assets/screenshots/7.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Compare Lines View |
|||
</h3> |
|||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> |
|||
On clicking the compare lines the view to compare the lines will be available. |
|||
Here the lines with the best unit price and total will be highlighted and |
|||
can choose/ clear the lines.</p> |
|||
<img src="assets/screenshots/8.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<!-- END OF SCREENSHOTS SECTION --> |
|||
|
|||
<!-- RELATED PRODUCTS --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/categories.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Related |
|||
Products |
|||
</h2> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div id="demo1" class="row carousel slide" data-ride="carousel"> |
|||
<!-- The slideshow --> |
|||
<div class="carousel-inner" style="padding: 30px;"> |
|||
<div class="carousel-item" style="min-height: 198.656px;"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/dynamic_accounts_report/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/1.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/15.0/custom_gantt_view/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/2.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/15.0/project_custom_gantt/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/3.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="carousel-item active" |
|||
style="min-height: 198.656px;"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/15.0/account_reports_xlsx/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/4.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/base_accounting_kit/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/5.gif"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/hr_payroll_community/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/6.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Left and right controls --> |
|||
<a class="carousel-control-prev" href="#demo1" data-slide="prev" |
|||
style="width:35px; color:#000"> <span |
|||
class="carousel-control-prev-icon"><i |
|||
class="fa fa-chevron-left" |
|||
style="font-size:24px"></i></span> |
|||
</a> <a class="carousel-control-next" href="#demo1" |
|||
data-slide="next" style="width:35px; color:#000"> |
|||
<span class="carousel-control-next-icon"><i |
|||
class="fa fa-chevron-right" |
|||
style="font-size:24px"></i></span> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF RELATED PRODUCTS --> |
|||
|
|||
<!-- OUR SERVICES --> |
|||
|
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/star.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Our Services |
|||
</h2> |
|||
</div> |
|||
|
|||
<div class="container my-5"> |
|||
<div class="row"> |
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/cogs.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Customization</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/wrench.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Implementation</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/lifebuoy.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Support</h6> |
|||
</div> |
|||
|
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/user.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Hire |
|||
Odoo |
|||
Developer</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/puzzle.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Integration</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/update.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Migration</h6> |
|||
</div> |
|||
|
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/consultation.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Consultancy</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/training.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Implementation</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/license.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Licensing Consultancy</h6> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<!-- END OF END OF OUR SERVICES --> |
|||
|
|||
<!-- OUR INDUSTRIES --> |
|||
|
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/corporate.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Our |
|||
Industries |
|||
</h2> |
|||
</div> |
|||
|
|||
<div class="container my-5"> |
|||
<div class="row"> |
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/trading-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Trading |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Easily procure |
|||
and |
|||
sell your products</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/pos-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
POS |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Easy |
|||
configuration |
|||
and convivial experience</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/education-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Education |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
A platform for |
|||
educational management</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/manufacturing-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Manufacturing |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Plan, track and |
|||
schedule your operations</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/ecom-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
E-commerce & Website |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Mobile |
|||
friendly, |
|||
awe-inspiring product pages</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/service-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Service Management |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Keep track of |
|||
services and invoice</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/restaurant-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Restaurant |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Run your bar or |
|||
restaurant methodically</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/hotel-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Hotel Management |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
An |
|||
all-inclusive |
|||
hotel management application</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- END OF END OF OUR INDUSTRIES --> |
|||
|
|||
<!-- SUPPORT --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/customer-support.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Support |
|||
</h2> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
|||
<div class="mr-4 d-flex justify-content-center align-items-center" |
|||
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
|||
<img src="assets/misc/support.png" height="48" width="48" |
|||
style="width: 42px; height: 42px;"/> |
|||
</div> |
|||
<div> |
|||
<h4>Need Help?</h4> |
|||
<p style="line-height: 100%;">Got questions or need help? |
|||
Get in touch.</p> |
|||
<a href="mailto:odoo@cybrosys.com"> |
|||
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;"> |
|||
odoo@cybrosys.com</p> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
|||
<div class="mr-4 d-flex justify-content-center align-items-center" |
|||
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
|||
<img src="assets/misc/whatsapp.png" height="52" width="52" |
|||
style="width: 52px; height: 52px;"/> |
|||
</div> |
|||
<div> |
|||
<h4>WhatsApp</h4> |
|||
<p style="line-height: 100%;">Say hi to us on WhatsApp!</p> |
|||
<a href="https://api.whatsapp.com/send?phone=918606827707"> |
|||
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;"> |
|||
+91 86068 |
|||
27707</p> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center"> |
|||
<img src="assets/misc/logo.png" width="144" height="31" |
|||
style="width:144px; height: 31px; margin-top: 40px;"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF SUPPORT --> |
@ -0,0 +1,50 @@ |
|||
/** @odoo-module */ |
|||
|
|||
import { ListRenderer } from "@web/views/list/list_renderer"; |
|||
|
|||
const { onWillStart, useState, useSubEnv } = owl; |
|||
|
|||
export class SaleOrderLineCompareListRenderer extends ListRenderer { |
|||
// extending the list renderer
|
|||
setup() { |
|||
super.setup(); |
|||
this.bestFields = useState({ |
|||
best_price_ids: [], |
|||
best_price_unit_ids: [], |
|||
}); |
|||
onWillStart(async () => { |
|||
await this.updateBestFields(); |
|||
}); |
|||
const defaultOnClickViewButton = this.env.onClickViewButton; |
|||
useSubEnv({ |
|||
onClickViewButton: async (params) => { |
|||
await defaultOnClickViewButton(params); |
|||
await this.updateBestFields(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
async updateBestFields() { |
|||
//to update the lines having best price and unit price
|
|||
[this.bestFields.best_price_ids, |
|||
this.bestFields.best_price_unit_ids] = await this.props.list.model.orm.call( |
|||
"sale.order", |
|||
"get_tender_best_lines", |
|||
[this.props.list.context.sale_order_id || this.props.list.context.active_id], |
|||
{ context: this.props.list.context } |
|||
); |
|||
} |
|||
|
|||
getCellClass(column, record) { |
|||
//to highlight the lines having best price and unit price
|
|||
let classNames = super.getCellClass(...arguments); |
|||
const customClassNames = []; |
|||
if (column.name === "price_subtotal" && this.bestFields.best_price_ids.includes(record.resId)) { |
|||
customClassNames.push("text-success"); |
|||
} |
|||
if (column.name === "price_unit" && this.bestFields.best_price_unit_ids.includes(record.resId)) { |
|||
customClassNames.push("text-success"); |
|||
} |
|||
return classNames.concat(" ", customClassNames.join(" ")); |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
/** @odoo-module **/ |
|||
|
|||
import { listView } from '@web/views/list/list_view'; |
|||
import { registry } from "@web/core/registry"; |
|||
import { SaleOrderLineCompareListRenderer } from "./sale_order_line_compare_list_renderer"; |
|||
export const saleOrderLineCompareListView = { |
|||
...listView, |
|||
Renderer: SaleOrderLineCompareListRenderer, |
|||
}; |
|||
// extending the list renderer
|
|||
registry.category("views").add("sale_order_line_compare", saleOrderLineCompareListView); |
@ -0,0 +1,46 @@ |
|||
/** @odoo-module */ |
|||
|
|||
import { registry } from "@web/core/registry"; |
|||
import { useService } from "@web/core/utils/hooks"; |
|||
import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; |
|||
import { ListRenderer } from "@web/views/list/list_renderer"; |
|||
|
|||
|
|||
export class FieldMany2ManyAltSosRenderer extends ListRenderer { |
|||
isCurrentRecord(record) { |
|||
return record.data.id === this.env.model.root.data.id; |
|||
} |
|||
} |
|||
|
|||
FieldMany2ManyAltSosRenderer.recordRowTemplate = "tender_sales.AltSOsListRenderer.RecordRow"; |
|||
|
|||
export class FieldMany2ManyAltSOs extends X2ManyField { |
|||
setup() { |
|||
this.orm = useService("orm"); |
|||
this.action = useService("action"); |
|||
// TODO: this is a terrible hack, make this a proper extension of many2many if/when possible
|
|||
this.props.record.activeFields[this.props.name].widget = "many2many"; |
|||
super.setup(); |
|||
} |
|||
|
|||
/** |
|||
* Override to: avoid reopening currently open record |
|||
* open record in same window w/breadcrumb extended |
|||
* @override |
|||
*/ |
|||
async openRecord(record) { |
|||
if (record.data.id !== this.props.record.data.id) { |
|||
const action = await this.orm.call(record.resModel, "get_formview_action", [[record.data.id]], { |
|||
context: this.props.context, |
|||
}); |
|||
await this.action.doAction(action); |
|||
} |
|||
} |
|||
} |
|||
|
|||
FieldMany2ManyAltSOs.components = { |
|||
...X2ManyField.components, |
|||
ListRenderer: FieldMany2ManyAltSosRenderer, |
|||
}; |
|||
|
|||
registry.category("fields").add("many2many_alt_sos", FieldMany2ManyAltSOs); |
@ -0,0 +1,3 @@ |
|||
.o_field_many2many_alt_sos { |
|||
width: 100%; |
|||
} |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates xml:space="preserve"> |
|||
|
|||
<!-- To not unlink a so from itself --> |
|||
<t t-name="tender_sales.AltSOsListRenderer.RecordRow" t-inherit="web.ListRenderer.RecordRow" t-inherit-mode="primary" owl="1"> |
|||
<xpath expr="//t[@t-if='displayOptionalFields or hasX2ManyAction']" position="attributes"> |
|||
<attribute name="t-if">(displayOptionalFields or hasX2ManyAction) and !isCurrentRecord(record)</attribute> |
|||
</xpath> |
|||
</t> |
|||
|
|||
</templates> |
@ -0,0 +1,96 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<!-- view for sale order Form Inherit--> |
|||
<record id="sale_order_form_inherit" model="ir.ui.view"> |
|||
<field name="name">sale.order.form.view.inherit</field> |
|||
<field name="model">sale.order</field> |
|||
<field name="inherit_id" ref="sale.view_order_form"/> |
|||
<field name="arch" type="xml"> |
|||
<data> |
|||
<xpath expr="//field[@name='partner_id']" position='after'> |
|||
<field name="tender_id" |
|||
attrs="{'readonly': [('state','!=','draft')]}" |
|||
domain="[('state', 'in', ('in_progress', 'open', 'ongoing')), ('customer_id', 'in', (partner_id, |
|||
False)), ('company_id', '=', company_id)]" |
|||
options="{'no_create': True}"/> |
|||
</xpath> |
|||
<xpath expr="//page[@name='customer_signature']" |
|||
position="after"> |
|||
<page string="Alternatives" name="alternative_so"> |
|||
<group> |
|||
<group> |
|||
<p colspan="2">Create alternatives. |
|||
</p> |
|||
</group> |
|||
<group> |
|||
<p colspan="2"> |
|||
<button name="action_create_alternative" |
|||
type="object" |
|||
class="btn-link d-block" |
|||
string="Create Alternative" |
|||
icon="fa-copy"/> |
|||
<button name="action_compare_alternative_lines" |
|||
type="object" |
|||
class="btn-link d-block" |
|||
string="Compare Product Lines" |
|||
icon="fa-bar-chart" |
|||
attrs="{'invisible': [('alternative_so_ids', '=', [])]}"/> |
|||
</p> |
|||
</group> |
|||
</group> |
|||
<field name="alternative_so_ids" |
|||
attrs="{'readonly': [('id', '=', False)]}" |
|||
widget="many2many_alt_sos" |
|||
context="{}" |
|||
domain="[('state', 'in', ('draft', 'sent'))]"> |
|||
<tree string="Alternative Quotation Order" |
|||
decoration-muted="state in ['cancel', 'sale', 'done']" |
|||
decoration-bf="id == parent.id"> |
|||
<control> |
|||
<create string="Link to Existing Quotations"/> |
|||
</control> |
|||
<field name="partner_id"/> |
|||
<field name="name" string="Reference"/> |
|||
<field name="amount_total"/> |
|||
<field name="state"/> |
|||
</tree> |
|||
</field> |
|||
</page> |
|||
</xpath> |
|||
</data> |
|||
</field> |
|||
</record> |
|||
<!-- view for sale order line compare--> |
|||
<record id="sale_order_line_compare_tree" model="ir.ui.view"> |
|||
<field name="name">sale.order.line.compare.tree</field> |
|||
<field name="model">sale.order.line</field> |
|||
<field name="priority">1000</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Sale Order Lines" |
|||
decoration-muted="state in ['cancel', 'purchase', 'done']" |
|||
create="0" delete="0" edit="0" expand="1" js_class="sale_order_line_compare"> |
|||
<header> |
|||
<button name="action_clear_quantities" |
|||
string="Clear Selected" type="object" |
|||
class="o_clear_qty_buttons"/> |
|||
</header> |
|||
<field name="product_id" readonly="1"/> |
|||
<field name="order_id" string="Reference" readonly="1"/> |
|||
<field name="state"/> |
|||
<field name="name" readonly="1"/> |
|||
<field name="product_uom_qty"/> |
|||
<field name="product_uom" groups="uom.group_uom"/> |
|||
<field name="price_unit"/> |
|||
<field name="price_subtotal" string="Total"/> |
|||
<field name="currency_id"/> |
|||
<button name="action_choose" string="Choose" type="object" |
|||
class="o_clear_qty_buttons" icon="fa-bullseye" |
|||
attrs="{'invisible': [('product_uom_qty', '<=', 0.0)]}"/> |
|||
<button name="action_clear_quantities" string="Clear" |
|||
type="object" class="o_clear_qty_buttons" |
|||
icon="fa-times" |
|||
attrs="{'invisible': ['|', ('product_uom_qty', '<=', 0.0), ('state', 'in', ['cancel', 'sale', 'done'])]}"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,186 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<data> |
|||
<!-- action to view sale order list-from tender form--> |
|||
<record id="action_sale_tender_list" model="ir.actions.act_window"> |
|||
<field name="name">Quotations</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="res_model">sale.order</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="domain">[('tender_id','=',active_id)]</field> |
|||
<field name="context">{ |
|||
"default_tender_id":active_id, |
|||
} |
|||
</field> |
|||
</record> |
|||
<!-- action to view sale order from tender form--> |
|||
<record id="sale_tender_action_so" model="ir.actions.act_window"> |
|||
<field name="name">Quotation</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="res_model">sale.order</field> |
|||
<field name="view_mode">form,tree</field> |
|||
<field name="domain">[('tender_id','=',active_id)]</field> |
|||
<field name="context">{ |
|||
"default_tender_id":active_id, |
|||
} |
|||
</field> |
|||
</record> |
|||
<!-- sale tender form--> |
|||
<record id="view_sale_tender_form" model="ir.ui.view"> |
|||
<field name="name">sale.tender.form</field> |
|||
<field name="model">sale.tender</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Sale Agreements"> |
|||
<field name="company_id" invisible="1"/> |
|||
<field name="currency_id" invisible="1"/> |
|||
<header> |
|||
<button name="%(sale_tender_action_so)d" |
|||
type="action" |
|||
string="New Quotation" |
|||
context="{'default_currency_id': currency_id, 'default_user_id': user_id}" |
|||
attrs="{'invisible': [('state', '!=', 'open')]}"/> |
|||
<button name="%(sale_tender_action_so)d" |
|||
type="action" |
|||
string="New Quotation" class="btn-primary" |
|||
context="{'default_currency_id': currency_id, 'default_user_id': user_id}" |
|||
attrs="{'invisible': [('state', 'not in', ('in_progress', 'ongoing'))]}"/> |
|||
<button name="action_in_progress" states="draft" |
|||
string="Confirm" type="object" |
|||
class="btn-primary"/> |
|||
<button name="action_cancel" |
|||
states="draft,in_progress,ongoing" |
|||
string="Cancel" type="object"/> |
|||
|
|||
<field name="state" widget="statusbar" |
|||
statusbar_visible="draft,in_progress,open,done"/> |
|||
</header> |
|||
<sheet> |
|||
<div class="oe_button_box" name="button_box"> |
|||
<button name="%(action_sale_tender_list)d" |
|||
type="action" class="oe_stat_button" |
|||
icon="fa-list-alt" |
|||
attrs="{'invisible': [('state', '=', 'draft')]}" |
|||
context="{'default_currency_id': currency_id}"> |
|||
<field name="order_count" widget="statinfo" |
|||
string="Quotations/Orders"/> |
|||
</button> |
|||
</div> |
|||
<div class="oe_title"> |
|||
<label for="name" class="oe_inline"/> |
|||
<h1> |
|||
<field name="name"/> |
|||
</h1> |
|||
</div> |
|||
<group> |
|||
<group> |
|||
<field name="user_id" |
|||
attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}" |
|||
domain="[('share', '=', False)]"/> |
|||
<field name="type_id" |
|||
attrs="{'readonly': [('state','!=','draft')]}"/> |
|||
<field name="customer_id" |
|||
context="{'res_partner_search_mode': 'customer'}" |
|||
attrs="{'readonly': [('state', 'in', ['ongoing','done'])]}"/> |
|||
<field name="currency_id"/> |
|||
</group> |
|||
<group> |
|||
<field name="date_end" |
|||
attrs="{'readonly': [('state','not in',('draft','in_progress','open','ongoing'))]}"/> |
|||
<field name="ordering_date" |
|||
attrs="{'readonly': [('state','not in',('draft','in_progress','open','ongoing'))]}"/> |
|||
<field name="schedule_date" |
|||
attrs="{'readonly': [('state','not in',('draft','in_progress','open','ongoing'))]}"/> |
|||
<field name="company_id" |
|||
options="{'no_create': True}" |
|||
attrs="{'readonly': [('state','not in',('draft'))]}"/> |
|||
</group> |
|||
</group> |
|||
<notebook> |
|||
<page string="Products" name="products"> |
|||
<field name="line_ids"> |
|||
<tree string="Products" editable="bottom"> |
|||
<field name="product_id" |
|||
domain="[('purchase_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"/> |
|||
<field name="product_description_variants" |
|||
attrs="{'invisible': [('product_description_variants', '=', '')], 'readonly': [('parent.state', '!=', 'draft')]}"/> |
|||
<field name="product_qty"/> |
|||
<field name="product_uom_category_id" |
|||
invisible="1"/> |
|||
<field name="product_uom_id" |
|||
string="UoM" |
|||
groups="uom.group_uom" |
|||
optional="show" |
|||
attrs="{'required': [('product_id', '!=', False)]}"/> |
|||
<field name="schedule_date" |
|||
optional="hide"/> |
|||
<field name="analytic_distribution" |
|||
widget="analytic_distribution" |
|||
optional="hide" |
|||
groups="analytic.group_analytic_accounting" |
|||
options="{'product_field': 'product_id', 'business_domain': 'purchase_order'}"/> |
|||
<field name="price_unit"/> |
|||
</tree> |
|||
<form string="Products"> |
|||
<group> |
|||
<field name="product_id" |
|||
domain="[('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"/> |
|||
<field name="product_qty"/> |
|||
<field name="product_uom_category_id" |
|||
invisible="1"/> |
|||
<field name="product_uom_id" |
|||
groups="uom.group_uom"/> |
|||
<field name="schedule_date"/> |
|||
<field name="analytic_distribution" |
|||
widget="analytic_distribution" |
|||
groups="analytic.group_analytic_accounting" |
|||
options="{'product_field': 'product_id', 'business_domain': 'sale_order'}"/> |
|||
<field name="company_id" |
|||
groups="base.group_multi_company" |
|||
options="{'no_create': True}"/> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
<separator string="Terms and Conditions"/> |
|||
<field name="description" |
|||
class="oe-bordered-editor" |
|||
attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/> |
|||
</page> |
|||
</notebook> |
|||
</sheet> |
|||
<div class="oe_chatter"> |
|||
<field name="message_follower_ids"/> |
|||
<field name="activity_ids"/> |
|||
<field name="message_ids"/> |
|||
</div> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
<!-- tree view--> |
|||
<record id="view_sale_tender_tree" model="ir.ui.view"> |
|||
<field name="name">sale.tender.tree</field> |
|||
<field name="model">sale.tender</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Sale Agreements" sample="1"> |
|||
<field name="name" decoration-bf="1"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
<!-- action for the tender--> |
|||
<record id="sale_tender_action" model="ir.actions.act_window"> |
|||
<field name="name">Sale Tenders</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="res_model">sale.tender</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="context">{}</field> |
|||
<field name="help" type="html"> |
|||
<p class="o_view_nocontent_smiling_face"> |
|||
Start a new Sale agreement |
|||
</p> |
|||
</field> |
|||
</record> |
|||
|
|||
<!-- menu item for the tender --> |
|||
<menuitem id="sale_tender_menu" sequence="10" parent="sale.sale_order_menu" |
|||
action="sale_tender_action"/> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import tender_sales_create_alternative |
@ -0,0 +1,71 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Nikhil M (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models, Command |
|||
|
|||
|
|||
class SaleTenderCreateAlternative(models.TransientModel): |
|||
""""Creating module for wizard from alternative create confirmation""" |
|||
_name = 'sale.tender.create.alternative' |
|||
_description = 'Wizard to preset values for alternative PO' |
|||
|
|||
origin_so_id = fields.Many2one( |
|||
'sale.order', help="The original PO that this alternative PO is being created for." |
|||
) |
|||
partner_id = fields.Many2one( |
|||
'res.partner', string='Customer', required=True, |
|||
help="Choose a customer for alternative sO") |
|||
|
|||
copy_products = fields.Boolean( |
|||
"Copy Products", default=True, |
|||
help="If this is checked, the product quantities of the original sO will be copied") |
|||
|
|||
def action_create_alternative(self): |
|||
""""Function to create alternative orders.""" |
|||
vals = self._get_alternative_values() |
|||
alt_so = self.env['sale.order'].with_context(origin_so_id=self.origin_so_id.id, default_tender_id=False).create(vals) |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'view_mode': 'form', |
|||
'res_model': 'sale.order', |
|||
'res_id': alt_so.id, |
|||
'context': { |
|||
'active_id': alt_so.id, |
|||
}, |
|||
} |
|||
|
|||
def _get_alternative_values(self): |
|||
""""function to return alternative values.""" |
|||
vals = { |
|||
'date_order': self.origin_so_id.date_order, |
|||
'partner_id': self.partner_id.id, |
|||
'user_id': self.origin_so_id.user_id.id, |
|||
'origin': self.origin_so_id.origin, |
|||
} |
|||
if self.copy_products and self.origin_so_id: |
|||
vals['order_line'] = [Command.create({ |
|||
'product_id': line.product_id.id, |
|||
'product_uom_qty': line.product_uom_qty, |
|||
'product_uom': line.product_uom.id, |
|||
'display_type': line.display_type, |
|||
'name': line.name, |
|||
}) for line in self.origin_so_id.order_line] |
|||
return vals |
@ -0,0 +1,27 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data> |
|||
<!-- Alternative create Form--> |
|||
<record id="sale_tender_create_alternative_form" model="ir.ui.view"> |
|||
<field name="name">Create Alternative</field> |
|||
<field name="model">sale.tender.create.alternative</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Create Alternative"> |
|||
<group> |
|||
<field name="origin_so_id" invisible="1"/> |
|||
<group> |
|||
<field name="partner_id"/> |
|||
</group> |
|||
<group> |
|||
<field name="copy_products"/> |
|||
</group> |
|||
</group> |
|||
<footer> |
|||
<button name="action_create_alternative" string="Create Alternative" data-hotkey="q" type="object" colspan="1" class="btn-primary"/> |
|||
<button string="Cancel" data-hotkey="x" special="cancel" colspan="1" class="btn-secondary"/> |
|||
</footer> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |