@ -0,0 +1,43 @@ |
|||
.. 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 |
|||
|
|||
Purchase Recurring Orders |
|||
========================= |
|||
This module allows you to create recurring orders for purchases. |
|||
|
|||
Configuration |
|||
============= |
|||
No additional configuration required |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
Developer: (V16) Unnimaya C O, |
|||
(V17) Ayana K P, |
|||
Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <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: Ayana KP (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,48 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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': 'Purchase Recurring Orders', |
|||
'version': '17.0.1.0.0', |
|||
'category': 'Inventory,Purchases', |
|||
'summary': """Helps to create Purchase Recurring orders""", |
|||
'description': """This module Helps to create recurring orders for |
|||
Purchases based on the information provided.""", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['purchase'], |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'data/ir_cron_data.xml', |
|||
'data/ir_sequence_data.xml', |
|||
'wizard/renew_wizard_views.xml', |
|||
'views/recurring_orders_views.xml', |
|||
'views/purchase_order_views.xml', |
|||
'views/res_partner_views.xml', |
|||
], |
|||
'images': ['static/description/banner.png'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,44 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Scheduled Action for Prolongation check of recurring orders |
|||
agreements--> |
|||
<record id="cron_recurring_orders_prolong_check" model="ir.cron"> |
|||
<field name="name">Prolongation Check for Recurring Orders Agreements |
|||
</field> |
|||
<field name="model_id" ref="model_purchase_recurring_agreement"/> |
|||
<field name="type">ir.actions.server</field> |
|||
<field name="state">code</field> |
|||
<field name="code">model.revise_agreements_expirations_planned() |
|||
</field> |
|||
<field name="interval_number">1</field> |
|||
<field name="doall" eval="False"/> |
|||
<field name="interval_type">days</field> |
|||
<field name="numbercall">-1</field> |
|||
</record> |
|||
<!-- Scheduled Action for Confirming current orders--> |
|||
<record id="cron_recurring_orders_confirm_orders" model="ir.cron"> |
|||
<field name="name">Confirm Current Orders</field> |
|||
<field name="model_id" ref="model_purchase_recurring_agreement"/> |
|||
<field name="type">ir.actions.server</field> |
|||
<field name="state">code</field> |
|||
<field name="code">model.confirm_current_orders_planned()</field> |
|||
<field name="interval_number">1</field> |
|||
<field name="doall" eval="False"/> |
|||
<field name="interval_type">days</field> |
|||
<field name="numbercall">-1</field> |
|||
</record> |
|||
<!-- Scheduled Action for Generating recurring orders for next |
|||
year--> |
|||
<record id="cron_recurring_orders_generate_orders" model="ir.cron"> |
|||
<field name="name">Generate Recurring Orders for Next Year</field> |
|||
<field name="model_id" ref="model_purchase_recurring_agreement"/> |
|||
<field name="type">ir.actions.server</field> |
|||
<field name="state">code</field> |
|||
<field name="code">model.generate_next_orders_planned()</field> |
|||
<field name="interval_number">1</field> |
|||
<field name="interval_type">days</field> |
|||
<field name="numbercall">-1</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,12 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Sequence Generator--> |
|||
<record id="seq_ro_agreement" model="ir.sequence"> |
|||
<field name="name">Agreement Sequence</field> |
|||
<field name="code">purchase.r_o.agreement.sequence</field> |
|||
<field name="padding">4</field> |
|||
<field name="prefix">AG-%(y)s-</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,7 @@ |
|||
## Module <purchase_recurring_orders> |
|||
|
|||
#### 15.03.2024 |
|||
#### Version 17.0.1.0.0 |
|||
#### ADD |
|||
|
|||
- Initial commit for Purchase Recurring Orders |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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 purchase_agreement_renewal |
|||
from . import purchase_order |
|||
from . import purchase_recurring_agreement |
|||
from . import recurring_agreement_line |
@ -0,0 +1,35 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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 |
|||
|
|||
|
|||
class PurchaseAgreementRenewal(models.Model): |
|||
"""Renew purchase recurring agreement""" |
|||
_name = 'purchase.agreement.renewal' |
|||
_description = "Purchase Agreement Renewal" |
|||
|
|||
recurring_agreement_id = fields.Many2one('purchase.recurring.agreement', |
|||
string='Agreement Reference', |
|||
ondelete='cascade') |
|||
date = fields.Datetime(string='Date', help="Date of the Renewal") |
|||
comments = fields.Char( |
|||
string='Comments', size=200, help='Renewal comments') |
@ -0,0 +1,98 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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 |
|||
|
|||
|
|||
class PurchaseOrder(models.Model): |
|||
"""purchase Order Inherited""" |
|||
_inherit = 'purchase.order' |
|||
|
|||
@api.model |
|||
def _prepare_agreement_vals(self, order): |
|||
""" Method for creating agreement values""" |
|||
return { |
|||
'name': order.name, |
|||
'partner_id': order.partner_id.id, |
|||
'company_id': order.company_id.id, |
|||
'start_date': fields.Datetime.now(), |
|||
} |
|||
|
|||
@api.model |
|||
def _prepare_agreement_line_vals(self, order_ids, agreement): |
|||
""" Returns the Agreement Line Values in a Dictionary Format""" |
|||
return { |
|||
'recurring_agreement_id': agreement.id, |
|||
'product_id': order_ids.product_id.id, |
|||
'quantity': order_ids.product_qty, |
|||
} |
|||
|
|||
def action_button_generate_agreement(self): |
|||
"""Generates Purchase Recurring Agreement""" |
|||
agreements = [] |
|||
agreement_obj = self.env['purchase.recurring.agreement'] |
|||
agreement_line_obj = self.env['recurring.agreement.line'] |
|||
for purchase_order in self: |
|||
agreement_vals = self._prepare_agreement_vals(purchase_order) |
|||
agreement = agreement_obj.create(agreement_vals) |
|||
agreements.append(agreement) |
|||
for order_id in purchase_order.order_line: |
|||
agreement_line_vals = self._prepare_agreement_line_vals( |
|||
order_id, agreement) |
|||
agreement_line_obj.create(agreement_line_vals) |
|||
if len(agreements) == 1: |
|||
view = self.env.ref( |
|||
'purchase_recurring_orders.' |
|||
'purchase_recurring_agreement_view_form') |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'view_mode': 'form', |
|||
'res_model': 'purchase.recurring.agreement', |
|||
'views': [(view.id, 'form')], |
|||
'view_id': view.id, |
|||
'target': 'new', |
|||
'res_id': agreement[0].id, |
|||
'nodestroy': True, |
|||
} |
|||
return True |
|||
|
|||
from_agreement = fields.Boolean( |
|||
string='From Agreement?', copy=False, |
|||
help='This field indicates if the purchase order comes from ' |
|||
'an agreement.') |
|||
recurring_agreement_id = fields.Many2one('purchase.recurring.agreement', |
|||
string='Agreement Reference', |
|||
help="this indicates the Purchase " |
|||
"Agreement", |
|||
ondelete='restrict') |
|||
|
|||
def view_order(self): |
|||
"""Returns the Corresponding Order""" |
|||
return { |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': 'purchase.order', |
|||
'context': self.env.context, |
|||
'res_id': self[:1].id, |
|||
'view_id': [self.env.ref('purchase.purchase_order_form').id], |
|||
'type': 'ir.actions.act_window', |
|||
'nodestroy': True |
|||
} |
@ -0,0 +1,421 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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 datetime import timedelta |
|||
from dateutil.relativedelta import relativedelta |
|||
from odoo import api, exceptions, fields, models, _ |
|||
|
|||
|
|||
class PurchaseRecurringAgreement(models.Model): |
|||
"""Model for generating purchase recurring agreement""" |
|||
_name = 'purchase.recurring.agreement' |
|||
_inherit = 'mail.thread' |
|||
_description = "Purchase Recurring Agreement" |
|||
|
|||
@api.model |
|||
def _get_next_term_date(self, date, unit, interval): |
|||
"""Returns the Next Term Date""" |
|||
if unit == 'days': |
|||
date = date + timedelta(days=interval) |
|||
elif unit == 'weeks': |
|||
date = date + timedelta(weeks=interval) |
|||
elif unit == 'months': |
|||
date = date + relativedelta(months=interval) |
|||
elif unit == 'years': |
|||
date = date + relativedelta(years=interval) |
|||
return date |
|||
|
|||
def _compute_next_expiration_date(self): |
|||
"""Calculates the Next Expiration Date According to the Prolongation |
|||
Unit Chosen""" |
|||
for agreement in self: |
|||
if agreement.prolong == 'fixed': |
|||
agreement.next_expiration_date = agreement.end_date |
|||
elif agreement.prolong == 'unlimited': |
|||
now = fields.Date.from_string(fields.Datetime.today()) |
|||
date = self._get_next_term_date( |
|||
fields.Date.from_string(agreement.start_date), |
|||
agreement.prolong_unit, agreement.prolong_interval) |
|||
while date < now: |
|||
date = self._get_next_term_date( |
|||
date, agreement.prolong_unit, |
|||
agreement.prolong_interval) |
|||
agreement.next_expiration_date = date |
|||
else: |
|||
agreement.next_expiration_date = self._get_next_term_date( |
|||
fields.Datetime.from_string( |
|||
agreement.last_renovation_date or |
|||
agreement.start_date), |
|||
agreement.prolong_unit, agreement.prolong_interval) |
|||
|
|||
def _default_company_id(self): |
|||
"""Returns the Current Company Id""" |
|||
company_model = self.env['res.company'] |
|||
company_id = company_model._company_default_get('purchase') |
|||
return company_model.browse(company_id.id) |
|||
|
|||
name = fields.Char( |
|||
string='Name', size=100, index=True, required=True, |
|||
help='Name that Helps to Identify the Agreement') |
|||
number = fields.Char( |
|||
string='Agreement Number', index=True, size=32, copy=False, |
|||
help="Number of Agreement. Keep Empty to Get the Number Assigned by a " |
|||
"Sequence.") |
|||
active = fields.Boolean( |
|||
string='Active', default=True, |
|||
help='Uncheck this Field, Quotas are not Generated') |
|||
partner_id = fields.Many2one('res.partner', string='Supplier', index=True, |
|||
change_default=True, required=True, |
|||
help="Supplier You are Making the " |
|||
"Agreement with") |
|||
company_id = fields.Many2one('res.company', string='Company', required=True, |
|||
help="Company that Signs the Agreement", |
|||
default=_default_company_id) |
|||
start_date = fields.Date( |
|||
string='Start Date', index=True, copy=False, |
|||
help="Beginning of the Agreement. Keep Empty to Use the Current Date") |
|||
prolong = fields.Selection( |
|||
selection=[('recurrent', 'Renewable Fixed Term'), |
|||
('unlimited', 'Unlimited Term'), |
|||
('fixed', 'Fixed Term')], |
|||
string='Prolongation', default='unlimited', |
|||
help="Sets the term of the agreement. 'Renewable fixed term': It sets " |
|||
"a fixed term, but with possibility of manual renew; 'Unlimited " |
|||
"term': Renew is made automatically; 'Fixed term': The term is " |
|||
"fixed and there is no possibility to renew.") |
|||
end_date = fields.Date( |
|||
string='End date', help="End Date of the Agreement") |
|||
prolong_interval = fields.Integer( |
|||
string='Interval', default=1, |
|||
help="Interval in time units to prolong the agreement until new " |
|||
"renewable (that is automatic for unlimited term, manual for " |
|||
"renewable fixed term).") |
|||
prolong_unit = fields.Selection( |
|||
selection=[('days', 'Days'), |
|||
('weeks', 'Weeks'), |
|||
('months', 'Months'), |
|||
('years', 'Years')], |
|||
string='Interval Unit', default='years', |
|||
help='Time unit for the prolongation interval') |
|||
agreement_line_ids = fields.One2many('recurring.agreement.line', |
|||
inverse_name='recurring_agreement_id', |
|||
string='Agreement Lines') |
|||
order_ids = fields.One2many('purchase.order', copy=False, |
|||
inverse_name='recurring_agreement_id', |
|||
string='Orders', readonly=True) |
|||
renewal_ids = fields.One2many('purchase.agreement.renewal', copy=False, |
|||
inverse_name='recurring_agreement_id', |
|||
string='Renewal Lines', |
|||
readonly=True) |
|||
last_renovation_date = fields.Datetime( |
|||
string='Last Renovation Date', |
|||
help="Last date when agreement was renewed (same as start date if not " |
|||
"renewed)") |
|||
next_expiration_date = fields.Datetime( |
|||
compute="_compute_next_expiration_date", |
|||
help="Date when agreement will expired ", |
|||
string='Next Expiration Date') |
|||
state = fields.Selection( |
|||
selection=[('empty', 'Without Orders'), |
|||
('first', 'First Order Created'), |
|||
('orders', 'With Orders')], |
|||
string='State', help="Indicates the state of recurring agreement", |
|||
readonly=True, default='empty') |
|||
renewal_state = fields.Selection( |
|||
selection=[('not_renewed', 'Agreement not Renewed'), |
|||
('renewed', 'Agreement Renewed')], |
|||
string='Renewal State', |
|||
help="Renewal Status of the Recurring agreement", readonly=True, |
|||
default='not_renewed') |
|||
notes = fields.Text('Notes', help="Notes regarding Renewal agreement") |
|||
order_count = fields.Integer(compute='_compute_order_count', |
|||
help="Indicates the No. of Orders Generated " |
|||
"with this Agreement") |
|||
|
|||
_sql_constraints = [ |
|||
('number_uniq', 'unique(number)', 'Agreement Number Must be Unique !'), |
|||
] |
|||
|
|||
def get_orders(self): |
|||
"""Returns All Orders Generated from the Agreement""" |
|||
self.ensure_one() |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'Orders', |
|||
'views': [[False, 'tree'], [False, 'form']], |
|||
'res_model': 'purchase.order', |
|||
'domain': [('recurring_agreement_id', '=', self.id)], |
|||
'context': "{'create': False}" |
|||
} |
|||
|
|||
def _compute_order_count(self): |
|||
"""Finds the count of orders generated from the Agreement""" |
|||
for record in self: |
|||
record.order_count = self.env['purchase.order'].search_count( |
|||
[('recurring_agreement_id', '=', self.id)]) |
|||
|
|||
@api.constrains('start_date', 'end_date') |
|||
def _check_dates(self): |
|||
"""Method for ensuring start date will be always less than |
|||
or equal to end date""" |
|||
for record in self: |
|||
if record.end_date and record.end_date < record.start_date: |
|||
raise exceptions.Warning( |
|||
_('Agreement End Date must be Greater than Start Date')) |
|||
|
|||
@api.model |
|||
def create(self, vals): |
|||
"""Function that supering create function""" |
|||
if not vals.get('start_date'): |
|||
vals['start_date'] = fields.Datetime.today() |
|||
if not vals.get('number'): |
|||
vals['number'] = self.env['ir.sequence'].get( |
|||
'purchase.r_o.agreement.sequence') |
|||
return super().create(vals) |
|||
|
|||
def write(self, vals): |
|||
"""Function that supering write function""" |
|||
value = super().write(vals) |
|||
if (any(vals.get(rec) is not None for rec in |
|||
['active', 'number', 'agreement_line_ids', 'prolong', |
|||
'end_date', |
|||
'prolong_interval', 'prolong_unit', 'partner_id'])): |
|||
self.unlink_orders(fields.Datetime.today()) |
|||
return value |
|||
|
|||
@api.returns('self', lambda value: value.id) |
|||
def copy(self, default=None): |
|||
default = dict(default or {}) |
|||
if 'name' not in default: |
|||
default['name'] = _("%s (Copy)") % self.name |
|||
return super().copy(default=default) |
|||
|
|||
def unlink(self): |
|||
"""Function that supering unlink function which will unlink Self and |
|||
the Current record""" |
|||
for agreement in self: |
|||
if any(agreement.mapped('order_ids')): |
|||
raise exceptions.Warning( |
|||
_('You Cannot Remove Agreements with Confirmed Orders!')) |
|||
self.unlink_orders(fields.Datetime.from_string(fields.Datetime.today())) |
|||
return models.Model.unlink(self) |
|||
|
|||
@api.onchange('start_date') |
|||
def onchange_start_date(self, start_date=False): |
|||
"""Method for updating last renovation date""" |
|||
if not start_date: |
|||
return {} |
|||
result = {'value': {'last_renovation_date': start_date}} |
|||
return result |
|||
|
|||
@api.model |
|||
def revise_agreements_expirations_planned(self): |
|||
"""Method for changing the prolong as unlimited""" |
|||
for agreement in self.search([('prolong', '=', 'unlimited')]): |
|||
if agreement.next_expiration_date <= fields.Datetime.today(): |
|||
agreement.write({'prolong': 'unlimited'}) |
|||
return True |
|||
|
|||
@api.model |
|||
def _prepare_purchase_order_vals(self, agreement, date): |
|||
"""Creates purchase order values""" |
|||
# Order Values |
|||
order_vals = {'date_order': date, 'origin': agreement.number, |
|||
'partner_id': agreement.partner_id.id, |
|||
'state': 'draft', 'company_id': agreement.company_id.id, |
|||
'from_agreement': True, |
|||
'recurring_agreement_id': agreement.id, |
|||
'date_planned': date, |
|||
'fiscal_position_id': self.env[ |
|||
'account.fiscal.position'].with_context( |
|||
company_id=agreement.company_id.id). |
|||
_get_fiscal_position(agreement.partner_id), |
|||
'payment_term_id': agreement.partner_id. |
|||
property_supplier_payment_term_id.id, |
|||
'currency_id': agreement.partner_id. |
|||
property_purchase_currency_id.id or |
|||
self.env.user.company_id.currency_id.id, |
|||
'user_id': agreement.partner_id.user_id.id} |
|||
return order_vals |
|||
|
|||
@api.model |
|||
def _prepare_purchase_order_line_vals(self, agreement_line_ids, order): |
|||
"""Returns the Purchase Order Line Values as a Dictionary Which can be |
|||
Used While creating the Purchase Order""" |
|||
product_lang = agreement_line_ids.product_id.with_context({ |
|||
'lang': order.partner_id.lang, |
|||
'partner_id': order.partner_id.id, |
|||
}) |
|||
fpos = order.fiscal_position_id |
|||
# Order Line Values as a Dictionary |
|||
order_line_vals = { |
|||
'order_id': order.id, |
|||
'product_id': agreement_line_ids.product_id.id, |
|||
'product_qty': agreement_line_ids.quantity, |
|||
'date_planned': order.date_planned, |
|||
'price_unit': agreement_line_ids.product_id. |
|||
_get_tax_included_unit_price( |
|||
order.company_id, |
|||
order.currency_id, |
|||
order.date_order, |
|||
'purchase', |
|||
fiscal_position=order.fiscal_position_id, |
|||
product_uom=agreement_line_ids.product_id.uom_po_id), |
|||
'product_uom': agreement_line_ids.product_id.uom_po_id.id or |
|||
agreement_line_ids.product_id.uom_id.id, |
|||
'name': product_lang.display_name, |
|||
'taxes_id': fpos.map_tax( |
|||
agreement_line_ids.product_id.supplier_taxes_id.filtered( |
|||
lambda r: r.company_id.id == self.company_id.id).ids) |
|||
} |
|||
# product price changed if specific price is added |
|||
if agreement_line_ids.specific_price: |
|||
order_line_vals['price_unit'] = agreement_line_ids.specific_price |
|||
order_line_vals['taxes_id'] = [ |
|||
(6, 0, tuple(order_line_vals['taxes_id']))] |
|||
# product price changed if specific price is added |
|||
if agreement_line_ids.additional_description: |
|||
order_line_vals['name'] += " %s" % ( |
|||
agreement_line_ids.additional_description) |
|||
return order_line_vals |
|||
|
|||
def create_order(self, date, agreement_lines): |
|||
"""Create Purchase Order from Recurring Agreement """ |
|||
self.ensure_one() |
|||
order_line_obj = self.env['purchase.order.line'].with_context( |
|||
company_id=self.company_id.id) |
|||
order_vals = self._prepare_purchase_order_vals(self, date) |
|||
order = self.env['purchase.order'].create(order_vals) |
|||
for agreement_line in agreement_lines: |
|||
# Create Purchase Order Line Values |
|||
order_line_vals = self._prepare_purchase_order_line_vals( |
|||
agreement_line, order) |
|||
order_line_obj.create(order_line_vals) |
|||
agreement_lines.write({'last_order_date': fields.Datetime.today()}) |
|||
if self.state != 'orders': |
|||
self.state = 'orders' |
|||
return order |
|||
|
|||
def _get_next_order_date(self, line, start_date): |
|||
"""Return The date of Next Purchase order generated from the |
|||
Agreement""" |
|||
self.ensure_one() |
|||
next_date = fields.Datetime.from_string(self.start_date) |
|||
while next_date <= start_date: |
|||
next_date = self._get_next_term_date( |
|||
next_date, line.ordering_unit, line.ordering_interval) |
|||
return next_date |
|||
|
|||
def generate_agreement_orders(self, start_date, end_date): |
|||
"""Method for generating agreement orders""" |
|||
self.ensure_one() |
|||
if not self.active: |
|||
return |
|||
lines_to_order = {} |
|||
# Get next expiration date |
|||
exp_date = fields.Datetime.from_string(self.next_expiration_date) |
|||
if exp_date < end_date and self.prolong != 'unlimited': |
|||
end_date = exp_date |
|||
for line in self.agreement_line_ids: |
|||
if not line.active_chk: |
|||
continue |
|||
# Get Date of Next Order |
|||
next_order_date = self._get_next_order_date(line, start_date) |
|||
while next_order_date <= end_date: |
|||
if not lines_to_order.get(next_order_date): |
|||
lines_to_order[next_order_date] = self.env[ |
|||
'recurring.agreement.line'] |
|||
lines_to_order[next_order_date] |= line |
|||
next_order_date = self._get_next_order_date( |
|||
line, next_order_date) |
|||
dates = lines_to_order.keys() |
|||
sorted(dates) |
|||
for date in dates: |
|||
order = self.order_ids.filtered( |
|||
lambda x: ( |
|||
fields.Date.to_string( |
|||
fields.Datetime.from_string(x.date_order)) == |
|||
fields.Date.to_string(date))) |
|||
if not order: |
|||
self.create_order( |
|||
fields.Datetime.to_string(date), lines_to_order[date]) |
|||
|
|||
def generate_initial_order(self): |
|||
"""This will generate the Initial purchase Order from the Purchase |
|||
Agreement""" |
|||
self.ensure_one() |
|||
agreement_lines = self.mapped('agreement_line_ids').filtered( |
|||
'active_chk') |
|||
order = self.create_order(self.start_date, agreement_lines) |
|||
self.write({'state': 'first'}) |
|||
order.button_confirm() |
|||
return { |
|||
'domain': "[('id', '=', %s)]" % order.id, |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': 'purchase.order', |
|||
'context': self.env.context, |
|||
'res_id': order.id, |
|||
'view_id': [self.env.ref('purchase.purchase_order_form').id], |
|||
'type': 'ir.actions.act_window', |
|||
'nodestroy': True |
|||
} |
|||
|
|||
@api.model |
|||
def generate_next_orders_planned(self, years=1, start_date=None): |
|||
"""Method for generating the planned orders""" |
|||
if start_date: |
|||
start_date = fields.Datetime.from_string(start_date) |
|||
self.search([]).generate_next_orders( |
|||
years=years, start_date=start_date) |
|||
|
|||
def generate_next_year_orders(self): |
|||
"""This will Generate Orders for Next year""" |
|||
return self.generate_next_orders(years=1) |
|||
|
|||
def generate_next_orders(self, years=1, start_date=None): |
|||
if not start_date: |
|||
start_date = fields.Datetime.from_string(fields.Date.today()) |
|||
end_date = start_date + relativedelta(years=years) |
|||
for agreement in self: |
|||
agreement.generate_agreement_orders(start_date, end_date) |
|||
return True |
|||
|
|||
@api.model |
|||
def confirm_current_orders_planned(self): |
|||
"""This will Confirm All Orders satisfying the Domain""" |
|||
tomorrow = fields.Date.to_string( |
|||
fields.Datetime.from_string(fields.Datetime.today()) + timedelta( |
|||
days=1)) |
|||
orders = self.env['purchase.order'].search([ |
|||
('recurring_agreement_id', '!=', False), |
|||
('state', 'in', ('draft', 'sent')), |
|||
('date_order', '<', tomorrow) |
|||
]) |
|||
for order in orders: |
|||
order.signal_workflow('order_confirm') |
|||
|
|||
def unlink_orders(self, start_date): |
|||
""" Remove the relation between ``self`` and the related record.""" |
|||
orders = self.mapped('order_ids').filtered( |
|||
lambda x: (x.state in ('draft', 'sent') and |
|||
x.date_order >= start_date)) |
|||
orders.unlink() |
@ -0,0 +1,91 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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.addons.base.models.decimal_precision import dp |
|||
|
|||
|
|||
class RecurringAgreementLine(models.Model): |
|||
"""Model generating purchase recurring agreement line""" |
|||
_name = 'recurring.agreement.line' |
|||
_description = 'Recurring Agreement Line' |
|||
|
|||
active_chk = fields.Boolean( |
|||
string='Active', default=True, |
|||
help='Unchecking this field, this quota is not generated') |
|||
recurring_agreement_id = fields.Many2one( |
|||
'purchase.recurring.agreement', |
|||
string='Agreement Reference', |
|||
help="The Corresponding Purchase Order Agreement", |
|||
ondelete='cascade') |
|||
product_id = fields.Many2one('product.product', string='Product', |
|||
ondelete='restrict', |
|||
required=True) |
|||
uom_id = fields.Many2one(related='product_id.product_tmpl_id.uom_id', |
|||
help="UOM of the product", string="Uom") |
|||
|
|||
name = fields.Char( |
|||
related="product_id.name", string='Description', |
|||
help="Description of the Product") |
|||
additional_description = fields.Char( |
|||
string='Description', size=30, |
|||
help='Additional description that will be added to the product ' |
|||
'description on orders.') |
|||
quantity = fields.Float( |
|||
string='Quantity', required=True, help='Quantity of the Product', |
|||
default=1.0) |
|||
ordering_interval = fields.Integer( |
|||
string='Interval', required=True, default=1, |
|||
help="Interval in time units for making an order of this product") |
|||
ordering_unit = fields.Selection( |
|||
selection=[('days', 'Days'), |
|||
('weeks', 'Weeks'), |
|||
('months', 'Months'), |
|||
('years', 'Years')], |
|||
string='Interval Unit', required=True, |
|||
help="It indicated the Recurring Time Unit", default='months') |
|||
last_order_date = fields.Datetime( |
|||
string='Last Order', help='Date of the last Purchase order generated') |
|||
specific_price = fields.Float( |
|||
string='Specific Price', |
|||
digits_compute=dp.get_precision('Purchase Price'), |
|||
help='Specific price for this product. Keep empty to use the list ' |
|||
'price while generating order') |
|||
list_price = fields.Float( |
|||
related='product_id.list_price', string="List Price", readonly=True, |
|||
help='Price of product in purchase order lines') |
|||
|
|||
_sql_constraints = [ |
|||
('line_qty_zero', 'CHECK (quantity > 0)', |
|||
'All product quantities must be greater than 0.\n'), |
|||
('line_interval_zero', 'CHECK (ordering_interval > 0)', |
|||
'All ordering intervals must be greater than 0.\n'), |
|||
] |
|||
|
|||
@api.onchange('product_id') |
|||
def onchange_product_id(self, product_id=False): |
|||
"""For getting product name""" |
|||
result = {} |
|||
if product_id: |
|||
product = self.env['product.product'].browse(product_id) |
|||
if product: |
|||
result['value'] = {'name': product['name']} |
|||
return result |
|
After Width: | Height: | Size: 36 KiB |
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.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 11 KiB |
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: 3.2 KiB |
After Width: | Height: | Size: 565 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 401 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,661 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Odoo App 3 Index</title> |
|||
<!-- Bootstrap CSS --> |
|||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" |
|||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css"> |
|||
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"> |
|||
</head> |
|||
<body> |
|||
<section> |
|||
<div class="container" style="font-family: 'Inter', sans-serif !important;background-color: #fff !important;"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-12 col-lg-12 d-flex justify-content-between flex-wrap align-items-sm-center" |
|||
style="border-bottom:1px solid rgba(0, 0, 0, 0.22)"> |
|||
<div class="my-3"> |
|||
<img src="assets/misc/Cybrosys R.png" style="width:auto !important; height:40px !important"> |
|||
</div> |
|||
<div class="my-3 d-flex align-items-center"> |
|||
<div class="text-center" |
|||
style="background-color:#017E84 !important;font-size: 0.8rem !important; color:#fff !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;"> |
|||
Community |
|||
</div> |
|||
<div class="text-center" |
|||
style="background-color:#875A7B !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important;min-width: 120px !important;"> |
|||
Enterprise |
|||
</div> |
|||
<div class="text-center" |
|||
style="background-color:#7C7BAD !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;"> |
|||
Odoo.sh |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-12 col-lg-12 text-center d-flex align-items-center flex-column" |
|||
style="margin: 80px 0px !important;"> |
|||
<h1 style="font-size: 2.8rem;font-weight: 700; color: |
|||
#1A202C;"> |
|||
Purchase Recurring Orders</h1> |
|||
<p class="my-3 mb-4" |
|||
style="max-width: 80%; font-weight: 400 !important; line-height: 32px; color: #718096;"> |
|||
Create Recurring Orders for Purchases Orders. |
|||
</p> |
|||
<div style="width: 80%; margin-top: 3rem;"> |
|||
<img src="assets/screenshots/hero-v17.gif" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5 mb-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4"> |
|||
<p class="m-0" style="font-weight: 600; font-size: 24px; color:#714b67 !important">Key Highlights |
|||
</p> |
|||
</div> |
|||
<div class="row py-4"> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px; |
|||
background: #FFF; |
|||
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); "> |
|||
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67; |
|||
display: flex; justify-content: center; align-items: center; |
|||
margin-right: 10px; flex-shrink: 0;"> |
|||
<i class="fa-solid fa-star " style="color: #fff;font-size:14px;"></i> |
|||
</div> |
|||
<div> |
|||
<p style="color: #1A202C;font-weight: 600; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Creates Recurring Orders for Purchase.</p> |
|||
<p class="m-0" style="color:#718096">Generates Recurring Orders for purchase orders. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px; |
|||
background: #FFF; |
|||
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); "> |
|||
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67; |
|||
display: flex; justify-content: center; align-items: center; |
|||
margin-right: 10px; flex-shrink: 0;"> |
|||
<i class="fa-solid fa-star " style="color: #fff;font-size:14px;"></i> |
|||
</div> |
|||
<div> |
|||
<p style="color: #1A202C;font-weight: 600; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Recurring Order Agreements.</p> |
|||
<p class="m-0" style="color:#718096">Generates Recurring Orders Agreements for purchase. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px; |
|||
background: #FFF; |
|||
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); "> |
|||
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67; |
|||
display: flex; justify-content: center; align-items: center; |
|||
margin-right: 10px; flex-shrink: 0;"> |
|||
<i class="fa-solid fa-star " style="color: #fff;font-size:14px;"></i> |
|||
</div> |
|||
<div> |
|||
<p style="color: #1A202C;font-weight: 600; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Create Next Year Orders at one click.</p> |
|||
<p class="m-0" style="color:#718096">It is possible to create next year orders at one click. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px; |
|||
background: #FFF; |
|||
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); "> |
|||
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67; |
|||
display: flex; justify-content: center; align-items: center; |
|||
margin-right: 10px; flex-shrink: 0;"> |
|||
<i class="fa-solid fa-star " style="color: #fff;font-size:14px;"></i> |
|||
</div> |
|||
<div> |
|||
<p style="color: #1A202C;font-weight: 600; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Scheduled action for creating Recurring Orders.</p> |
|||
<p class="m-0" style="color:#718096">Can Automatically generate recurring orders on scheduled time. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container rounded" > |
|||
<ul class="nav nav-tabs d-flex" style="width: fit-content;margin: 0 auto;gap: 1rem;"> |
|||
<li class="col text-center py-2 text-nowrap " |
|||
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;"><a |
|||
class="active show" data-toggle="tab" href="#tab1" |
|||
style="color: #fff;font-weight: 500; background-color: #714B67; text-decoration: none;"> |
|||
<i class="fa-regular fa-image pr-2" style="color: #fff;"></i> |
|||
Screenshots</a></li> |
|||
<li class="col text-center py-2 text-nowrap " |
|||
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;"><a |
|||
data-toggle="tab" href="#tab2" |
|||
style="color: #fff;font-weight: 500; text-decoration: none;"><i |
|||
class="fa-solid fa-star pr-2" style="color: #fff;"></i>Features</a></li> |
|||
<li class="col text-center py-2 text-nowrap " |
|||
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;"><a |
|||
data-toggle="tab" href="#tab3" |
|||
style="color: #fff;font-weight: 500; text-decoration: none; background-color: #714B67;"><i |
|||
class="fa-solid fa-book-open pr-2" style="color: #fff;"></i>Released Notes</a></li> |
|||
</ul> |
|||
<div class="tab-content" style="background-color: rgba(121, 113, 119, 0.04);"> |
|||
<div id="tab1" class="tab-pane fade in active show"> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/1.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
Menu for viewing and creating Recurring Order Agreements.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/2.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
Create new recurring order agreement by filling the details in the form and add the products in Agreement Order Line. Click on the Generate Initial Order button for generating the first order</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/3.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
First Purchase Order Generated according to the details added in the Recurring purchase agreement.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/4.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
We can see all orders created from the recurring purchase agreement from the smart button which shows the number of orders.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/6.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
It is possible to create next year orders at one click.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/7.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
The Number of Orders are increased.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/8.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
All orders generated including Initial and Next year Orders.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/9.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
If prolongation type changed to Renewable then a button appears for Renew Agreement.</h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-12 py-2" style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/10.png" class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
Scheduled Action for generating Recurring Orders for Next Year </h4> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div id="tab2" class="tab-pane fade"> |
|||
<div class="col-mg-12" style="padding: 1rem 4rem;"> |
|||
<ul style="list-style: none; padding: 1rem 0;font-weight: 500;"> |
|||
<li class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<span style="margin-right: 12px;"><img src="assets/misc/star (1) 2.svg" alt="" |
|||
width="16px"></span>Creates recurring orders for purchas |
|||
</li> |
|||
<li class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<span style="margin-right: 12px;"><img src="assets/misc/star (1) 2.svg" alt="" |
|||
width="16px"></span>Scheduled action for creating recurring orders. |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<div id="tab3" class="tab-pane fade"> |
|||
<div class="col-mg-12 active" style="padding: 1rem 4rem;"> |
|||
<div class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="d-flex mb-3" style="font-size: 0.8rem; font-weight: 500;"><span>Version |
|||
17.0.1.0.0</span><span class="px-2">|</span><span |
|||
style="color: #714B67;font-weight: 600;">Released on:22th January 2024</span> |
|||
</div> |
|||
<p class="m-0" |
|||
style=" color:#718096!important; font-size:1rem !important;line-height: 28px;"> |
|||
|
|||
Initial Commit for Purchase Recurring Orders.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5"> |
|||
<p class="m-0" style="font-weight: 600; font-size: 24px; color:#000 !important">Related Products</p> |
|||
</div> |
|||
</div> |
|||
<div id="myCarousel" class="carousel slide py-3" data-ride="carousel"> |
|||
<div class="carousel-inner"> |
|||
<div class="carousel-item active"> |
|||
<div class="row p-4"> |
|||
<div class="col-md-4"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/purchase_product_history/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/module_image.jpeg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Purchase History Of Products</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/merge_rfq/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/module_image (1).jpeg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Merge RFQ</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/sale_purchase_previous_product_cost/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/module_image (2).jpeg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Previous Sale/Purchase Product Rates</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="carousel-item"> |
|||
<div class="row p-4"> |
|||
<div class="col-md-4"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/product_multi_attachment/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/module_image (4).jpeg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Product Multi Attachment</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/purchase_orderline_image/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px;"> |
|||
<img src="assets/modules/module_image (5).jpeg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Purchase Order Line Images</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/purchase_order_delivery_status/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/module_image (3).jpg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Delivery Status on Purchase Order</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
<a class="carousel-control-prev" href="#myCarousel" 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="#myCarousel" 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 class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4"> |
|||
<p class="m-0" style="font-weight: 600; font-size: 24px; color:#000 !important">Our Services</p> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="container my-5"> |
|||
<div class="row py-3"> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#13EA36 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/cogs.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Customization</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#DBC711; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/wrench.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Implementation</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#FF6B6B ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/lifebuoy.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Support</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#FFA801 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/user.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Hire Odoo Developer</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;"> |
|||
|
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#54A0FF; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/puzzle.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Integration</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#6D7680 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/update.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Migration</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#786FA6 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/consultation.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Consultancy</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#F8A5C2 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/training.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Implementation</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#E6BE26; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/license.png" alt="service-icon" width="38px" height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo Licensing Consultancy</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4"> |
|||
<p class="m-0" style="font-weight: 600; font-size: 24px; color:#000 !important">Our Industries</p> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="container"> |
|||
<div class="row my-5 py-4"> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100 " |
|||
style="border-right: 1px solid rgb(209, 209, 209); border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; box-shadow: 6px 0 10px rgba(228, 227, 227, 0.373);"> |
|||
<img src="assets/icons/trading-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Trading</p> |
|||
<p>Easily procure and sell your products</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgb(209, 209, 209); padding: 30px;"> |
|||
<img src="assets/icons/pos-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">POS</p> |
|||
<p>Easy configuration and convivial experience</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgba(0, 0, 0, 0.2); padding: 30px; box-shadow: 0 5px 10px rgba(228, 227, 227, 0.373)"> |
|||
<img src="assets/icons/education-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Education</p> |
|||
<p>A platform for educational management</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; "> |
|||
<img src="assets/icons/manufacturing-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Manufacturing</p> |
|||
<p>Plan, track and schedule your operations</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;"> |
|||
<img src="assets/icons/ecom-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">E-commerce & Website</p> |
|||
<p>Mobile friendly, awe-inspiring product pages</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;box-shadow: 0 -5px 10px rgba(228, 227, 227, 0.373);"> |
|||
<img src="assets/icons/service-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Service Management</p> |
|||
<p>Keep track of services and invoice</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px; "> |
|||
<img src="assets/icons/restaurant-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Restaurant</p> |
|||
<p>Run your bar or restaurant methodically</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style=" padding: 30px;box-shadow: -5px 0 10px rgba(228, 227, 227, 0.373);"> |
|||
<img src="assets/icons/hotel-black.png" width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Hotel Management</p> |
|||
<p>An all-inclusive hotel management application</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5"> |
|||
<p class="m-0" style="font-weight: 600; font-size: 24px; color:#000 !important">Support</p> |
|||
</div> |
|||
</div> |
|||
<div class="container my-5"> |
|||
<div class="row" style="background-color: #FFFAFE;"> |
|||
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center" |
|||
style="border-right: 1px solid #D9D9D9;"> |
|||
<div style="padding: 30px;"> |
|||
<div class="d-flex align-items-center"> |
|||
<img src="assets/misc/support (1) 1.svg" alt="" width="60px" style="margin-right: 12px;"> |
|||
<div style="padding: 0px 8px;"> |
|||
<span |
|||
style="color: #714B67;font-size: 24px;font-weight: 600;padding-bottom: 1rem;">Need |
|||
Help?</span> |
|||
<p class="m-0" style="color:#718096;">Got questions or need help? Get in touch.</p> |
|||
<div style="font-weight: 400;"><span><img src="assets/misc/support-email.svg" alt="" |
|||
width="18px" |
|||
style="filter: invert(1);margin-right: 0.8rem;"></span>odoo@cybrosys.com |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center"> |
|||
<div style="padding: 30px;"> |
|||
<div class="d-flex align-items-center"> |
|||
<img src="assets/misc/whatsapp 1.svg" alt="" width="60px" style="margin-right: 12px;"> |
|||
<div> |
|||
<span style="color: #714B67;font-size: 24px;font-weight: 600;">WhatsApp</span> |
|||
<p class="m-0" style="color:#718096;">Say hi to us on WhatsApp!</p> |
|||
<div style="font-weight: 400; font-size: 16px;"><span><img src="assets/misc/phone.svg" |
|||
alt="" width="14px" |
|||
style="filter: invert(1); margin-right: 0.8rem;"></span>+91 |
|||
99456767686</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
<!-- Optional JavaScript --> |
|||
<!-- jQuery first, then Popper.js, then Bootstrap JS --> |
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,27 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!--Generate Agreement Button --> |
|||
<record id="purchase_order_form" model="ir.ui.view"> |
|||
<field name="name">purchase.order.inherit.purchase.recurring.orders</field> |
|||
<field name="model">purchase.order</field> |
|||
<field name="inherit_id" ref="purchase.purchase_order_form"/> |
|||
<field name="arch" type="xml"> |
|||
<button name="button_cancel" position="after"> |
|||
<button name="action_button_generate_agreement" |
|||
string="Generate agreement" type="object"/> |
|||
</button> |
|||
</field> |
|||
</record> |
|||
<record id="view_purchase_order_filter" model="ir.ui.view"> |
|||
<field name="name">purchase.order.inherit.purchase.recurring.orders</field> |
|||
<field name="model">purchase.order</field> |
|||
<field name="inherit_id" ref="purchase.view_purchase_order_filter"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="name" position="after"> |
|||
<filter string="Not from agreements" name="from_agreement" |
|||
domain="[('from_agreement','=',False)]"/> |
|||
<separator/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,183 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Purchase Agreement Renewal tree View--> |
|||
<record model="ir.ui.view" |
|||
id="purchase_agreement_renewal_view_tree"> |
|||
<field name="name">purchase.agreement.renewal.view.tree</field> |
|||
<field name="model">purchase.agreement.renewal</field> |
|||
<field name="type">tree</field> |
|||
<field name="priority" eval="6"/> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Agreement Renewals"> |
|||
<field name="date"/> |
|||
<field name="comments"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
<!-- Purchase Recurring Agreement tree View--> |
|||
<record model="ir.ui.view" |
|||
id="purchase_recurring_agreement_view_tree"> |
|||
<field name="name">purchase.recurring.agreement.view.tree</field> |
|||
<field name="model">purchase.recurring.agreement</field> |
|||
<field name="type">tree</field> |
|||
<field name="priority" eval="6"/> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Recurring Orders Agreements"> |
|||
<field name="number"/> |
|||
<field name="name"/> |
|||
<field name="partner_id"/> |
|||
<field name="start_date"/> |
|||
<field name="prolong"/> |
|||
<field name="next_expiration_date"/> |
|||
<field name="company_id" groups="base.group_multi_company"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
<!-- Purchase Recurring Agreement Form View--> |
|||
<record id="purchase_recurring_agreement_view_form" |
|||
model="ir.ui.view"> |
|||
<field name="name">purchase.recurring.agreement.view.form</field> |
|||
<field name="model">purchase.recurring.agreement</field> |
|||
<field name="priority">6</field> |
|||
<field name="arch" type="xml"> |
|||
<form> |
|||
<header> |
|||
<button name="generate_initial_order" class="oe_highlight" |
|||
invisible="state != 'empty'" |
|||
string="Generate Initial Order" |
|||
type="object"/> |
|||
<button name="generate_next_year_orders" |
|||
string="Generate Next Year Orders" type="object" |
|||
icon="fa-envelope"/> |
|||
<button name="%(agreement_renewal_action)d" |
|||
type="action" |
|||
string="Renew Agreement" |
|||
invisible="prolong != 'recurrent'"/> |
|||
</header> |
|||
<sheet> |
|||
<div name="button_box" position="inside"> |
|||
<button class="oe_stat_button" type="object" |
|||
name="get_orders" |
|||
icon="fa-file"> |
|||
<field string="Orders" name="order_count" |
|||
widget="statinfo"/> |
|||
</button> |
|||
</div> |
|||
<group> |
|||
<group> |
|||
<field name="name"/> |
|||
<field name="partner_id"/> |
|||
<field name="company_id" |
|||
groups="base.group_multi_company"/> |
|||
</group> |
|||
<group> |
|||
<field name="active" invisible="1"/> |
|||
<field name="number" invisible="1"/> |
|||
<field name="start_date" |
|||
on_change="onchange_start_date(start_date)" |
|||
readonly="state != 'empty'"/> |
|||
<field name="next_expiration_date"/> |
|||
<field name="prolong_unit" |
|||
required="prolong != 'fixed'"/> |
|||
</group> |
|||
<group> |
|||
<group> |
|||
<field name="prolong" |
|||
readonly="renewal_state !='not_renewed'"/> |
|||
|
|||
<field name="last_renovation_date" readonly="1" |
|||
invisible="prolong != 'recurrent'"/> |
|||
<field name="state" invisible="1"/> |
|||
<field name="renewal_state" invisible="1"/> |
|||
</group> |
|||
<group> |
|||
<group invisible="prolong == 'fixed'" |
|||
colspan="4" col="4"> |
|||
<field name="prolong_interval" |
|||
required="prolong != 'fixed'"/> |
|||
</group> |
|||
<group invisible = "prolong != 'fixed'" |
|||
colspan="4" col="2"> |
|||
<field name="end_date" |
|||
required="prolong == 'fixed'"/> |
|||
</group> |
|||
</group> |
|||
</group> |
|||
</group> |
|||
<notebook colspan="4"> |
|||
<page string="Lines"> |
|||
<field name="agreement_line_ids"> |
|||
<tree string="Agreement lines" |
|||
editable="bottom"> |
|||
<field name="active_chk"/> |
|||
<field name="product_id" on_change="1"/> |
|||
<field name="additional_description"/> |
|||
<field name="quantity"/> |
|||
<field name="uom_id"/> |
|||
<field name="list_price"/> |
|||
<field name="specific_price"/> |
|||
<field name="ordering_interval"/> |
|||
<field name="ordering_unit"/> |
|||
<field name="last_order_date" |
|||
readonly="True"/> |
|||
</tree> |
|||
</field> |
|||
</page> |
|||
<page string="Renewals" |
|||
invisible="prolong != 'recurrent'"> |
|||
<field colspan="4" mode="tree" name="renewal_ids" |
|||
widget="one2many_list" nolabel="1"/> |
|||
</page> |
|||
</notebook> |
|||
<div class="oe_clear"/> |
|||
<field name="notes"/> |
|||
</sheet> |
|||
<div class="oe_chatter"> |
|||
<field name="message_follower_ids" widget="mail_followers" |
|||
groups="base.group_user"/> |
|||
<field name="message_ids" widget="mail_thread" |
|||
placeholder="Share a message..."/> |
|||
</div> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
<!-- Purchase Recurring Agreement search View--> |
|||
<record id="purchases_recurring_agreement_view_search" model="ir.ui.view"> |
|||
<field name="name">purchase.recurring.agreement.view.search</field> |
|||
<field name="model">purchase.recurring.agreement</field> |
|||
<field name="arch" type="xml"> |
|||
<search> |
|||
<field name="number" |
|||
filter_domain="[('number', 'ilike', self)]"/> |
|||
<field name="name" string="Agreement Name" |
|||
filter_domain="[('name', 'ilike', self)]"/> |
|||
<field name="partner_id" |
|||
filter_domain="[('partner_id', 'child_of', self)]"/> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
<!-- Recurring Orders Agreement Menu Action--> |
|||
<record model="ir.actions.act_window" |
|||
id="recurring_orders_agreement_action"> |
|||
<field name="name">Recurring Order Agreement</field> |
|||
<field name="res_model">purchase.recurring.agreement</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="help" type="html"> |
|||
<p class="oe_view_nocontent_create"> |
|||
Click to set a new agreement. |
|||
</p> |
|||
<p> |
|||
Agreements are the way you define the commercial relation with |
|||
your customers which specify certain |
|||
products/services that you are providing them which requires a |
|||
recurring order. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
<!-- Menu Recurring Order Agreement--> |
|||
<menuitem name="Recurring Order Agreement" |
|||
id="menu_recurring_order_agreement" |
|||
parent="purchase.menu_procurement_management" |
|||
action="recurring_orders_agreement_action" |
|||
sequence="4"/> |
|||
</odoo> |
@ -0,0 +1,27 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Agreement action for partner view --> |
|||
<record id="purchase_recurring_agreement_action" model="ir.actions.act_window"> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="name">Purchase Recurring Agreement</field> |
|||
<field name="res_model">purchase.recurring.agreement</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="context">{'search_default_partner_id': active_id}</field> |
|||
</record> |
|||
<!-- Agreement Smart Button in partner view --> |
|||
<record id="view_partner_form" model="ir.ui.view"> |
|||
<field name="name">res.partner.inherit.purchase.recurring.orders</field> |
|||
<field name="model">res.partner</field> |
|||
<field name="inherit_id" ref="base.view_partner_form"/> |
|||
<field name="priority" eval="20"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//div[@name='button_box']" position="inside"> |
|||
<button name="%(purchase_recurring_agreement_action)d" |
|||
type="action" |
|||
class="oe_stat_button" |
|||
icon="fa-file-text-o" |
|||
string="Agreements"/> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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 renew_wizard |
@ -0,0 +1,56 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Ayana KP (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 |
|||
|
|||
|
|||
class RenewWizard(models.TransientModel): |
|||
_name = "agreement.renewal" |
|||
_inherit = 'mail.thread' |
|||
_description = "Agreement Renewal Wizard" |
|||
|
|||
def _get_renewal_date(self): |
|||
"""Method for getting the renewal date""" |
|||
agreements = self.env['purchase.recurring.agreement'].browse( |
|||
self.env.context.get('active_ids', [])) |
|||
return agreements[:1].next_expiration_date |
|||
|
|||
date = fields.Date( |
|||
string='Renewal Date', required=True, |
|||
help="Effective date of the renewal. This date is the one taken into " |
|||
"account in the next renewal", |
|||
default=_get_renewal_date) |
|||
comments = fields.Char( |
|||
string='Comments', size=200, help='Renewal comments') |
|||
|
|||
def create_renewal(self): |
|||
"""Method for renewal of purchase recurring agreement""" |
|||
self.ensure_one() |
|||
agreement_ids = self.env.context.get('active_ids', []) |
|||
for agreement_id in agreement_ids: |
|||
self.env['purchase.agreement.renewal'].create( |
|||
{'recurring_agreement_id': agreement_id, |
|||
'date': self.date, |
|||
'comments': self.comments}) |
|||
self.env['purchase.recurring.agreement'].browse(agreement_ids).write( |
|||
{'last_renovation_date': self.date, |
|||
'renewal_state': 'renewed'}) |
|||
return True |
@ -0,0 +1,46 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Purchase Agreement Renewal Wizard Form View --> |
|||
<record id="agreement_renewal_view_form" model="ir.ui.view" > |
|||
<field name="model">agreement.renewal</field> |
|||
<field name="arch" type="xml"> |
|||
<form> |
|||
<sheet> |
|||
<group> |
|||
<group> |
|||
<field name="date"/> |
|||
<separator string="" colspan="4"/> |
|||
<field name="comments" default_focus="1"/> |
|||
<separator string="" colspan="4"/> |
|||
</group> |
|||
</group> |
|||
<div class="oe_chatter"> |
|||
<field name="message_follower_ids" |
|||
widget="mail_followers" |
|||
groups="base.group_user"/> |
|||
<field name="message_ids" widget="mail_thread" |
|||
placeholder="Share a message..."/> |
|||
</div> |
|||
</sheet> |
|||
<footer> |
|||
<button special="cancel" class="oe_highlight" |
|||
string="Cancel" icon="fa-close"/> |
|||
<button name="create_renewal" class="oe_highlight" |
|||
string="Renew Agreement" type="object" |
|||
icon="fa-refresh"/> |
|||
</footer> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
<!-- Purchase Agreement Renewal Wizard Action --> |
|||
<record id="agreement_renewal_action" |
|||
model="ir.actions.act_window"> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="name">Renew Agreement</field> |
|||
<field name="res_model">agreement.renewal</field> |
|||
<field name="view_mode">kanban,list,form</field> |
|||
<field name="view_id" |
|||
ref="agreement_renewal_view_form"/> |
|||
<field name="target">new</field> |
|||
</record> |
|||
</odoo> |